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