1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.eclipse.jgit.transport;
47
48 import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
49 import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
50 import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
51 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
52 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
53 import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
54 import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
55 import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE;
56 import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
57 import static org.eclipse.jgit.util.HttpSupport.METHOD_POST;
58
59 import java.io.BufferedReader;
60 import java.io.ByteArrayInputStream;
61 import java.io.FileNotFoundException;
62 import java.io.IOException;
63 import java.io.InputStream;
64 import java.io.InputStreamReader;
65 import java.io.OutputStream;
66 import java.net.MalformedURLException;
67 import java.net.Proxy;
68 import java.net.ProxySelector;
69 import java.net.URL;
70 import java.text.MessageFormat;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.Collection;
74 import java.util.Collections;
75 import java.util.EnumSet;
76 import java.util.LinkedHashSet;
77 import java.util.Map;
78 import java.util.Set;
79 import java.util.TreeMap;
80 import java.util.zip.GZIPInputStream;
81 import java.util.zip.GZIPOutputStream;
82
83 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
84 import org.eclipse.jgit.errors.NotSupportedException;
85 import org.eclipse.jgit.errors.PackProtocolException;
86 import org.eclipse.jgit.errors.TransportException;
87 import org.eclipse.jgit.internal.JGitText;
88 import org.eclipse.jgit.internal.storage.file.RefDirectory;
89 import org.eclipse.jgit.lib.Config;
90 import org.eclipse.jgit.lib.Config.SectionParser;
91 import org.eclipse.jgit.lib.Constants;
92 import org.eclipse.jgit.lib.ObjectId;
93 import org.eclipse.jgit.lib.ObjectIdRef;
94 import org.eclipse.jgit.lib.ProgressMonitor;
95 import org.eclipse.jgit.lib.Ref;
96 import org.eclipse.jgit.lib.Repository;
97 import org.eclipse.jgit.lib.SymbolicRef;
98 import org.eclipse.jgit.transport.http.HttpConnection;
99 import org.eclipse.jgit.util.HttpSupport;
100 import org.eclipse.jgit.util.IO;
101 import org.eclipse.jgit.util.RawParseUtils;
102 import org.eclipse.jgit.util.TemporaryBuffer;
103 import org.eclipse.jgit.util.io.DisabledOutputStream;
104 import org.eclipse.jgit.util.io.UnionInputStream;
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public class TransportHttp extends HttpTransport implements WalkTransport,
123 PackTransport {
124
125 private static final String SVC_UPLOAD_PACK = "git-upload-pack";
126
127 private static final String SVC_RECEIVE_PACK = "git-receive-pack";
128
129 static final TransportProtocol PROTO_HTTP = new TransportProtocol() {
130 private final String[] schemeNames = { "http", "https" };
131
132 private final Set<String> schemeSet = Collections
133 .unmodifiableSet(new LinkedHashSet<String>(Arrays
134 .asList(schemeNames)));
135
136 public String getName() {
137 return JGitText.get().transportProtoHTTP;
138 }
139
140 public Set<String> getSchemes() {
141 return schemeSet;
142 }
143
144 public Set<URIishField> getRequiredFields() {
145 return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
146 URIishField.PATH));
147 }
148
149 public Set<URIishField> getOptionalFields() {
150 return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
151 URIishField.PASS, URIishField.PORT));
152 }
153
154 public int getDefaultPort() {
155 return 80;
156 }
157
158 public Transport open(URIish uri, Repository local, String remoteName)
159 throws NotSupportedException {
160 return new TransportHttp(local, uri);
161 }
162
163 public Transport open(URIish uri) throws NotSupportedException {
164 return new TransportHttp(uri);
165 }
166 };
167
168 static final TransportProtocol PROTO_FTP = new TransportProtocol() {
169 public String getName() {
170 return JGitText.get().transportProtoFTP;
171 }
172
173 public Set<String> getSchemes() {
174 return Collections.singleton("ftp");
175 }
176
177 public Set<URIishField> getRequiredFields() {
178 return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
179 URIishField.PATH));
180 }
181
182 public Set<URIishField> getOptionalFields() {
183 return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
184 URIishField.PASS, URIishField.PORT));
185 }
186
187 public int getDefaultPort() {
188 return 21;
189 }
190
191 public Transport open(URIish uri, Repository local, String remoteName)
192 throws NotSupportedException {
193 return new TransportHttp(local, uri);
194 }
195 };
196
197 private static final Config.SectionParser<HttpConfig> HTTP_KEY = new SectionParser<HttpConfig>() {
198 public HttpConfig parse(final Config cfg) {
199 return new HttpConfig(cfg);
200 }
201 };
202
203 private static class HttpConfig {
204 final int postBuffer;
205
206 final boolean sslVerify;
207
208 HttpConfig(final Config rc) {
209 postBuffer = rc.getInt("http", "postbuffer", 1 * 1024 * 1024);
210 sslVerify = rc.getBoolean("http", "sslVerify", true);
211 }
212
213 HttpConfig() {
214 this(new Config());
215 }
216 }
217
218 final URL baseUrl;
219
220 private final URL objectsUrl;
221
222 final HttpConfig http;
223
224 private final ProxySelector proxySelector;
225
226 private boolean useSmartHttp = true;
227
228 private HttpAuthMethod authMethod = HttpAuthMethod.Type.NONE.method(null);
229
230 private Map<String, String> headers;
231
232 TransportHttp(final Repository local, final URIish uri)
233 throws NotSupportedException {
234 super(local, uri);
235 try {
236 String uriString = uri.toString();
237 if (!uriString.endsWith("/"))
238 uriString += "/";
239 baseUrl = new URL(uriString);
240 objectsUrl = new URL(baseUrl, "objects/");
241 } catch (MalformedURLException e) {
242 throw new NotSupportedException(MessageFormat.format(JGitText.get().invalidURL, uri), e);
243 }
244 http = local.getConfig().get(HTTP_KEY);
245 proxySelector = ProxySelector.getDefault();
246 }
247
248
249
250
251
252
253
254 TransportHttp(final URIish uri) throws NotSupportedException {
255 super(uri);
256 try {
257 String uriString = uri.toString();
258 if (!uriString.endsWith("/"))
259 uriString += "/";
260 baseUrl = new URL(uriString);
261 objectsUrl = new URL(baseUrl, "objects/");
262 } catch (MalformedURLException e) {
263 throw new NotSupportedException(MessageFormat.format(JGitText.get().invalidURL, uri), e);
264 }
265 http = new HttpConfig();
266 proxySelector = ProxySelector.getDefault();
267 }
268
269
270
271
272
273
274
275
276
277
278
279 public void setUseSmartHttp(final boolean on) {
280 useSmartHttp = on;
281 }
282
283 @Override
284 public FetchConnection openFetch() throws TransportException,
285 NotSupportedException {
286 final String service = SVC_UPLOAD_PACK;
287 try {
288 final HttpConnection c = connect(service);
289 final InputStream in = openInputStream(c);
290 try {
291 BaseConnection f;
292 if (isSmartHttp(c, service)) {
293 readSmartHeaders(in, service);
294 f = new SmartHttpFetchConnection(in);
295 } else {
296
297
298 f = newDumbConnection(in);
299 }
300 f.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
301 return (FetchConnection) f;
302 } finally {
303 in.close();
304 }
305 } catch (NotSupportedException err) {
306 throw err;
307 } catch (TransportException err) {
308 throw err;
309 } catch (IOException err) {
310 throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
311 }
312 }
313
314 private WalkFetchConnection newDumbConnection(InputStream in)
315 throws IOException, PackProtocolException {
316 HttpObjectDB d = new HttpObjectDB(objectsUrl);
317 BufferedReader br = toBufferedReader(in);
318 Map<String, Ref> refs;
319 try {
320 refs = d.readAdvertisedImpl(br);
321 } finally {
322 br.close();
323 }
324
325 if (!refs.containsKey(Constants.HEAD)) {
326
327
328
329
330 HttpConnection conn = httpOpen(new URL(baseUrl, Constants.HEAD));
331 int status = HttpSupport.response(conn);
332 switch (status) {
333 case HttpConnection.HTTP_OK: {
334 br = toBufferedReader(openInputStream(conn));
335 try {
336 String line = br.readLine();
337 if (line != null && line.startsWith(RefDirectory.SYMREF)) {
338 String target = line.substring(RefDirectory.SYMREF.length());
339 Ref r = refs.get(target);
340 if (r == null)
341 r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null);
342 r = new SymbolicRef(Constants.HEAD, r);
343 refs.put(r.getName(), r);
344 } else if (line != null && ObjectId.isId(line)) {
345 Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK,
346 Constants.HEAD, ObjectId.fromString(line));
347 refs.put(r.getName(), r);
348 }
349 } finally {
350 br.close();
351 }
352 break;
353 }
354
355 case HttpConnection.HTTP_NOT_FOUND:
356 break;
357
358 default:
359 throw new TransportException(uri, MessageFormat.format(
360 JGitText.get().cannotReadHEAD, Integer.valueOf(status),
361 conn.getResponseMessage()));
362 }
363 }
364
365 WalkFetchConnection wfc = new WalkFetchConnection(this, d);
366 wfc.available(refs);
367 return wfc;
368 }
369
370 private BufferedReader toBufferedReader(InputStream in) {
371 return new BufferedReader(new InputStreamReader(in, Constants.CHARSET));
372 }
373
374 @Override
375 public PushConnection openPush() throws NotSupportedException,
376 TransportException {
377 final String service = SVC_RECEIVE_PACK;
378 try {
379 final HttpConnection c = connect(service);
380 final InputStream in = openInputStream(c);
381 try {
382 if (isSmartHttp(c, service)) {
383 return smartPush(service, c, in);
384 } else if (!useSmartHttp) {
385 final String msg = JGitText.get().smartHTTPPushDisabled;
386 throw new NotSupportedException(msg);
387
388 } else {
389 final String msg = JGitText.get().remoteDoesNotSupportSmartHTTPPush;
390 throw new NotSupportedException(msg);
391 }
392 } finally {
393 in.close();
394 }
395 } catch (NotSupportedException err) {
396 throw err;
397 } catch (TransportException err) {
398 throw err;
399 } catch (IOException err) {
400 throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
401 }
402 }
403
404 private PushConnection smartPush(String service, HttpConnection c,
405 InputStream in) throws IOException, TransportException {
406 readSmartHeaders(in, service);
407 SmartHttpPushConnection p = new SmartHttpPushConnection(in);
408 p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
409 return p;
410 }
411
412 @Override
413 public void close() {
414
415 }
416
417
418
419
420
421
422
423
424
425 public void setAdditionalHeaders(Map<String, String> headers) {
426 this.headers = headers;
427 }
428
429 private HttpConnection connect(final String service)
430 throws TransportException, NotSupportedException {
431 final URL u;
432 try {
433 final StringBuilder b = new StringBuilder();
434 b.append(baseUrl);
435
436 if (b.charAt(b.length() - 1) != '/')
437 b.append('/');
438 b.append(Constants.INFO_REFS);
439
440 if (useSmartHttp) {
441 b.append(b.indexOf("?") < 0 ? '?' : '&');
442 b.append("service=");
443 b.append(service);
444 }
445
446 u = new URL(b.toString());
447 } catch (MalformedURLException e) {
448 throw new NotSupportedException(MessageFormat.format(JGitText.get().invalidURL, uri), e);
449 }
450
451 try {
452 int authAttempts = 1;
453 for (;;) {
454 final HttpConnection conn = httpOpen(u);
455 if (useSmartHttp) {
456 String exp = "application/x-" + service + "-advertisement";
457 conn.setRequestProperty(HDR_ACCEPT, exp + ", */*");
458 } else {
459 conn.setRequestProperty(HDR_ACCEPT, "*/*");
460 }
461 final int status = HttpSupport.response(conn);
462 switch (status) {
463 case HttpConnection.HTTP_OK:
464
465
466
467
468 if (authMethod.getType() == HttpAuthMethod.Type.NONE
469 && conn.getHeaderField(HDR_WWW_AUTHENTICATE) != null)
470 authMethod = HttpAuthMethod.scanResponse(conn);
471 return conn;
472
473 case HttpConnection.HTTP_NOT_FOUND:
474 throw new NoRemoteRepositoryException(uri,
475 MessageFormat.format(JGitText.get().uriNotFound, u));
476
477 case HttpConnection.HTTP_UNAUTHORIZED:
478 authMethod = HttpAuthMethod.scanResponse(conn);
479 if (authMethod.getType() == HttpAuthMethod.Type.NONE)
480 throw new TransportException(uri, MessageFormat.format(
481 JGitText.get().authenticationNotSupported, uri));
482 CredentialsProvider credentialsProvider = getCredentialsProvider();
483 if (credentialsProvider == null)
484 throw new TransportException(uri,
485 JGitText.get().noCredentialsProvider);
486 if (authAttempts > 1)
487 credentialsProvider.reset(uri);
488 if (3 < authAttempts
489 || !authMethod.authorize(uri, credentialsProvider)) {
490 throw new TransportException(uri,
491 JGitText.get().notAuthorized);
492 }
493 authAttempts++;
494 continue;
495
496 case HttpConnection.HTTP_FORBIDDEN:
497 throw new TransportException(uri, MessageFormat.format(
498 JGitText.get().serviceNotPermitted, service));
499
500 default:
501 String err = status + " " + conn.getResponseMessage();
502 throw new TransportException(uri, err);
503 }
504 }
505 } catch (NotSupportedException e) {
506 throw e;
507 } catch (TransportException e) {
508 throw e;
509 } catch (IOException e) {
510 throw new TransportException(uri, MessageFormat.format(JGitText.get().cannotOpenService, service), e);
511 }
512 }
513
514 final HttpConnection httpOpen(URL u) throws IOException {
515 return httpOpen(METHOD_GET, u);
516 }
517
518
519
520
521
522
523
524
525
526
527 protected HttpConnection httpOpen(String method, URL u)
528 throws IOException {
529 final Proxy proxy = HttpSupport.proxyFor(proxySelector, u);
530 HttpConnection conn = connectionFactory.create(u, proxy);
531
532 if (!http.sslVerify && "https".equals(u.getProtocol())) {
533 HttpSupport.disableSslVerify(conn);
534 }
535
536 conn.setRequestMethod(method);
537 conn.setUseCaches(false);
538 conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
539 conn.setRequestProperty(HDR_PRAGMA, "no-cache");
540 if (UserAgent.get() != null) {
541 conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
542 }
543 int timeOut = getTimeout();
544 if (timeOut != -1) {
545 int effTimeOut = timeOut * 1000;
546 conn.setConnectTimeout(effTimeOut);
547 conn.setReadTimeout(effTimeOut);
548 }
549 if (this.headers != null && !this.headers.isEmpty()) {
550 for (Map.Entry<String, String> entry : this.headers.entrySet())
551 conn.setRequestProperty(entry.getKey(), entry.getValue());
552 }
553 authMethod.configureRequest(conn);
554 return conn;
555 }
556
557 final InputStream openInputStream(HttpConnection conn)
558 throws IOException {
559 InputStream input = conn.getInputStream();
560 if (ENCODING_GZIP.equals(conn.getHeaderField(HDR_CONTENT_ENCODING)))
561 input = new GZIPInputStream(input);
562 return input;
563 }
564
565 IOException wrongContentType(String expType, String actType) {
566 final String why = MessageFormat.format(JGitText.get().expectedReceivedContentType, expType, actType);
567 return new TransportException(uri, why);
568 }
569
570 private boolean isSmartHttp(final HttpConnection c, final String service) {
571 final String expType = "application/x-" + service + "-advertisement";
572 final String actType = c.getContentType();
573 return expType.equals(actType);
574 }
575
576 private void readSmartHeaders(final InputStream in, final String service)
577 throws IOException {
578
579
580
581
582
583 final byte[] magic = new byte[5];
584 IO.readFully(in, magic, 0, magic.length);
585 if (magic[4] != '#') {
586 throw new TransportException(uri, MessageFormat.format(
587 JGitText.get().expectedPktLineWithService, RawParseUtils.decode(magic)));
588 }
589
590 final PacketLineIn pckIn = new PacketLineIn(new UnionInputStream(
591 new ByteArrayInputStream(magic), in));
592 final String exp = "# service=" + service;
593 final String act = pckIn.readString();
594 if (!exp.equals(act)) {
595 throw new TransportException(uri, MessageFormat.format(
596 JGitText.get().expectedGot, exp, act));
597 }
598
599 while (pckIn.readString() != PacketLineIn.END) {
600
601 }
602 }
603
604 class HttpObjectDB extends WalkRemoteObjectDatabase {
605 private final URL httpObjectsUrl;
606
607 HttpObjectDB(final URL b) {
608 httpObjectsUrl = b;
609 }
610
611 @Override
612 URIish getURI() {
613 return new URIish(httpObjectsUrl);
614 }
615
616 @Override
617 Collection<WalkRemoteObjectDatabase> getAlternates() throws IOException {
618 try {
619 return readAlternates(INFO_HTTP_ALTERNATES);
620 } catch (FileNotFoundException err) {
621
622 }
623
624 try {
625 return readAlternates(INFO_ALTERNATES);
626 } catch (FileNotFoundException err) {
627
628 }
629
630 return null;
631 }
632
633 @Override
634 WalkRemoteObjectDatabase openAlternate(final String location)
635 throws IOException {
636 return new HttpObjectDB(new URL(httpObjectsUrl, location));
637 }
638
639 @Override
640 Collection<String> getPackNames() throws IOException {
641 final Collection<String> packs = new ArrayList<String>();
642 try {
643 final BufferedReader br = openReader(INFO_PACKS);
644 try {
645 for (;;) {
646 final String s = br.readLine();
647 if (s == null || s.length() == 0)
648 break;
649 if (!s.startsWith("P pack-") || !s.endsWith(".pack"))
650 throw invalidAdvertisement(s);
651 packs.add(s.substring(2));
652 }
653 return packs;
654 } finally {
655 br.close();
656 }
657 } catch (FileNotFoundException err) {
658 return packs;
659 }
660 }
661
662 @Override
663 FileStream open(final String path) throws IOException {
664 final URL base = httpObjectsUrl;
665 final URL u = new URL(base, path);
666 final HttpConnection c = httpOpen(u);
667 switch (HttpSupport.response(c)) {
668 case HttpConnection.HTTP_OK:
669 final InputStream in = openInputStream(c);
670 final int len = c.getContentLength();
671 return new FileStream(in, len);
672 case HttpConnection.HTTP_NOT_FOUND:
673 throw new FileNotFoundException(u.toString());
674 default:
675 throw new IOException(u.toString() + ": "
676 + HttpSupport.response(c) + " "
677 + c.getResponseMessage());
678 }
679 }
680
681 Map<String, Ref> readAdvertisedImpl(final BufferedReader br)
682 throws IOException, PackProtocolException {
683 final TreeMap<String, Ref> avail = new TreeMap<String, Ref>();
684 for (;;) {
685 String line = br.readLine();
686 if (line == null)
687 break;
688
689 final int tab = line.indexOf('\t');
690 if (tab < 0)
691 throw invalidAdvertisement(line);
692
693 String name;
694 final ObjectId id;
695
696 name = line.substring(tab + 1);
697 id = ObjectId.fromString(line.substring(0, tab));
698 if (name.endsWith("^{}")) {
699 name = name.substring(0, name.length() - 3);
700 final Ref prior = avail.get(name);
701 if (prior == null)
702 throw outOfOrderAdvertisement(name);
703
704 if (prior.getPeeledObjectId() != null)
705 throw duplicateAdvertisement(name + "^{}");
706
707 avail.put(name, new ObjectIdRef.PeeledTag(
708 Ref.Storage.NETWORK, name,
709 prior.getObjectId(), id));
710 } else {
711 Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
712 Ref.Storage.NETWORK, name, id));
713 if (prior != null)
714 throw duplicateAdvertisement(name);
715 }
716 }
717 return avail;
718 }
719
720 private PackProtocolException outOfOrderAdvertisement(final String n) {
721 return new PackProtocolException(MessageFormat.format(JGitText.get().advertisementOfCameBefore, n, n));
722 }
723
724 private PackProtocolException invalidAdvertisement(final String n) {
725 return new PackProtocolException(MessageFormat.format(JGitText.get().invalidAdvertisementOf, n));
726 }
727
728 private PackProtocolException duplicateAdvertisement(final String n) {
729 return new PackProtocolException(MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, n));
730 }
731
732 @Override
733 void close() {
734
735 }
736 }
737
738 class SmartHttpFetchConnection extends BasePackFetchConnection {
739 private MultiRequestService svc;
740
741 SmartHttpFetchConnection(final InputStream advertisement)
742 throws TransportException {
743 super(TransportHttp.this);
744 statelessRPC = true;
745
746 init(advertisement, DisabledOutputStream.INSTANCE);
747 outNeedsEnd = false;
748 readAdvertisedRefs();
749 }
750
751 @Override
752 protected void doFetch(final ProgressMonitor monitor,
753 final Collection<Ref> want, final Set<ObjectId> have,
754 final OutputStream outputStream) throws TransportException {
755 try {
756 svc = new MultiRequestService(SVC_UPLOAD_PACK);
757 init(svc.getInputStream(), svc.getOutputStream());
758 super.doFetch(monitor, want, have, outputStream);
759 } finally {
760 svc = null;
761 }
762 }
763
764 @Override
765 protected void onReceivePack() {
766 svc.finalRequest = true;
767 }
768 }
769
770 class SmartHttpPushConnection extends BasePackPushConnection {
771 SmartHttpPushConnection(final InputStream advertisement)
772 throws TransportException {
773 super(TransportHttp.this);
774 statelessRPC = true;
775
776 init(advertisement, DisabledOutputStream.INSTANCE);
777 outNeedsEnd = false;
778 readAdvertisedRefs();
779 }
780
781 protected void doPush(final ProgressMonitor monitor,
782 final Map<String, RemoteRefUpdate> refUpdates,
783 OutputStream outputStream) throws TransportException {
784 final Service svc = new MultiRequestService(SVC_RECEIVE_PACK);
785 init(svc.getInputStream(), svc.getOutputStream());
786 super.doPush(monitor, refUpdates, outputStream);
787 }
788 }
789
790
791 abstract class Service {
792 protected final String serviceName;
793
794 protected final String requestType;
795
796 protected final String responseType;
797
798 protected HttpConnection conn;
799
800 protected HttpOutputStream out;
801
802 protected final HttpExecuteStream execute;
803
804 final UnionInputStream in;
805
806 Service(String serviceName) {
807 this.serviceName = serviceName;
808 this.requestType = "application/x-" + serviceName + "-request";
809 this.responseType = "application/x-" + serviceName + "-result";
810
811 this.out = new HttpOutputStream();
812 this.execute = new HttpExecuteStream();
813 this.in = new UnionInputStream(execute);
814 }
815
816 void openStream() throws IOException {
817 conn = httpOpen(METHOD_POST, new URL(baseUrl, serviceName));
818 conn.setInstanceFollowRedirects(false);
819 conn.setDoOutput(true);
820 conn.setRequestProperty(HDR_CONTENT_TYPE, requestType);
821 conn.setRequestProperty(HDR_ACCEPT, responseType);
822 }
823
824 void sendRequest() throws IOException {
825
826 TemporaryBuffer buf = new TemporaryBuffer.Heap(http.postBuffer);
827 try {
828 GZIPOutputStream gzip = new GZIPOutputStream(buf);
829 out.writeTo(gzip, null);
830 gzip.close();
831 if (out.length() < buf.length())
832 buf = out;
833 } catch (IOException err) {
834
835
836 buf = out;
837 }
838
839 openStream();
840 if (buf != out)
841 conn.setRequestProperty(HDR_CONTENT_ENCODING, ENCODING_GZIP);
842 conn.setFixedLengthStreamingMode((int) buf.length());
843 final OutputStream httpOut = conn.getOutputStream();
844 try {
845 buf.writeTo(httpOut, null);
846 } finally {
847 httpOut.close();
848 }
849 }
850
851 void openResponse() throws IOException {
852 final int status = HttpSupport.response(conn);
853 if (status != HttpConnection.HTTP_OK) {
854 throw new TransportException(uri, status + " "
855 + conn.getResponseMessage());
856 }
857
858 final String contentType = conn.getContentType();
859 if (!responseType.equals(contentType)) {
860 conn.getInputStream().close();
861 throw wrongContentType(responseType, contentType);
862 }
863 }
864
865 HttpOutputStream getOutputStream() {
866 return out;
867 }
868
869 InputStream getInputStream() {
870 return in;
871 }
872
873 abstract void execute() throws IOException;
874
875 class HttpExecuteStream extends InputStream {
876 public int read() throws IOException {
877 execute();
878 return -1;
879 }
880
881 public int read(byte[] b, int off, int len) throws IOException {
882 execute();
883 return -1;
884 }
885
886 public long skip(long n) throws IOException {
887 execute();
888 return 0;
889 }
890 }
891
892 class HttpOutputStream extends TemporaryBuffer {
893 HttpOutputStream() {
894 super(http.postBuffer);
895 }
896
897 @Override
898 protected OutputStream overflow() throws IOException {
899 openStream();
900 conn.setChunkedStreamingMode(0);
901 return conn.getOutputStream();
902 }
903 }
904 }
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926 class MultiRequestService extends Service {
927 boolean finalRequest;
928
929 MultiRequestService(final String serviceName) {
930 super(serviceName);
931 }
932
933
934 @Override
935 void execute() throws IOException {
936 out.close();
937
938 if (conn == null) {
939 if (out.length() == 0) {
940
941
942
943
944
945 if (finalRequest)
946 return;
947 throw new TransportException(uri,
948 JGitText.get().startingReadStageWithoutWrittenRequestDataPendingIsNotSupported);
949 }
950
951 sendRequest();
952 }
953
954 out.reset();
955
956 openResponse();
957
958 in.add(openInputStream(conn));
959 if (!finalRequest)
960 in.add(execute);
961 conn = null;
962 }
963 }
964
965
966 class LongPollService extends Service {
967
968
969
970 LongPollService(String serviceName) {
971 super(serviceName);
972 }
973
974
975 @Override
976 void execute() throws IOException {
977 out.close();
978 if (conn == null)
979 sendRequest();
980 openResponse();
981 in.add(openInputStream(conn));
982 }
983 }
984 }