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