1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jgit.transport;
15
16 import static java.nio.charset.StandardCharsets.UTF_8;
17 import static org.eclipse.jgit.lib.Constants.HEAD;
18 import static org.eclipse.jgit.lib.Constants.INFO_ALTERNATES;
19 import static org.eclipse.jgit.lib.Constants.INFO_HTTP_ALTERNATES;
20 import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
21 import static org.eclipse.jgit.util.HttpSupport.ENCODING_X_GZIP;
22 import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT;
23 import static org.eclipse.jgit.util.HttpSupport.HDR_ACCEPT_ENCODING;
24 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
25 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
26 import static org.eclipse.jgit.util.HttpSupport.HDR_COOKIE;
27 import static org.eclipse.jgit.util.HttpSupport.HDR_LOCATION;
28 import static org.eclipse.jgit.util.HttpSupport.HDR_PRAGMA;
29 import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE;
30 import static org.eclipse.jgit.util.HttpSupport.HDR_SET_COOKIE2;
31 import static org.eclipse.jgit.util.HttpSupport.HDR_USER_AGENT;
32 import static org.eclipse.jgit.util.HttpSupport.HDR_WWW_AUTHENTICATE;
33 import static org.eclipse.jgit.util.HttpSupport.METHOD_GET;
34 import static org.eclipse.jgit.util.HttpSupport.METHOD_POST;
35
36 import java.io.BufferedReader;
37 import java.io.ByteArrayInputStream;
38 import java.io.FileNotFoundException;
39 import java.io.IOException;
40 import java.io.InputStream;
41 import java.io.InputStreamReader;
42 import java.io.InterruptedIOException;
43 import java.io.OutputStream;
44 import java.net.HttpCookie;
45 import java.net.MalformedURLException;
46 import java.net.Proxy;
47 import java.net.ProxySelector;
48 import java.net.SocketException;
49 import java.net.URI;
50 import java.net.URISyntaxException;
51 import java.net.URL;
52 import java.nio.file.InvalidPathException;
53 import java.nio.file.Path;
54 import java.nio.file.Paths;
55 import java.security.cert.CertPathBuilderException;
56 import java.security.cert.CertPathValidatorException;
57 import java.security.cert.CertificateException;
58 import java.text.MessageFormat;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.EnumSet;
64 import java.util.HashSet;
65 import java.util.LinkedHashSet;
66 import java.util.LinkedList;
67 import java.util.List;
68 import java.util.Locale;
69 import java.util.Map;
70 import java.util.Set;
71 import java.util.TreeMap;
72 import java.util.zip.GZIPInputStream;
73 import java.util.zip.GZIPOutputStream;
74
75 import javax.net.ssl.SSLHandshakeException;
76
77 import org.eclipse.jgit.errors.ConfigInvalidException;
78 import org.eclipse.jgit.errors.NoRemoteRepositoryException;
79 import org.eclipse.jgit.errors.NotSupportedException;
80 import org.eclipse.jgit.errors.PackProtocolException;
81 import org.eclipse.jgit.errors.TransportException;
82 import org.eclipse.jgit.internal.JGitText;
83 import org.eclipse.jgit.internal.storage.file.RefDirectory;
84 import org.eclipse.jgit.internal.transport.http.NetscapeCookieFile;
85 import org.eclipse.jgit.internal.transport.http.NetscapeCookieFileCache;
86 import org.eclipse.jgit.lib.Constants;
87 import org.eclipse.jgit.lib.ObjectId;
88 import org.eclipse.jgit.lib.ObjectIdRef;
89 import org.eclipse.jgit.lib.ProgressMonitor;
90 import org.eclipse.jgit.lib.Ref;
91 import org.eclipse.jgit.lib.Repository;
92 import org.eclipse.jgit.lib.StoredConfig;
93 import org.eclipse.jgit.lib.SymbolicRef;
94 import org.eclipse.jgit.transport.HttpAuthMethod.Type;
95 import org.eclipse.jgit.transport.HttpConfig.HttpRedirectMode;
96 import org.eclipse.jgit.transport.http.HttpConnection;
97 import org.eclipse.jgit.util.HttpSupport;
98 import org.eclipse.jgit.util.IO;
99 import org.eclipse.jgit.util.RawParseUtils;
100 import org.eclipse.jgit.util.StringUtils;
101 import org.eclipse.jgit.util.SystemReader;
102 import org.eclipse.jgit.util.TemporaryBuffer;
103 import org.eclipse.jgit.util.io.DisabledOutputStream;
104 import org.eclipse.jgit.util.io.UnionInputStream;
105 import org.slf4j.Logger;
106 import org.slf4j.LoggerFactory;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124 public class TransportHttp extends HttpTransport implements WalkTransport,
125 PackTransport {
126
127 private static final Logger LOG = LoggerFactory
128 .getLogger(TransportHttp.class);
129
130 private static final String SVC_UPLOAD_PACK = "git-upload-pack";
131
132 private static final String SVC_RECEIVE_PACK = "git-receive-pack";
133
134
135
136
137
138
139
140 public enum AcceptEncoding {
141
142
143
144
145 UNSPECIFIED,
146
147
148
149
150 GZIP
151 }
152
153 static final TransportProtocoll.html#TransportProtocol">TransportProtocol PROTO_HTTP = new TransportProtocol() {
154 private final String[] schemeNames = { "http", "https" };
155
156 private final Set<String> schemeSet = Collections
157 .unmodifiableSet(new LinkedHashSet<>(Arrays
158 .asList(schemeNames)));
159
160 @Override
161 public String getName() {
162 return JGitText.get().transportProtoHTTP;
163 }
164
165 @Override
166 public Set<String> getSchemes() {
167 return schemeSet;
168 }
169
170 @Override
171 public Set<URIishField> getRequiredFields() {
172 return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
173 URIishField.PATH));
174 }
175
176 @Override
177 public Set<URIishField> getOptionalFields() {
178 return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
179 URIishField.PASS, URIishField.PORT));
180 }
181
182 @Override
183 public int getDefaultPort() {
184 return 80;
185 }
186
187 @Override
188 public Transport open(URIish uri, Repository local, String remoteName)
189 throws NotSupportedException {
190 return new TransportHttp(local, uri);
191 }
192
193 @Override
194 public Transport open(URIish uri) throws NotSupportedException {
195 return new TransportHttp(uri);
196 }
197 };
198
199 static final TransportProtocolol.html#TransportProtocol">TransportProtocol PROTO_FTP = new TransportProtocol() {
200 @Override
201 public String getName() {
202 return JGitText.get().transportProtoFTP;
203 }
204
205 @Override
206 public Set<String> getSchemes() {
207 return Collections.singleton("ftp");
208 }
209
210 @Override
211 public Set<URIishField> getRequiredFields() {
212 return Collections.unmodifiableSet(EnumSet.of(URIishField.HOST,
213 URIishField.PATH));
214 }
215
216 @Override
217 public Set<URIishField> getOptionalFields() {
218 return Collections.unmodifiableSet(EnumSet.of(URIishField.USER,
219 URIishField.PASS, URIishField.PORT));
220 }
221
222 @Override
223 public int getDefaultPort() {
224 return 21;
225 }
226
227 @Override
228 public Transport open(URIish uri, Repository local, String remoteName)
229 throws NotSupportedException {
230 return new TransportHttp(local, uri);
231 }
232 };
233
234
235
236
237
238
239 private URIish currentUri;
240
241 private URL baseUrl;
242
243 private URL objectsUrl;
244
245 private final HttpConfig http;
246
247 private final ProxySelector proxySelector;
248
249 private boolean useSmartHttp = true;
250
251 private HttpAuthMethod authMethod = HttpAuthMethod.Type.NONE.method(null);
252
253 private Map<String, String> headers;
254
255 private boolean sslVerify;
256
257 private boolean sslFailure = false;
258
259
260
261
262 private final NetscapeCookieFile cookieFile;
263
264
265
266
267
268
269
270 private final Set<HttpCookie> relevantCookies;
271
272 TransportHttp(Repository local, URIish uri)
273 throws NotSupportedException {
274 super(local, uri);
275 setURI(uri);
276 http = new HttpConfig(local.getConfig(), uri);
277 proxySelector = ProxySelector.getDefault();
278 sslVerify = http.isSslVerify();
279 cookieFile = getCookieFileFromConfig(http);
280 relevantCookies = filterCookies(cookieFile, baseUrl);
281 }
282
283 private URL toURL(URIish urish) throws MalformedURLException {
284 String uriString = urish.toString();
285 if (!uriString.endsWith("/")) {
286 uriString += '/';
287 }
288 return new URL(uriString);
289 }
290
291
292
293
294
295
296
297
298
299 protected void setURI(URIish uri) throws NotSupportedException {
300 try {
301 currentUri = uri;
302 baseUrl = toURL(uri);
303 objectsUrl = new URL(baseUrl, "objects/");
304 } catch (MalformedURLException e) {
305 throw new NotSupportedException(MessageFormat.format(JGitText.get().invalidURL, uri), e);
306 }
307 }
308
309
310
311
312
313
314
315 TransportHttp(URIish uri) throws NotSupportedException {
316 super(uri);
317 setURI(uri);
318 http = new HttpConfig(uri);
319 proxySelector = ProxySelector.getDefault();
320 sslVerify = http.isSslVerify();
321 cookieFile = getCookieFileFromConfig(http);
322 relevantCookies = filterCookies(cookieFile, baseUrl);
323 }
324
325
326
327
328
329
330
331
332
333
334
335 public void setUseSmartHttp(boolean on) {
336 useSmartHttp = on;
337 }
338
339 @SuppressWarnings("resource")
340 private FetchConnection getConnection(HttpConnection c, InputStream in,
341 String service) throws IOException {
342 BaseConnection f;
343 if (isSmartHttp(c, service)) {
344 readSmartHeaders(in, service);
345 f = new SmartHttpFetchConnection(in);
346 } else {
347
348
349 f = newDumbConnection(in);
350 }
351 f.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
352 return (FetchConnection) f;
353 }
354
355
356 @Override
357 public FetchConnection openFetch() throws TransportException,
358 NotSupportedException {
359 final String service = SVC_UPLOAD_PACK;
360 try {
361 final HttpConnection c = connect(service);
362 try (InputStream in = openInputStream(c)) {
363 return getConnection(c, in, service);
364 }
365 } catch (NotSupportedException | TransportException err) {
366 throw err;
367 } catch (IOException err) {
368 throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
369 }
370 }
371
372 private WalkFetchConnection newDumbConnection(InputStream in)
373 throws IOException, PackProtocolException {
374 HttpObjectDB d = new HttpObjectDB(objectsUrl);
375 Map<String, Ref> refs;
376 try (BufferedReader br = toBufferedReader(in)) {
377 refs = d.readAdvertisedImpl(br);
378 }
379
380 if (!refs.containsKey(HEAD)) {
381
382
383
384
385 HttpConnection conn = httpOpen(
386 METHOD_GET,
387 new URL(baseUrl, HEAD),
388 AcceptEncoding.GZIP);
389 int status = HttpSupport.response(conn);
390 switch (status) {
391 case HttpConnection.HTTP_OK: {
392 try (BufferedReader br = toBufferedReader(
393 openInputStream(conn))) {
394 String line = br.readLine();
395 if (line != null && line.startsWith(RefDirectory.SYMREF)) {
396 String target = line.substring(RefDirectory.SYMREF.length());
397 Ref r = refs.get(target);
398 if (r == null)
399 r = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, target, null);
400 r = new SymbolicRef(HEAD, r);
401 refs.put(r.getName(), r);
402 } else if (line != null && ObjectId.isId(line)) {
403 Ref r = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK,
404 HEAD, ObjectId.fromString(line));
405 refs.put(r.getName(), r);
406 }
407 }
408 break;
409 }
410
411 case HttpConnection.HTTP_NOT_FOUND:
412 break;
413
414 default:
415 throw new TransportException(uri, MessageFormat.format(
416 JGitText.get().cannotReadHEAD, Integer.valueOf(status),
417 conn.getResponseMessage()));
418 }
419 }
420
421 WalkFetchConnection wfc = new WalkFetchConnection(this, d);
422 wfc.available(refs);
423 return wfc;
424 }
425
426 private BufferedReader toBufferedReader(InputStream in) {
427 return new BufferedReader(new InputStreamReader(in, UTF_8));
428 }
429
430
431 @Override
432 public PushConnection openPush() throws NotSupportedException,
433 TransportException {
434 final String service = SVC_RECEIVE_PACK;
435 try {
436 final HttpConnection c = connect(service);
437 try (InputStream in = openInputStream(c)) {
438 if (isSmartHttp(c, service)) {
439 return smartPush(service, c, in);
440 } else if (!useSmartHttp) {
441 final String msg = JGitText.get().smartHTTPPushDisabled;
442 throw new NotSupportedException(msg);
443
444 } else {
445 final String msg = JGitText.get().remoteDoesNotSupportSmartHTTPPush;
446 throw new NotSupportedException(msg);
447 }
448 }
449 } catch (NotSupportedException | TransportException err) {
450 throw err;
451 } catch (IOException err) {
452 throw new TransportException(uri, JGitText.get().errorReadingInfoRefs, err);
453 }
454 }
455
456 private PushConnection smartPush(String service, HttpConnection c,
457 InputStream in) throws IOException, TransportException {
458 readSmartHeaders(in, service);
459 SmartHttpPushConnection p = new SmartHttpPushConnection(in);
460 p.setPeerUserAgent(c.getHeaderField(HttpSupport.HDR_SERVER));
461 return p;
462 }
463
464
465 @Override
466 public void close() {
467
468 }
469
470
471
472
473
474
475
476
477
478 public void setAdditionalHeaders(Map<String, String> headers) {
479 this.headers = headers;
480 }
481
482 private NoRemoteRepositoryException createNotFoundException(URIish u,
483 URL url, String msg) {
484 String text;
485 if (msg != null && !msg.isEmpty()) {
486 text = MessageFormat.format(JGitText.get().uriNotFoundWithMessage,
487 url, msg);
488 } else {
489 text = MessageFormat.format(JGitText.get().uriNotFound, url);
490 }
491 return new NoRemoteRepositoryException(u, text);
492 }
493
494 private HttpConnection connect(String service)
495 throws TransportException, NotSupportedException {
496 URL u = getServiceURL(service);
497 int authAttempts = 1;
498 int redirects = 0;
499 Collection<Type> ignoreTypes = null;
500 for (;;) {
501 try {
502 final HttpConnection conn = httpOpen(METHOD_GET, u, AcceptEncoding.GZIP);
503 if (useSmartHttp) {
504 String exp = "application/x-" + service + "-advertisement";
505 conn.setRequestProperty(HDR_ACCEPT, exp + ", */*");
506 } else {
507 conn.setRequestProperty(HDR_ACCEPT, "*/*");
508 }
509 final int status = HttpSupport.response(conn);
510 processResponseCookies(conn);
511 switch (status) {
512 case HttpConnection.HTTP_OK:
513
514
515
516
517 if (authMethod.getType() == HttpAuthMethod.Type.NONE
518 && conn.getHeaderField(HDR_WWW_AUTHENTICATE) != null)
519 authMethod = HttpAuthMethod.scanResponse(conn, ignoreTypes);
520 return conn;
521
522 case HttpConnection.HTTP_NOT_FOUND:
523 throw createNotFoundException(uri, u,
524 conn.getResponseMessage());
525
526 case HttpConnection.HTTP_UNAUTHORIZED:
527 authMethod = HttpAuthMethod.scanResponse(conn, ignoreTypes);
528 if (authMethod.getType() == HttpAuthMethod.Type.NONE)
529 throw new TransportException(uri, MessageFormat.format(
530 JGitText.get().authenticationNotSupported, uri));
531 CredentialsProvider credentialsProvider = getCredentialsProvider();
532 if (credentialsProvider == null)
533 throw new TransportException(uri,
534 JGitText.get().noCredentialsProvider);
535 if (authAttempts > 1)
536 credentialsProvider.reset(currentUri);
537 if (3 < authAttempts
538 || !authMethod.authorize(currentUri,
539 credentialsProvider)) {
540 throw new TransportException(uri,
541 JGitText.get().notAuthorized);
542 }
543 authAttempts++;
544 continue;
545
546 case HttpConnection.HTTP_FORBIDDEN:
547 throw new TransportException(uri, MessageFormat.format(
548 JGitText.get().serviceNotPermitted, baseUrl,
549 service));
550
551 case HttpConnection.HTTP_MOVED_PERM:
552 case HttpConnection.HTTP_MOVED_TEMP:
553 case HttpConnection.HTTP_SEE_OTHER:
554 case HttpConnection.HTTP_11_MOVED_PERM:
555 case HttpConnection.HTTP_11_MOVED_TEMP:
556
557
558
559 if (http.getFollowRedirects() == HttpRedirectMode.FALSE) {
560 throw new TransportException(uri,
561 MessageFormat.format(
562 JGitText.get().redirectsOff,
563 Integer.valueOf(status)));
564 }
565 URIish newUri = redirect(u,
566 conn.getHeaderField(HDR_LOCATION),
567 Constants.INFO_REFS, redirects++);
568 setURI(newUri);
569 u = getServiceURL(service);
570 authAttempts = 1;
571 break;
572 default:
573 String err = status + " " + conn.getResponseMessage();
574 throw new TransportException(uri, err);
575 }
576 } catch (NotSupportedException | TransportException e) {
577 throw e;
578 } catch (InterruptedIOException e) {
579
580 throw new TransportException(uri, MessageFormat.format(
581 JGitText.get().connectionTimeOut, u.getHost()), e);
582 } catch (SocketException e) {
583
584 throw new TransportException(uri,
585 JGitText.get().connectionFailed, e);
586 } catch (SSLHandshakeException e) {
587 handleSslFailure(e);
588 continue;
589 } catch (IOException e) {
590 if (authMethod.getType() != HttpAuthMethod.Type.NONE) {
591 if (ignoreTypes == null) {
592 ignoreTypes = new HashSet<>();
593 }
594
595 ignoreTypes.add(authMethod.getType());
596
597
598 authMethod = HttpAuthMethod.Type.NONE.method(null);
599 authAttempts = 1;
600
601 continue;
602 }
603
604 throw new TransportException(uri, MessageFormat.format(JGitText.get().cannotOpenService, service), e);
605 }
606 }
607 }
608
609 void processResponseCookies(HttpConnection conn) {
610 if (cookieFile != null && http.getSaveCookies()) {
611 List<HttpCookie> foundCookies = new LinkedList<>();
612
613 List<String> cookieHeaderValues = conn
614 .getHeaderFields(HDR_SET_COOKIE);
615 if (!cookieHeaderValues.isEmpty()) {
616 foundCookies.addAll(
617 extractCookies(HDR_SET_COOKIE, cookieHeaderValues));
618 }
619 cookieHeaderValues = conn.getHeaderFields(HDR_SET_COOKIE2);
620 if (!cookieHeaderValues.isEmpty()) {
621 foundCookies.addAll(
622 extractCookies(HDR_SET_COOKIE2, cookieHeaderValues));
623 }
624 if (!foundCookies.isEmpty()) {
625 try {
626
627 Set<HttpCookie> cookies = cookieFile.getCookies(false);
628 cookies.addAll(foundCookies);
629 cookieFile.write(baseUrl);
630 relevantCookies.addAll(foundCookies);
631 } catch (IOException | IllegalArgumentException
632 | InterruptedException e) {
633 LOG.warn(MessageFormat.format(
634 JGitText.get().couldNotPersistCookies,
635 cookieFile.getPath()), e);
636 }
637 }
638 }
639 }
640
641 private List<HttpCookie> extractCookies(String headerKey,
642 List<String> headerValues) {
643 List<HttpCookie> foundCookies = new LinkedList<>();
644 for (String headerValue : headerValues) {
645 foundCookies
646 .addAll(HttpCookie.parse(headerKey + ':' + headerValue));
647 }
648
649
650
651 for (HttpCookie foundCookie : foundCookies) {
652 String domain = foundCookie.getDomain();
653 if (domain != null && domain.startsWith(".")) {
654 foundCookie.setDomain(domain.substring(1));
655 }
656 }
657 return foundCookies;
658 }
659
660 private static class CredentialItems {
661 CredentialItem.InformationalMessage message;
662
663
664 CredentialItem.YesNoType now;
665
666
667
668
669
670
671 CredentialItem.YesNoType forRepo;
672
673
674 CredentialItem.YesNoType always;
675
676 public CredentialItem[] items() {
677 if (forRepo == null) {
678 return new CredentialItem[] { message, now, always };
679 }
680 return new CredentialItem[] { message, now, forRepo, always };
681 }
682 }
683
684 private void handleSslFailure(Throwable e) throws TransportException {
685 if (sslFailure || !trustInsecureSslConnection(e.getCause())) {
686 throw new TransportException(uri,
687 MessageFormat.format(
688 JGitText.get().sslFailureExceptionMessage,
689 currentUri.setPass(null)),
690 e);
691 }
692 sslFailure = true;
693 }
694
695 private boolean trustInsecureSslConnection(Throwable cause) {
696 if (cause instanceof CertificateException
697 || cause instanceof CertPathBuilderException
698 || cause instanceof CertPathValidatorException) {
699
700
701 CredentialsProvider provider = getCredentialsProvider();
702 if (provider != null) {
703 CredentialItems trust = constructSslTrustItems(cause);
704 CredentialItem[] items = trust.items();
705 if (provider.supports(items)) {
706 boolean answered = provider.get(uri, items);
707 if (answered) {
708
709 boolean trustNow = trust.now.getValue();
710 boolean trustLocal = trust.forRepo != null
711 && trust.forRepo.getValue();
712 boolean trustAlways = trust.always.getValue();
713 if (trustNow || trustLocal || trustAlways) {
714 sslVerify = false;
715 if (trustAlways) {
716 updateSslVerifyUser(false);
717 } else if (trustLocal) {
718 updateSslVerify(local.getConfig(), false);
719 }
720 return true;
721 }
722 }
723 }
724 }
725 }
726 return false;
727 }
728
729 private CredentialItems constructSslTrustItems(Throwable cause) {
730 CredentialItems items = new CredentialItems();
731 String info = MessageFormat.format(JGitText.get().sslFailureInfo,
732 currentUri.setPass(null));
733 String sslMessage = cause.getLocalizedMessage();
734 if (sslMessage == null) {
735 sslMessage = cause.toString();
736 }
737 sslMessage = MessageFormat.format(JGitText.get().sslFailureCause,
738 sslMessage);
739 items.message = new CredentialItem.InformationalMessage(info + '\n'
740 + sslMessage + '\n'
741 + JGitText.get().sslFailureTrustExplanation);
742 items.now = new CredentialItem.YesNoType(JGitText.get().sslTrustNow);
743 if (local != null) {
744 items.forRepo = new CredentialItem.YesNoType(
745 MessageFormat.format(JGitText.get().sslTrustForRepo,
746 local.getDirectory()));
747 }
748 items.always = new CredentialItem.YesNoType(
749 JGitText.get().sslTrustAlways);
750 return items;
751 }
752
753 private void updateSslVerify(StoredConfig config, boolean value) {
754
755
756
757 String uriPattern = uri.getScheme() + "://" + uri.getHost(); //$NON-NLS-1$
758 int port = uri.getPort();
759 if (port > 0) {
760 uriPattern += ":" + port;
761 }
762 config.setBoolean(HttpConfig.HTTP, uriPattern,
763 HttpConfig.SSL_VERIFY_KEY, value);
764 try {
765 config.save();
766 } catch (IOException e) {
767 LOG.error(JGitText.get().sslVerifyCannotSave, e);
768 }
769 }
770
771 private void updateSslVerifyUser(boolean value) {
772 StoredConfig userConfig = null;
773 try {
774 userConfig = SystemReader.getInstance().getUserConfig();
775 updateSslVerify(userConfig, value);
776 } catch (IOException | ConfigInvalidException e) {
777
778 LOG.error(e.getMessage(), e);
779 }
780 }
781
782 private URIish redirect(URL currentUrl, String location, String checkFor,
783 int redirects)
784 throws TransportException {
785 if (location == null || location.isEmpty()) {
786 throw new TransportException(uri,
787 MessageFormat.format(JGitText.get().redirectLocationMissing,
788 baseUrl));
789 }
790 if (redirects >= http.getMaxRedirects()) {
791 throw new TransportException(uri,
792 MessageFormat.format(JGitText.get().redirectLimitExceeded,
793 Integer.valueOf(http.getMaxRedirects()), baseUrl,
794 location));
795 }
796 try {
797 URI redirectTo = new URI(location);
798 redirectTo = currentUrl.toURI().resolve(redirectTo);
799 String redirected = redirectTo.toASCIIString();
800 if (!isValidRedirect(baseUrl, redirected, checkFor)) {
801 throw new TransportException(uri,
802 MessageFormat.format(JGitText.get().redirectBlocked,
803 baseUrl, redirected));
804 }
805 redirected = redirected.substring(0, redirected.indexOf(checkFor));
806 URIish result = new URIish(redirected);
807 if (LOG.isInfoEnabled()) {
808 LOG.info(MessageFormat.format(JGitText.get().redirectHttp,
809 uri.setPass(null),
810 Integer.valueOf(redirects), baseUrl, result));
811 }
812 return result;
813 } catch (URISyntaxException e) {
814 throw new TransportException(uri,
815 MessageFormat.format(JGitText.get().invalidRedirectLocation,
816 baseUrl, location),
817 e);
818 }
819 }
820
821 private boolean isValidRedirect(URL current, String next, String checkFor) {
822
823
824 String oldProtocol = current.getProtocol().toLowerCase(Locale.ROOT);
825 int schemeEnd = next.indexOf("://"); //$NON-NLS-1$
826 if (schemeEnd < 0) {
827 return false;
828 }
829 String newProtocol = next.substring(0, schemeEnd)
830 .toLowerCase(Locale.ROOT);
831 if (!oldProtocol.equals(newProtocol)) {
832 if (!"https".equals(newProtocol)) {
833 return false;
834 }
835 }
836
837
838 if (!next.contains(checkFor)) {
839 return false;
840 }
841
842
843
844 return true;
845 }
846
847 private URL getServiceURL(String service)
848 throws NotSupportedException {
849 try {
850 final StringBuilder b = new StringBuilder();
851 b.append(baseUrl);
852
853 if (b.charAt(b.length() - 1) != '/') {
854 b.append('/');
855 }
856 b.append(Constants.INFO_REFS);
857
858 if (useSmartHttp) {
859 b.append(b.indexOf("?") < 0 ? '?' : '&');
860 b.append("service=");
861 b.append(service);
862 }
863
864 return new URL(b.toString());
865 } catch (MalformedURLException e) {
866 throw new NotSupportedException(MessageFormat.format(JGitText.get().invalidURL, uri), e);
867 }
868 }
869
870
871
872
873
874
875
876
877
878
879
880 protected HttpConnection httpOpen(String method, URL u,
881 AcceptEncoding acceptEncoding) throws IOException {
882 if (method == null || u == null || acceptEncoding == null) {
883 throw new NullPointerException();
884 }
885
886 final Proxy proxy = HttpSupport.proxyFor(proxySelector, u);
887 HttpConnection conn = connectionFactory.create(u, proxy);
888
889 if (!sslVerify && "https".equals(u.getProtocol())) {
890 HttpSupport.disableSslVerify(conn);
891 }
892
893
894
895 conn.setInstanceFollowRedirects(false);
896
897 conn.setRequestMethod(method);
898 conn.setUseCaches(false);
899 if (acceptEncoding == AcceptEncoding.GZIP) {
900 conn.setRequestProperty(HDR_ACCEPT_ENCODING, ENCODING_GZIP);
901 }
902 conn.setRequestProperty(HDR_PRAGMA, "no-cache");
903 if (UserAgent.get() != null) {
904 conn.setRequestProperty(HDR_USER_AGENT, UserAgent.get());
905 }
906 int timeOut = getTimeout();
907 if (timeOut != -1) {
908 int effTimeOut = timeOut * 1000;
909 conn.setConnectTimeout(effTimeOut);
910 conn.setReadTimeout(effTimeOut);
911 }
912
913 if (!relevantCookies.isEmpty()) {
914 setCookieHeader(conn);
915 }
916
917 if (this.headers != null && !this.headers.isEmpty()) {
918 for (Map.Entry<String, String> entry : this.headers.entrySet()) {
919 conn.setRequestProperty(entry.getKey(), entry.getValue());
920 }
921 }
922 authMethod.configureRequest(conn);
923 return conn;
924 }
925
926 private void setCookieHeader(HttpConnection conn) {
927 StringBuilder cookieHeaderValue = new StringBuilder();
928 for (HttpCookie cookie : relevantCookies) {
929 if (!cookie.hasExpired()) {
930 if (cookieHeaderValue.length() > 0) {
931 cookieHeaderValue.append(';');
932 }
933 cookieHeaderValue.append(cookie.toString());
934 }
935 }
936 if (cookieHeaderValue.length() > 0) {
937 conn.setRequestProperty(HDR_COOKIE, cookieHeaderValue.toString());
938 }
939 }
940
941 final InputStream openInputStream(HttpConnection conn)
942 throws IOException {
943 InputStream input = conn.getInputStream();
944 if (isGzipContent(conn))
945 input = new GZIPInputStream(input);
946 return input;
947 }
948
949 IOException wrongContentType(String expType, String actType) {
950 final String why = MessageFormat.format(JGitText.get().expectedReceivedContentType, expType, actType);
951 return new TransportException(uri, why);
952 }
953
954 private static NetscapeCookieFile getCookieFileFromConfig(
955 HttpConfig config) {
956 if (!StringUtils.isEmptyOrNull(config.getCookieFile())) {
957 try {
958 Path cookieFilePath = Paths.get(config.getCookieFile());
959 return NetscapeCookieFileCache.getInstance(config)
960 .getEntry(cookieFilePath);
961 } catch (InvalidPathException e) {
962 LOG.warn(MessageFormat.format(
963 JGitText.get().couldNotReadCookieFile,
964 config.getCookieFile()), e);
965 }
966 }
967 return null;
968 }
969
970 private static Set<HttpCookie> filterCookies(NetscapeCookieFile cookieFile,
971 URL url) {
972 if (cookieFile != null) {
973 return filterCookies(cookieFile.getCookies(true), url);
974 }
975 return Collections.emptySet();
976 }
977
978
979
980
981
982
983
984
985
986
987
988 private static Set<HttpCookie> filterCookies(Set<HttpCookie> allCookies,
989 URL url) {
990 Set<HttpCookie> filteredCookies = new HashSet<>();
991 for (HttpCookie cookie : allCookies) {
992 if (cookie.hasExpired()) {
993 continue;
994 }
995 if (!matchesCookieDomain(url.getHost(), cookie.getDomain())) {
996 continue;
997 }
998 if (!matchesCookiePath(url.getPath(), cookie.getPath())) {
999 continue;
1000 }
1001 if (cookie.getSecure() && !"https".equals(url.getProtocol())) {
1002 continue;
1003 }
1004 filteredCookies.add(cookie);
1005 }
1006 return filteredCookies;
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051 static boolean matchesCookieDomain(String host, String cookieDomain) {
1052 cookieDomain = cookieDomain.toLowerCase(Locale.ROOT);
1053 host = host.toLowerCase(Locale.ROOT);
1054 if (host.equals(cookieDomain)) {
1055 return true;
1056 }
1057 if (!host.endsWith(cookieDomain)) {
1058 return false;
1059 }
1060 return host.charAt(host.length() - cookieDomain.length() - 1) == '.';
1061 }
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086 static boolean matchesCookiePath(String path, String cookiePath) {
1087 if (cookiePath.equals(path)) {
1088 return true;
1089 }
1090 if (!cookiePath.endsWith("/")) {
1091 cookiePath += "/";
1092 }
1093 return path.startsWith(cookiePath);
1094 }
1095
1096 private boolean isSmartHttp(HttpConnection c, String service) {
1097 final String expType = "application/x-" + service + "-advertisement";
1098 final String actType = c.getContentType();
1099 return expType.equals(actType);
1100 }
1101
1102 private boolean isGzipContent(HttpConnection c) {
1103 return ENCODING_GZIP.equals(c.getHeaderField(HDR_CONTENT_ENCODING))
1104 || ENCODING_X_GZIP.equals(c.getHeaderField(HDR_CONTENT_ENCODING));
1105 }
1106
1107 private void readSmartHeaders(InputStream in, String service)
1108 throws IOException {
1109
1110
1111
1112
1113
1114 final byte[] magic = new byte[5];
1115 IO.readFully(in, magic, 0, magic.length);
1116 if (magic[4] != '#') {
1117 throw new TransportException(uri, MessageFormat.format(
1118 JGitText.get().expectedPktLineWithService, RawParseUtils.decode(magic)));
1119 }
1120
1121 final PacketLineInLineIn.html#PacketLineIn">PacketLineIn pckIn = new PacketLineIn(new UnionInputStream(
1122 new ByteArrayInputStream(magic), in));
1123 final String exp = "# service=" + service;
1124 final String act = pckIn.readString();
1125 if (!exp.equals(act)) {
1126 throw new TransportException(uri, MessageFormat.format(
1127 JGitText.get().expectedGot, exp, act));
1128 }
1129
1130 while (!PacketLineIn.isEnd(pckIn.readString())) {
1131
1132 }
1133 }
1134
1135 class HttpObjectDB extends WalkRemoteObjectDatabase {
1136 private final URL httpObjectsUrl;
1137
1138 HttpObjectDB(URL b) {
1139 httpObjectsUrl = b;
1140 }
1141
1142 @Override
1143 URIish getURI() {
1144 return new URIish(httpObjectsUrl);
1145 }
1146
1147 @Override
1148 Collection<WalkRemoteObjectDatabase> getAlternates() throws IOException {
1149 try {
1150 return readAlternates(INFO_HTTP_ALTERNATES);
1151 } catch (FileNotFoundException err) {
1152
1153 }
1154
1155 try {
1156 return readAlternates(INFO_ALTERNATES);
1157 } catch (FileNotFoundException err) {
1158
1159 }
1160
1161 return null;
1162 }
1163
1164 @Override
1165 WalkRemoteObjectDatabase openAlternate(String location)
1166 throws IOException {
1167 return new HttpObjectDB(new URL(httpObjectsUrl, location));
1168 }
1169
1170 @Override
1171 BufferedReader openReader(String path) throws IOException {
1172
1173
1174 InputStream is = open(path, AcceptEncoding.GZIP).in;
1175 return new BufferedReader(new InputStreamReader(is, UTF_8));
1176 }
1177
1178 @Override
1179 Collection<String> getPackNames() throws IOException {
1180 final Collection<String> packs = new ArrayList<>();
1181 try (BufferedReader br = openReader(INFO_PACKS)) {
1182 for (;;) {
1183 final String s = br.readLine();
1184 if (s == null || s.length() == 0)
1185 break;
1186 if (!s.startsWith("P pack-") || !s.endsWith(".pack"))
1187 throw invalidAdvertisement(s);
1188 packs.add(s.substring(2));
1189 }
1190 return packs;
1191 } catch (FileNotFoundException err) {
1192 return packs;
1193 }
1194 }
1195
1196 @Override
1197 FileStream open(String path) throws IOException {
1198 return open(path, AcceptEncoding.UNSPECIFIED);
1199 }
1200
1201 FileStream open(String path, AcceptEncoding acceptEncoding)
1202 throws IOException {
1203 final URL base = httpObjectsUrl;
1204 final URL u = new URL(base, path);
1205 final HttpConnection c = httpOpen(METHOD_GET, u, acceptEncoding);
1206 switch (HttpSupport.response(c)) {
1207 case HttpConnection.HTTP_OK:
1208 final InputStream in = openInputStream(c);
1209
1210
1211
1212 if (!isGzipContent(c)) {
1213 final int len = c.getContentLength();
1214 return new FileStream(in, len);
1215 }
1216 return new FileStream(in);
1217 case HttpConnection.HTTP_NOT_FOUND:
1218 throw new FileNotFoundException(u.toString());
1219 default:
1220 throw new IOException(u.toString() + ": "
1221 + HttpSupport.response(c) + " "
1222 + c.getResponseMessage());
1223 }
1224 }
1225
1226 Map<String, Ref> readAdvertisedImpl(final BufferedReader br)
1227 throws IOException, PackProtocolException {
1228 final TreeMap<String, Ref> avail = new TreeMap<>();
1229 for (;;) {
1230 String line = br.readLine();
1231 if (line == null)
1232 break;
1233
1234 final int tab = line.indexOf('\t');
1235 if (tab < 0)
1236 throw invalidAdvertisement(line);
1237
1238 String name;
1239 final ObjectId id;
1240
1241 name = line.substring(tab + 1);
1242 id = ObjectId.fromString(line.substring(0, tab));
1243 if (name.endsWith("^{}")) {
1244 name = name.substring(0, name.length() - 3);
1245 final Ref prior = avail.get(name);
1246 if (prior == null)
1247 throw outOfOrderAdvertisement(name);
1248
1249 if (prior.getPeeledObjectId() != null)
1250 throw duplicateAdvertisement(name + "^{}");
1251
1252 avail.put(name, new ObjectIdRef.PeeledTag(
1253 Ref.Storage.NETWORK, name,
1254 prior.getObjectId(), id));
1255 } else {
1256 Ref prior = avail.put(name, new ObjectIdRef.PeeledNonTag(
1257 Ref.Storage.NETWORK, name, id));
1258 if (prior != null)
1259 throw duplicateAdvertisement(name);
1260 }
1261 }
1262 return avail;
1263 }
1264
1265 private PackProtocolException outOfOrderAdvertisement(String n) {
1266 return new PackProtocolException(MessageFormat.format(JGitText.get().advertisementOfCameBefore, n, n));
1267 }
1268
1269 private PackProtocolException invalidAdvertisement(String n) {
1270 return new PackProtocolException(MessageFormat.format(JGitText.get().invalidAdvertisementOf, n));
1271 }
1272
1273 private PackProtocolException duplicateAdvertisement(String n) {
1274 return new PackProtocolException(MessageFormat.format(JGitText.get().duplicateAdvertisementsOf, n));
1275 }
1276
1277 @Override
1278 void close() {
1279
1280 }
1281 }
1282
1283 class SmartHttpFetchConnection extends BasePackFetchConnection {
1284 private MultiRequestService svc;
1285
1286 SmartHttpFetchConnection(InputStream advertisement)
1287 throws TransportException {
1288 super(TransportHttp.this);
1289 statelessRPC = true;
1290
1291 init(advertisement, DisabledOutputStream.INSTANCE);
1292 outNeedsEnd = false;
1293 readAdvertisedRefs();
1294 }
1295
1296 @Override
1297 protected void doFetch(final ProgressMonitor monitor,
1298 final Collection<Ref> want, final Set<ObjectId> have,
1299 final OutputStream outputStream) throws TransportException {
1300 try {
1301 svc = new MultiRequestService(SVC_UPLOAD_PACK);
1302 init(svc.getInputStream(), svc.getOutputStream());
1303 super.doFetch(monitor, want, have, outputStream);
1304 } finally {
1305 svc = null;
1306 }
1307 }
1308
1309 @Override
1310 protected void onReceivePack() {
1311 svc.finalRequest = true;
1312 }
1313 }
1314
1315 class SmartHttpPushConnection extends BasePackPushConnection {
1316 SmartHttpPushConnection(InputStream advertisement)
1317 throws TransportException {
1318 super(TransportHttp.this);
1319 statelessRPC = true;
1320
1321 init(advertisement, DisabledOutputStream.INSTANCE);
1322 outNeedsEnd = false;
1323 readAdvertisedRefs();
1324 }
1325
1326 @Override
1327 protected void doPush(final ProgressMonitor monitor,
1328 final Map<String, RemoteRefUpdate> refUpdates,
1329 OutputStream outputStream) throws TransportException {
1330 final Service svc = new MultiRequestService(SVC_RECEIVE_PACK);
1331 init(svc.getInputStream(), svc.getOutputStream());
1332 super.doPush(monitor, refUpdates, outputStream);
1333 }
1334 }
1335
1336
1337 abstract class Service {
1338 protected final String serviceName;
1339
1340 protected final String requestType;
1341
1342 protected final String responseType;
1343
1344 protected HttpConnection conn;
1345
1346 protected HttpOutputStream out;
1347
1348 protected final HttpExecuteStream execute;
1349
1350 final UnionInputStream in;
1351
1352 Service(String serviceName) {
1353 this.serviceName = serviceName;
1354 this.requestType = "application/x-" + serviceName + "-request";
1355 this.responseType = "application/x-" + serviceName + "-result";
1356
1357 this.out = new HttpOutputStream();
1358 this.execute = new HttpExecuteStream();
1359 this.in = new UnionInputStream(execute);
1360 }
1361
1362 void openStream() throws IOException {
1363 conn = httpOpen(METHOD_POST, new URL(baseUrl, serviceName),
1364 AcceptEncoding.GZIP);
1365 conn.setInstanceFollowRedirects(false);
1366 conn.setDoOutput(true);
1367 conn.setRequestProperty(HDR_CONTENT_TYPE, requestType);
1368 conn.setRequestProperty(HDR_ACCEPT, responseType);
1369 }
1370
1371 void sendRequest() throws IOException {
1372
1373 TemporaryBuffer buf = new TemporaryBuffer.Heap(
1374 http.getPostBuffer());
1375 try (GZIPOutputStream gzip = new GZIPOutputStream(buf)) {
1376 out.writeTo(gzip, null);
1377 if (out.length() < buf.length())
1378 buf = out;
1379 } catch (IOException err) {
1380
1381
1382 buf = out;
1383 }
1384
1385 HttpAuthMethod authenticator = null;
1386 Collection<Type> ignoreTypes = EnumSet.noneOf(Type.class);
1387
1388
1389 int authAttempts = 1;
1390 int redirects = 0;
1391 for (;;) {
1392 try {
1393
1394
1395
1396
1397
1398 openStream();
1399 if (buf != out) {
1400 conn.setRequestProperty(HDR_CONTENT_ENCODING,
1401 ENCODING_GZIP);
1402 }
1403 conn.setFixedLengthStreamingMode((int) buf.length());
1404 try (OutputStream httpOut = conn.getOutputStream()) {
1405 buf.writeTo(httpOut, null);
1406 }
1407
1408 final int status = HttpSupport.response(conn);
1409 switch (status) {
1410 case HttpConnection.HTTP_OK:
1411
1412 return;
1413
1414 case HttpConnection.HTTP_NOT_FOUND:
1415 throw createNotFoundException(uri, conn.getURL(),
1416 conn.getResponseMessage());
1417
1418 case HttpConnection.HTTP_FORBIDDEN:
1419 throw new TransportException(uri,
1420 MessageFormat.format(
1421 JGitText.get().serviceNotPermitted,
1422 baseUrl, serviceName));
1423
1424 case HttpConnection.HTTP_MOVED_PERM:
1425 case HttpConnection.HTTP_MOVED_TEMP:
1426 case HttpConnection.HTTP_11_MOVED_PERM:
1427 case HttpConnection.HTTP_11_MOVED_TEMP:
1428
1429
1430
1431 if (http.getFollowRedirects() != HttpRedirectMode.TRUE) {
1432
1433 return;
1434 }
1435 currentUri = redirect(conn.getURL(),
1436 conn.getHeaderField(HDR_LOCATION),
1437 '/' + serviceName, redirects++);
1438 try {
1439 baseUrl = toURL(currentUri);
1440 } catch (MalformedURLException e) {
1441 throw new TransportException(uri,
1442 MessageFormat.format(
1443 JGitText.get().invalidRedirectLocation,
1444 baseUrl, currentUri),
1445 e);
1446 }
1447 continue;
1448
1449 case HttpConnection.HTTP_UNAUTHORIZED:
1450 HttpAuthMethod nextMethod = HttpAuthMethod
1451 .scanResponse(conn, ignoreTypes);
1452 switch (nextMethod.getType()) {
1453 case NONE:
1454 throw new TransportException(uri,
1455 MessageFormat.format(
1456 JGitText.get().authenticationNotSupported,
1457 conn.getURL()));
1458 case NEGOTIATE:
1459
1460
1461
1462
1463
1464
1465
1466 ignoreTypes.add(HttpAuthMethod.Type.NEGOTIATE);
1467 if (authenticator != null) {
1468 ignoreTypes.add(authenticator.getType());
1469 }
1470 authAttempts = 1;
1471
1472
1473 break;
1474 default:
1475
1476
1477
1478 ignoreTypes.add(HttpAuthMethod.Type.NEGOTIATE);
1479 if (authenticator == null || authenticator
1480 .getType() != nextMethod.getType()) {
1481 if (authenticator != null) {
1482 ignoreTypes.add(authenticator.getType());
1483 }
1484 authAttempts = 1;
1485 }
1486 break;
1487 }
1488 authMethod = nextMethod;
1489 authenticator = nextMethod;
1490 CredentialsProvider credentialsProvider = getCredentialsProvider();
1491 if (credentialsProvider == null) {
1492 throw new TransportException(uri,
1493 JGitText.get().noCredentialsProvider);
1494 }
1495 if (authAttempts > 1) {
1496 credentialsProvider.reset(currentUri);
1497 }
1498 if (3 < authAttempts || !authMethod
1499 .authorize(currentUri, credentialsProvider)) {
1500 throw new TransportException(uri,
1501 JGitText.get().notAuthorized);
1502 }
1503 authAttempts++;
1504 continue;
1505
1506 default:
1507
1508
1509 return;
1510 }
1511 } catch (SSLHandshakeException e) {
1512 handleSslFailure(e);
1513 continue;
1514 } catch (SocketException | InterruptedIOException e) {
1515
1516
1517 throw e;
1518 } catch (IOException e) {
1519 if (authenticator == null || authMethod
1520 .getType() != HttpAuthMethod.Type.NONE) {
1521
1522
1523
1524
1525
1526
1527
1528
1529 if (authMethod.getType() != HttpAuthMethod.Type.NONE) {
1530 ignoreTypes.add(authMethod.getType());
1531 }
1532
1533 authMethod = HttpAuthMethod.Type.NONE.method(null);
1534 authenticator = authMethod;
1535 authAttempts = 1;
1536 continue;
1537 }
1538 throw e;
1539 }
1540 }
1541 }
1542
1543 void openResponse() throws IOException {
1544 final int status = HttpSupport.response(conn);
1545 if (status != HttpConnection.HTTP_OK) {
1546 throw new TransportException(uri, status + " "
1547 + conn.getResponseMessage());
1548 }
1549
1550 final String contentType = conn.getContentType();
1551 if (!responseType.equals(contentType)) {
1552 conn.getInputStream().close();
1553 throw wrongContentType(responseType, contentType);
1554 }
1555 }
1556
1557 HttpOutputStream getOutputStream() {
1558 return out;
1559 }
1560
1561 InputStream getInputStream() {
1562 return in;
1563 }
1564
1565 abstract void execute() throws IOException;
1566
1567 class HttpExecuteStream extends InputStream {
1568 @Override
1569 public int read() throws IOException {
1570 execute();
1571 return -1;
1572 }
1573
1574 @Override
1575 public int read(byte[] b, int off, int len) throws IOException {
1576 execute();
1577 return -1;
1578 }
1579
1580 @Override
1581 public long skip(long n) throws IOException {
1582 execute();
1583 return 0;
1584 }
1585 }
1586
1587 class HttpOutputStream extends TemporaryBuffer {
1588 HttpOutputStream() {
1589 super(http.getPostBuffer());
1590 }
1591
1592 @Override
1593 protected OutputStream overflow() throws IOException {
1594 openStream();
1595 conn.setChunkedStreamingMode(0);
1596 return conn.getOutputStream();
1597 }
1598 }
1599 }
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621 class MultiRequestService extends Service {
1622 boolean finalRequest;
1623
1624 MultiRequestService(String serviceName) {
1625 super(serviceName);
1626 }
1627
1628
1629 @Override
1630 void execute() throws IOException {
1631 out.close();
1632
1633 if (conn == null) {
1634 if (out.length() == 0) {
1635
1636
1637
1638
1639
1640 if (finalRequest)
1641 return;
1642 throw new TransportException(uri,
1643 JGitText.get().startingReadStageWithoutWrittenRequestDataPendingIsNotSupported);
1644 }
1645
1646 sendRequest();
1647 }
1648
1649 out.reset();
1650
1651 openResponse();
1652
1653 in.add(openInputStream(conn));
1654 if (!finalRequest)
1655 in.add(execute);
1656 conn = null;
1657 }
1658 }
1659
1660
1661 class LongPollService extends Service {
1662
1663
1664
1665 LongPollService(String serviceName) {
1666 super(serviceName);
1667 }
1668
1669
1670 @Override
1671 void execute() throws IOException {
1672 out.close();
1673 if (conn == null)
1674 sendRequest();
1675 openResponse();
1676 in.add(openInputStream(conn));
1677 }
1678 }
1679 }