1
2
3
4
5
6
7
8
9
10
11 package org.eclipse.jgit.http.test;
12
13 import static java.nio.charset.StandardCharsets.UTF_8;
14 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
15 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
16 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
17 import static org.junit.Assert.assertEquals;
18 import static org.junit.Assert.assertFalse;
19 import static org.junit.Assert.assertNotNull;
20 import static org.junit.Assert.assertNull;
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.File;
27 import java.io.IOException;
28 import java.io.OutputStreamWriter;
29 import java.io.PrintWriter;
30 import java.io.Writer;
31 import java.net.Proxy;
32 import java.net.URI;
33 import java.net.URISyntaxException;
34 import java.net.URL;
35 import java.nio.charset.StandardCharsets;
36 import java.text.MessageFormat;
37 import java.util.Collections;
38 import java.util.EnumSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43
44 import javax.servlet.DispatcherType;
45 import javax.servlet.Filter;
46 import javax.servlet.FilterChain;
47 import javax.servlet.FilterConfig;
48 import javax.servlet.RequestDispatcher;
49 import javax.servlet.ServletException;
50 import javax.servlet.ServletRequest;
51 import javax.servlet.ServletResponse;
52 import javax.servlet.http.HttpServletRequest;
53 import javax.servlet.http.HttpServletResponse;
54
55 import org.eclipse.jetty.servlet.FilterHolder;
56 import org.eclipse.jetty.servlet.ServletContextHandler;
57 import org.eclipse.jetty.servlet.ServletHolder;
58 import org.eclipse.jgit.api.Git;
59 import org.eclipse.jgit.api.TransportConfigCallback;
60 import org.eclipse.jgit.errors.RemoteRepositoryException;
61 import org.eclipse.jgit.errors.TransportException;
62 import org.eclipse.jgit.errors.UnsupportedCredentialItem;
63 import org.eclipse.jgit.http.server.GitServlet;
64 import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
65 import org.eclipse.jgit.internal.JGitText;
66 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
67 import org.eclipse.jgit.junit.TestRepository;
68 import org.eclipse.jgit.junit.TestRng;
69 import org.eclipse.jgit.junit.http.AccessEvent;
70 import org.eclipse.jgit.junit.http.AppServer;
71 import org.eclipse.jgit.lib.ConfigConstants;
72 import org.eclipse.jgit.lib.Constants;
73 import org.eclipse.jgit.lib.NullProgressMonitor;
74 import org.eclipse.jgit.lib.ObjectId;
75 import org.eclipse.jgit.lib.ObjectIdRef;
76 import org.eclipse.jgit.lib.ObjectInserter;
77 import org.eclipse.jgit.lib.Ref;
78 import org.eclipse.jgit.lib.ReflogEntry;
79 import org.eclipse.jgit.lib.ReflogReader;
80 import org.eclipse.jgit.lib.Repository;
81 import org.eclipse.jgit.lib.StoredConfig;
82 import org.eclipse.jgit.lib.TextProgressMonitor;
83 import org.eclipse.jgit.revwalk.RevBlob;
84 import org.eclipse.jgit.revwalk.RevCommit;
85 import org.eclipse.jgit.revwalk.RevWalk;
86 import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
87 import org.eclipse.jgit.transport.AdvertiseRefsHook;
88 import org.eclipse.jgit.transport.CredentialItem;
89 import org.eclipse.jgit.transport.CredentialsProvider;
90 import org.eclipse.jgit.transport.FetchConnection;
91 import org.eclipse.jgit.transport.HttpTransport;
92 import org.eclipse.jgit.transport.RefSpec;
93 import org.eclipse.jgit.transport.RemoteRefUpdate;
94 import org.eclipse.jgit.transport.Transport;
95 import org.eclipse.jgit.transport.TransportHttp;
96 import org.eclipse.jgit.transport.URIish;
97 import org.eclipse.jgit.transport.UploadPack;
98 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
99 import org.eclipse.jgit.transport.http.HttpConnection;
100 import org.eclipse.jgit.transport.http.HttpConnectionFactory;
101 import org.eclipse.jgit.util.HttpSupport;
102 import org.eclipse.jgit.util.SystemReader;
103 import org.junit.Before;
104 import org.junit.Test;
105
106 public class SmartClientSmartServerTest extends AllProtocolsHttpTestCase {
107 private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
108
109 private AdvertiseRefsHook advertiseRefsHook;
110
111 private Repository remoteRepository;
112
113 private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider(
114 AppServer.username, AppServer.password);
115
116 private URIish remoteURI;
117
118 private URIish brokenURI;
119
120 private URIish redirectURI;
121
122 private URIish authURI;
123
124 private URIish authOnPostURI;
125
126 private URIish slowURI;
127
128 private URIish slowAuthURI;
129
130 private RevBlob A_txt;
131
132 private RevCommit A, B, unreachableCommit;
133
134 public SmartClientSmartServerTest(TestParameters params) {
135 super(params);
136 }
137
138 @Override
139 @Before
140 public void setUp() throws Exception {
141 super.setUp();
142
143 final TestRepository<Repository> src = createTestRepository();
144 final String srcName = src.getRepository().getDirectory().getName();
145 StoredConfig cfg = src.getRepository().getConfig();
146 cfg.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
147 ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
148 cfg.setInt("protocol", null, "version", enableProtocolV2 ? 2 : 0);
149 cfg.save();
150
151 GitServlet gs = new GitServlet();
152 gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
153 DefaultUploadPackFactory f = new DefaultUploadPackFactory();
154 UploadPack up = f.create(req, db);
155 if (advertiseRefsHook != null) {
156 up.setAdvertiseRefsHook(advertiseRefsHook);
157 }
158 return up;
159 });
160
161 ServletContextHandler app = addNormalContext(gs, src, srcName);
162
163 ServletContextHandler broken = addBrokenContext(gs, srcName);
164
165 ServletContextHandler redirect = addRedirectContext(gs);
166
167 ServletContextHandler auth = addAuthContext(gs, "auth");
168
169 ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST");
170
171 ServletContextHandler slow = addSlowContext(gs, "slow", false);
172
173 ServletContextHandler slowAuth = addSlowContext(gs, "slowAuth", true);
174
175 server.setUp();
176
177 remoteRepository = src.getRepository();
178 remoteURI = toURIish(app, srcName);
179 brokenURI = toURIish(broken, srcName);
180 redirectURI = toURIish(redirect, srcName);
181 authURI = toURIish(auth, srcName);
182 authOnPostURI = toURIish(authOnPost, srcName);
183 slowURI = toURIish(slow, srcName);
184 slowAuthURI = toURIish(slowAuth, srcName);
185
186 A_txt = src.blob("A");
187 A = src.commit().add("A_txt", A_txt).create();
188 B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
189 src.update(master, B);
190
191 unreachableCommit = src.commit().add("A_txt", A_txt).create();
192
193 src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
194 }
195
196 private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
197 ServletContextHandler app = server.addContext("/git");
198 app.addFilter(new FilterHolder(new Filter() {
199
200 @Override
201 public void init(FilterConfig filterConfig)
202 throws ServletException {
203
204 }
205
206
207
208
209 @Override
210 public void doFilter(ServletRequest request,
211 ServletResponse response, FilterChain chain)
212 throws IOException, ServletException {
213 final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
214 final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
215 final StringBuffer fullUrl = httpServletRequest.getRequestURL();
216 if (httpServletRequest.getQueryString() != null) {
217 fullUrl.append("?")
218 .append(httpServletRequest.getQueryString());
219 }
220 String urlString = fullUrl.toString();
221 if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
222 httpServletResponse.setStatus(
223 HttpServletResponse.SC_MOVED_PERMANENTLY);
224 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
225 urlString.replace("/post/", "/"));
226 } else {
227 String path = httpServletRequest.getPathInfo();
228 path = path.replace("/post/", "/");
229 if (httpServletRequest.getQueryString() != null) {
230 path += '?' + httpServletRequest.getQueryString();
231 }
232 RequestDispatcher dispatcher = httpServletRequest
233 .getRequestDispatcher(path);
234 dispatcher.forward(httpServletRequest, httpServletResponse);
235 }
236 }
237
238 @Override
239 public void destroy() {
240
241 }
242 }), "/post/*", EnumSet.of(DispatcherType.REQUEST));
243 gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName));
244 app.addServlet(new ServletHolder(gs), "/*");
245 return app;
246 }
247
248 private ServletContextHandler addBrokenContext(GitServlet gs,
249 String srcName) {
250 ServletContextHandler broken = server.addContext("/bad");
251 broken.addFilter(new FilterHolder(new Filter() {
252
253 @Override
254 public void doFilter(ServletRequest request,
255 ServletResponse response, FilterChain chain)
256 throws IOException, ServletException {
257 final HttpServletResponse r = (HttpServletResponse) response;
258 r.setContentType("text/plain");
259 r.setCharacterEncoding(UTF_8.name());
260 try (PrintWriter w = r.getWriter()) {
261 w.print("OK");
262 }
263 }
264
265 @Override
266 public void init(FilterConfig filterConfig)
267 throws ServletException {
268
269 }
270
271 @Override
272 public void destroy() {
273
274 }
275 }), "/" + srcName + "/git-upload-pack",
276 EnumSet.of(DispatcherType.REQUEST));
277 broken.addServlet(new ServletHolder(gs), "/*");
278 return broken;
279 }
280
281 private ServletContextHandler addAuthContext(GitServlet gs,
282 String contextPath, String... methods) {
283 ServletContextHandler auth = server.addContext('/' + contextPath);
284 auth.addServlet(new ServletHolder(gs), "/*");
285 return server.authBasic(auth, methods);
286 }
287
288 private ServletContextHandler addRedirectContext(GitServlet gs) {
289 ServletContextHandler redirect = server.addContext("/redirect");
290 redirect.addFilter(new FilterHolder(new Filter() {
291
292
293
294
295 private Pattern responsePattern = Pattern
296 .compile("/response/(\\d+)/(30[1237])/");
297
298
299
300
301 private Pattern targetPattern = Pattern.compile("/target(/\\w+)/");
302
303 @Override
304 public void init(FilterConfig filterConfig)
305 throws ServletException {
306
307 }
308
309 private String local(String url, boolean toLocal) {
310 if (!toLocal) {
311 return url;
312 }
313 try {
314 URI u = new URI(url);
315 String fragment = u.getRawFragment();
316 if (fragment != null) {
317 return u.getRawPath() + '#' + fragment;
318 }
319 return u.getRawPath();
320 } catch (URISyntaxException e) {
321 return url;
322 }
323 }
324
325 @Override
326 public void doFilter(ServletRequest request,
327 ServletResponse response, FilterChain chain)
328 throws IOException, ServletException {
329 final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
330 final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
331 final StringBuffer fullUrl = httpServletRequest.getRequestURL();
332 if (httpServletRequest.getQueryString() != null) {
333 fullUrl.append("?")
334 .append(httpServletRequest.getQueryString());
335 }
336 String urlString = fullUrl.toString();
337 boolean localRedirect = false;
338 if (urlString.contains("/local")) {
339 urlString = urlString.replace("/local", "");
340 localRedirect = true;
341 }
342 if (urlString.contains("/loop/")) {
343 urlString = urlString.replace("/loop/", "/loop/x/");
344 if (urlString.contains("/loop/x/x/x/x/x/x/x/x/")) {
345
346 urlString = urlString.replace("/loop/x/x/x/x/x/x/x/x/",
347 "/loop/");
348 }
349 httpServletResponse.setStatus(
350 HttpServletResponse.SC_MOVED_TEMPORARILY);
351 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
352 local(urlString, localRedirect));
353 return;
354 }
355 int responseCode = HttpServletResponse.SC_MOVED_PERMANENTLY;
356 int nofRedirects = 0;
357 Matcher matcher = responsePattern.matcher(urlString);
358 if (matcher.find()) {
359 nofRedirects = Integer
360 .parseUnsignedInt(matcher.group(1));
361 responseCode = Integer.parseUnsignedInt(matcher.group(2));
362 if (--nofRedirects <= 0) {
363 urlString = urlString.substring(0, matcher.start())
364 + '/' + urlString.substring(matcher.end());
365 } else {
366 urlString = urlString.substring(0, matcher.start())
367 + "/response/" + nofRedirects + "/"
368 + responseCode + '/'
369 + urlString.substring(matcher.end());
370 }
371 }
372 httpServletResponse.setStatus(responseCode);
373 if (nofRedirects <= 0) {
374 String targetContext = "/git";
375 matcher = targetPattern.matcher(urlString);
376 if (matcher.find()) {
377 urlString = urlString.substring(0, matcher.start())
378 + '/' + urlString.substring(matcher.end());
379 targetContext = matcher.group(1);
380 }
381 urlString = urlString.replace("/redirect", targetContext);
382
383 }
384 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
385 local(urlString, localRedirect));
386 }
387
388 @Override
389 public void destroy() {
390
391 }
392 }), "/*", EnumSet.of(DispatcherType.REQUEST));
393 redirect.addServlet(new ServletHolder(gs), "/*");
394 return redirect;
395 }
396
397 private ServletContextHandler addSlowContext(GitServlet gs, String path,
398 boolean auth) {
399 ServletContextHandler slow = server.addContext('/' + path);
400 slow.addFilter(new FilterHolder(new Filter() {
401
402 @Override
403 public void init(FilterConfig filterConfig)
404 throws ServletException {
405
406 }
407
408
409
410 @Override
411 public void doFilter(ServletRequest request,
412 ServletResponse response, FilterChain chain)
413 throws IOException, ServletException {
414 try {
415 Thread.sleep(2000);
416 } catch (InterruptedException e) {
417 throw new IOException(e);
418 }
419 chain.doFilter(request, response);
420 }
421
422 @Override
423 public void destroy() {
424
425 }
426 }), "/*", EnumSet.of(DispatcherType.REQUEST));
427 slow.addServlet(new ServletHolder(gs), "/*");
428 if (auth) {
429 return server.authBasic(slow);
430 }
431 return slow;
432 }
433
434 @Test
435 public void testListRemote() throws IOException {
436 assertEquals("http", remoteURI.getScheme());
437
438 Map<String, Ref> map;
439 try (Repository dst = createBareRepository();
440 Transport t = Transport.open(dst, remoteURI)) {
441
442
443
444
445 assertTrue("isa TransportHttp", t instanceof TransportHttp);
446 assertTrue("isa HttpTransport", t instanceof HttpTransport);
447
448 try (FetchConnection c = t.openFetch()) {
449 map = c.getRefsMap();
450 }
451 }
452
453 assertNotNull("have map of refs", map);
454 assertEquals(3, map.size());
455
456 assertNotNull("has " + master, map.get(master));
457 assertEquals(B, map.get(master).getObjectId());
458
459 assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
460 assertEquals(B, map.get(Constants.HEAD).getObjectId());
461
462 List<AccessEvent> requests = getRequests();
463 assertEquals(enableProtocolV2 ? 2 : 1, requests.size());
464
465 AccessEvent info = requests.get(0);
466 assertEquals("GET", info.getMethod());
467 assertEquals(join(remoteURI, "info/refs"), info.getPath());
468 assertEquals(1, info.getParameters().size());
469 assertEquals("git-upload-pack", info.getParameter("service"));
470 assertEquals(200, info.getStatus());
471 assertEquals("application/x-git-upload-pack-advertisement", info
472 .getResponseHeader(HDR_CONTENT_TYPE));
473 if (!enableProtocolV2) {
474 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
475 } else {
476 AccessEvent lsRefs = requests.get(1);
477 assertEquals("POST", lsRefs.getMethod());
478 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
479 assertEquals(0, lsRefs.getParameters().size());
480 assertNotNull("has content-length",
481 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
482 assertNull("not chunked",
483 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
484 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
485 assertEquals(200, lsRefs.getStatus());
486 assertEquals("application/x-git-upload-pack-result",
487 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
488 }
489 }
490
491 @Test
492 public void testListRemote_BadName() throws IOException, URISyntaxException {
493 URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
494 try (Repository dst = createBareRepository();
495 Transport t = Transport.open(dst, uri)) {
496 try {
497 t.openFetch();
498 fail("fetch connection opened");
499 } catch (RemoteRepositoryException notFound) {
500 assertEquals(uri + ": Git repository not found",
501 notFound.getMessage());
502 }
503 }
504
505 List<AccessEvent> requests = getRequests();
506 assertEquals(1, requests.size());
507
508 AccessEvent info = requests.get(0);
509 assertEquals("GET", info.getMethod());
510 assertEquals(join(uri, "info/refs"), info.getPath());
511 assertEquals(1, info.getParameters().size());
512 assertEquals("git-upload-pack", info.getParameter("service"));
513 assertEquals(200, info.getStatus());
514 assertEquals("application/x-git-upload-pack-advertisement",
515 info.getResponseHeader(HDR_CONTENT_TYPE));
516 }
517
518 @Test
519 public void testFetchBySHA1() throws Exception {
520 try (Repository dst = createBareRepository();
521 Transport t = Transport.open(dst, remoteURI)) {
522 assertFalse(dst.getObjectDatabase().has(A_txt));
523 t.fetch(NullProgressMonitor.INSTANCE,
524 Collections.singletonList(new RefSpec(B.name())));
525 assertTrue(dst.getObjectDatabase().has(A_txt));
526 }
527 }
528
529 @Test
530 public void testFetchBySHA1Unreachable() throws Exception {
531 try (Repository dst = createBareRepository();
532 Transport t = Transport.open(dst, remoteURI)) {
533 assertFalse(dst.getObjectDatabase().has(A_txt));
534 Exception e = assertThrows(TransportException.class,
535 () -> t.fetch(NullProgressMonitor.INSTANCE,
536 Collections.singletonList(
537 new RefSpec(unreachableCommit.name()))));
538 assertTrue(e.getMessage().contains(
539 "want " + unreachableCommit.name() + " not valid"));
540 }
541 }
542
543 @Test
544 public void testFetchBySHA1UnreachableByAdvertiseRefsHook()
545 throws Exception {
546 advertiseRefsHook = new AbstractAdvertiseRefsHook() {
547 @Override
548 protected Map<String, Ref> getAdvertisedRefs(Repository repository,
549 RevWalk revWalk) {
550 return Collections.emptyMap();
551 }
552 };
553
554 try (Repository dst = createBareRepository();
555 Transport t = Transport.open(dst, remoteURI)) {
556 assertFalse(dst.getObjectDatabase().has(A_txt));
557 Exception e = assertThrows(TransportException.class,
558 () -> t.fetch(NullProgressMonitor.INSTANCE,
559 Collections.singletonList(new RefSpec(A.name()))));
560 assertTrue(
561 e.getMessage().contains("want " + A.name() + " not valid"));
562 }
563 }
564
565 @Test
566 public void testTimeoutExpired() throws Exception {
567 try (Repository dst = createBareRepository();
568 Transport t = Transport.open(dst, slowURI)) {
569 t.setTimeout(1);
570 TransportException expected = assertThrows(TransportException.class,
571 () -> t.fetch(NullProgressMonitor.INSTANCE,
572 mirror(master)));
573 assertTrue("Unexpected exception message: " + expected.toString(),
574 expected.getMessage().contains("time"));
575 }
576 }
577
578 @Test
579 public void testTimeoutExpiredWithAuth() throws Exception {
580 try (Repository dst = createBareRepository();
581 Transport t = Transport.open(dst, slowAuthURI)) {
582 t.setTimeout(1);
583 t.setCredentialsProvider(testCredentials);
584 TransportException expected = assertThrows(TransportException.class,
585 () -> t.fetch(NullProgressMonitor.INSTANCE,
586 mirror(master)));
587 assertTrue("Unexpected exception message: " + expected.toString(),
588 expected.getMessage().contains("time"));
589 assertFalse("Unexpected exception message: " + expected.toString(),
590 expected.getMessage().contains("auth"));
591 }
592 }
593
594 @Test
595 public void testInitialClone_Small() throws Exception {
596 try (Repository dst = createBareRepository();
597 Transport t = Transport.open(dst, remoteURI)) {
598 assertFalse(dst.getObjectDatabase().has(A_txt));
599 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
600 assertTrue(dst.getObjectDatabase().has(A_txt));
601 assertEquals(B, dst.exactRef(master).getObjectId());
602 fsck(dst, B);
603 }
604
605 List<AccessEvent> requests = getRequests();
606 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
607
608 int requestNumber = 0;
609 AccessEvent info = requests.get(requestNumber++);
610 assertEquals("GET", info.getMethod());
611 assertEquals(join(remoteURI, "info/refs"), info.getPath());
612 assertEquals(1, info.getParameters().size());
613 assertEquals("git-upload-pack", info.getParameter("service"));
614 assertEquals(200, info.getStatus());
615 assertEquals("application/x-git-upload-pack-advertisement", info
616 .getResponseHeader(HDR_CONTENT_TYPE));
617 if (!enableProtocolV2) {
618 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
619 } else {
620 AccessEvent lsRefs = requests.get(requestNumber++);
621 assertEquals("POST", lsRefs.getMethod());
622 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
623 assertEquals(0, lsRefs.getParameters().size());
624 assertNotNull("has content-length",
625 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
626 assertNull("not chunked",
627 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
628 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
629 assertEquals(200, lsRefs.getStatus());
630 assertEquals("application/x-git-upload-pack-result",
631 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
632 }
633
634 AccessEvent service = requests.get(requestNumber);
635 assertEquals("POST", service.getMethod());
636 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
637 assertEquals(0, service.getParameters().size());
638 assertNotNull("has content-length", service
639 .getRequestHeader(HDR_CONTENT_LENGTH));
640 assertNull("not chunked", service
641 .getRequestHeader(HDR_TRANSFER_ENCODING));
642
643 assertEquals(200, service.getStatus());
644 assertEquals("application/x-git-upload-pack-result", service
645 .getResponseHeader(HDR_CONTENT_TYPE));
646 }
647
648 @Test
649 public void test_CloneWithCustomFactory() throws Exception {
650 HttpConnectionFactory globalFactory = HttpTransport
651 .getConnectionFactory();
652 HttpConnectionFactory failingConnectionFactory = new HttpConnectionFactory() {
653
654 @Override
655 public HttpConnection create(URL url) throws IOException {
656 throw new IOException("Should not be reached");
657 }
658
659 @Override
660 public HttpConnection create(URL url, Proxy proxy)
661 throws IOException {
662 throw new IOException("Should not be reached");
663 }
664 };
665 HttpTransport.setConnectionFactory(failingConnectionFactory);
666 try {
667 File tmp = createTempDirectory("cloneViaApi");
668 boolean[] localFactoryUsed = { false };
669 TransportConfigCallback callback = new TransportConfigCallback() {
670
671 @Override
672 public void configure(Transport transport) {
673 if (transport instanceof TransportHttp) {
674 ((TransportHttp) transport).setHttpConnectionFactory(
675 new HttpConnectionFactory() {
676
677 @Override
678 public HttpConnection create(URL url)
679 throws IOException {
680 localFactoryUsed[0] = true;
681 return globalFactory.create(url);
682 }
683
684 @Override
685 public HttpConnection create(URL url,
686 Proxy proxy) throws IOException {
687 localFactoryUsed[0] = true;
688 return globalFactory.create(url, proxy);
689 }
690 });
691 }
692 }
693 };
694 try (Git git = Git.cloneRepository().setDirectory(tmp)
695 .setTransportConfigCallback(callback)
696 .setURI(remoteURI.toPrivateString()).call()) {
697 assertTrue("Should have used the local HttpConnectionFactory",
698 localFactoryUsed[0]);
699 }
700 } finally {
701 HttpTransport.setConnectionFactory(globalFactory);
702 }
703 }
704
705 private void initialClone_Redirect(int nofRedirects, int code)
706 throws Exception {
707 initialClone_Redirect(nofRedirects, code, false);
708 }
709
710 private void initialClone_Redirect(int nofRedirects, int code,
711 boolean localRedirect) throws Exception {
712 URIish cloneFrom = redirectURI;
713 if (localRedirect) {
714 cloneFrom = extendPath(cloneFrom, "/local");
715 }
716 if (code != 301 || nofRedirects > 1) {
717 cloneFrom = extendPath(cloneFrom,
718 "/response/" + nofRedirects + "/" + code);
719 }
720
721 try (Repository dst = createBareRepository();
722 Transport t = Transport.open(dst, cloneFrom)) {
723 assertFalse(dst.getObjectDatabase().has(A_txt));
724 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
725 assertTrue(dst.getObjectDatabase().has(A_txt));
726 assertEquals(B, dst.exactRef(master).getObjectId());
727 fsck(dst, B);
728 }
729
730 List<AccessEvent> requests = getRequests();
731 assertEquals((enableProtocolV2 ? 3 : 2) + nofRedirects,
732 requests.size());
733
734 int n = 0;
735 while (n < nofRedirects) {
736 AccessEvent redirect = requests.get(n++);
737 assertEquals(code, redirect.getStatus());
738 }
739
740 AccessEvent info = requests.get(n++);
741 assertEquals("GET", info.getMethod());
742 assertEquals(join(remoteURI, "info/refs"), info.getPath());
743 assertEquals(1, info.getParameters().size());
744 assertEquals("git-upload-pack", info.getParameter("service"));
745 assertEquals(200, info.getStatus());
746 assertEquals("application/x-git-upload-pack-advertisement",
747 info.getResponseHeader(HDR_CONTENT_TYPE));
748 if (!enableProtocolV2) {
749 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
750 } else {
751 AccessEvent lsRefs = requests.get(n++);
752 assertEquals("POST", lsRefs.getMethod());
753 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
754 assertEquals(0, lsRefs.getParameters().size());
755 assertNotNull("has content-length",
756 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
757 assertNull("not chunked",
758 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
759 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
760 assertEquals(200, lsRefs.getStatus());
761 assertEquals("application/x-git-upload-pack-result",
762 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
763 }
764
765 AccessEvent service = requests.get(n++);
766 assertEquals("POST", service.getMethod());
767 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
768 assertEquals(0, service.getParameters().size());
769 assertNotNull("has content-length",
770 service.getRequestHeader(HDR_CONTENT_LENGTH));
771 assertNull("not chunked",
772 service.getRequestHeader(HDR_TRANSFER_ENCODING));
773
774 assertEquals(200, service.getStatus());
775 assertEquals("application/x-git-upload-pack-result",
776 service.getResponseHeader(HDR_CONTENT_TYPE));
777 }
778
779 @Test
780 public void testInitialClone_Redirect301Small() throws Exception {
781 initialClone_Redirect(1, 301);
782 }
783
784 @Test
785 public void testInitialClone_Redirect301Local() throws Exception {
786 initialClone_Redirect(1, 301, true);
787 }
788
789 @Test
790 public void testInitialClone_Redirect302Small() throws Exception {
791 initialClone_Redirect(1, 302);
792 }
793
794 @Test
795 public void testInitialClone_Redirect303Small() throws Exception {
796 initialClone_Redirect(1, 303);
797 }
798
799 @Test
800 public void testInitialClone_Redirect307Small() throws Exception {
801 initialClone_Redirect(1, 307);
802 }
803
804 @Test
805 public void testInitialClone_RedirectMultiple() throws Exception {
806 initialClone_Redirect(4, 302);
807 }
808
809 @Test
810 public void testInitialClone_RedirectMax() throws Exception {
811 StoredConfig userConfig = SystemReader.getInstance()
812 .getUserConfig();
813 userConfig.setInt("http", null, "maxRedirects", 4);
814 userConfig.save();
815 initialClone_Redirect(4, 302);
816 }
817
818 @Test
819 public void testInitialClone_RedirectTooOften() throws Exception {
820 StoredConfig userConfig = SystemReader.getInstance()
821 .getUserConfig();
822 userConfig.setInt("http", null, "maxRedirects", 3);
823 userConfig.save();
824
825 URIish cloneFrom = extendPath(redirectURI, "/response/4/302");
826 String remoteUri = cloneFrom.toString();
827 try (Repository dst = createBareRepository();
828 Transport t = Transport.open(dst, cloneFrom)) {
829 assertFalse(dst.getObjectDatabase().has(A_txt));
830 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
831 fail("Should have failed (too many redirects)");
832 } catch (TransportException e) {
833 String expectedMessageBegin = remoteUri.toString() + ": "
834 + MessageFormat.format(JGitText.get().redirectLimitExceeded,
835 "3", remoteUri.replace("/4/", "/1/") + '/', "");
836 String message = e.getMessage();
837 if (message.length() > expectedMessageBegin.length()) {
838 message = message.substring(0, expectedMessageBegin.length());
839 }
840 assertEquals(expectedMessageBegin, message);
841 }
842 }
843
844 @Test
845 public void testInitialClone_RedirectLoop() throws Exception {
846 URIish cloneFrom = extendPath(redirectURI, "/loop");
847 try (Repository dst = createBareRepository();
848 Transport t = Transport.open(dst, cloneFrom)) {
849 assertFalse(dst.getObjectDatabase().has(A_txt));
850 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
851 fail("Should have failed (redirect loop)");
852 } catch (TransportException e) {
853 assertTrue(e.getMessage().contains("Redirected more than"));
854 }
855 }
856
857 @Test
858 public void testInitialClone_RedirectOnPostAllowed() throws Exception {
859 StoredConfig userConfig = SystemReader.getInstance()
860 .getUserConfig();
861 userConfig.setString("http", null, "followRedirects", "true");
862 userConfig.save();
863
864 URIish cloneFrom = extendPath(remoteURI, "/post");
865 try (Repository dst = createBareRepository();
866 Transport t = Transport.open(dst, cloneFrom)) {
867 assertFalse(dst.getObjectDatabase().has(A_txt));
868 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
869 assertTrue(dst.getObjectDatabase().has(A_txt));
870 assertEquals(B, dst.exactRef(master).getObjectId());
871 fsck(dst, B);
872 }
873
874 List<AccessEvent> requests = getRequests();
875 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
876
877 AccessEvent info = requests.get(0);
878 assertEquals("GET", info.getMethod());
879 assertEquals(join(cloneFrom, "info/refs"), info.getPath());
880 assertEquals(1, info.getParameters().size());
881 assertEquals("git-upload-pack", info.getParameter("service"));
882 assertEquals(200, info.getStatus());
883 assertEquals("application/x-git-upload-pack-advertisement",
884 info.getResponseHeader(HDR_CONTENT_TYPE));
885 if (!enableProtocolV2) {
886 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
887 }
888
889 AccessEvent redirect = requests.get(1);
890 assertEquals("POST", redirect.getMethod());
891 assertEquals(301, redirect.getStatus());
892
893 for (int i = 2; i < requests.size(); i++) {
894 AccessEvent service = requests.get(i);
895 assertEquals("POST", service.getMethod());
896 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
897 assertEquals(0, service.getParameters().size());
898 assertNotNull("has content-length",
899 service.getRequestHeader(HDR_CONTENT_LENGTH));
900 assertNull("not chunked",
901 service.getRequestHeader(HDR_TRANSFER_ENCODING));
902 assertEquals(200, service.getStatus());
903 assertEquals("application/x-git-upload-pack-result",
904 service.getResponseHeader(HDR_CONTENT_TYPE));
905 }
906 }
907
908 @Test
909 public void testInitialClone_RedirectOnPostForbidden() throws Exception {
910 URIish cloneFrom = extendPath(remoteURI, "/post");
911 try (Repository dst = createBareRepository();
912 Transport t = Transport.open(dst, cloneFrom)) {
913 assertFalse(dst.getObjectDatabase().has(A_txt));
914 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
915 fail("Should have failed (redirect on POST)");
916 } catch (TransportException e) {
917 assertTrue(e.getMessage().contains("301"));
918 }
919 }
920
921 @Test
922 public void testInitialClone_RedirectForbidden() throws Exception {
923 StoredConfig userConfig = SystemReader.getInstance()
924 .getUserConfig();
925 userConfig.setString("http", null, "followRedirects", "false");
926 userConfig.save();
927
928 try (Repository dst = createBareRepository();
929 Transport t = Transport.open(dst, redirectURI)) {
930 assertFalse(dst.getObjectDatabase().has(A_txt));
931 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
932 fail("Should have failed (redirects forbidden)");
933 } catch (TransportException e) {
934 assertTrue(
935 e.getMessage().contains("http.followRedirects is false"));
936 }
937 }
938
939 private void assertFetchRequests(List<AccessEvent> requests, int index) {
940 AccessEvent info = requests.get(index++);
941 assertEquals("GET", info.getMethod());
942 assertEquals(join(authURI, "info/refs"), info.getPath());
943 assertEquals(1, info.getParameters().size());
944 assertEquals("git-upload-pack", info.getParameter("service"));
945 assertEquals(200, info.getStatus());
946 assertEquals("application/x-git-upload-pack-advertisement",
947 info.getResponseHeader(HDR_CONTENT_TYPE));
948 if (!enableProtocolV2) {
949 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
950 }
951
952 for (int i = index; i < requests.size(); i++) {
953 AccessEvent service = requests.get(i);
954 assertEquals("POST", service.getMethod());
955 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
956 assertEquals(0, service.getParameters().size());
957 assertNotNull("has content-length",
958 service.getRequestHeader(HDR_CONTENT_LENGTH));
959 assertNull("not chunked",
960 service.getRequestHeader(HDR_TRANSFER_ENCODING));
961
962 assertEquals(200, service.getStatus());
963 assertEquals("application/x-git-upload-pack-result",
964 service.getResponseHeader(HDR_CONTENT_TYPE));
965 }
966 }
967
968 @Test
969 public void testInitialClone_WithAuthentication() throws Exception {
970 try (Repository dst = createBareRepository();
971 Transport t = Transport.open(dst, authURI)) {
972 assertFalse(dst.getObjectDatabase().has(A_txt));
973 t.setCredentialsProvider(testCredentials);
974 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
975 assertTrue(dst.getObjectDatabase().has(A_txt));
976 assertEquals(B, dst.exactRef(master).getObjectId());
977 fsck(dst, B);
978 }
979
980 List<AccessEvent> requests = getRequests();
981 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
982
983 AccessEvent info = requests.get(0);
984 assertEquals("GET", info.getMethod());
985 assertEquals(401, info.getStatus());
986
987 assertFetchRequests(requests, 1);
988 }
989
990 @Test
991 public void testInitialClone_WithPreAuthentication() throws Exception {
992 try (Repository dst = createBareRepository();
993 Transport t = Transport.open(dst, authURI)) {
994 assertFalse(dst.getObjectDatabase().has(A_txt));
995 ((TransportHttp) t).setPreemptiveBasicAuthentication(
996 AppServer.username, AppServer.password);
997 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
998 assertTrue(dst.getObjectDatabase().has(A_txt));
999 assertEquals(B, dst.exactRef(master).getObjectId());
1000 fsck(dst, B);
1001 }
1002
1003 List<AccessEvent> requests = getRequests();
1004 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1005
1006 assertFetchRequests(requests, 0);
1007 }
1008
1009 @Test
1010 public void testInitialClone_WithPreAuthenticationCleared()
1011 throws Exception {
1012 try (Repository dst = createBareRepository();
1013 Transport t = Transport.open(dst, authURI)) {
1014 assertFalse(dst.getObjectDatabase().has(A_txt));
1015 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1016 AppServer.username, AppServer.password);
1017 ((TransportHttp) t).setPreemptiveBasicAuthentication(null, null);
1018 t.setCredentialsProvider(testCredentials);
1019 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1020 assertTrue(dst.getObjectDatabase().has(A_txt));
1021 assertEquals(B, dst.exactRef(master).getObjectId());
1022 fsck(dst, B);
1023 }
1024
1025 List<AccessEvent> requests = getRequests();
1026 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1027
1028 AccessEvent info = requests.get(0);
1029 assertEquals("GET", info.getMethod());
1030 assertEquals(401, info.getStatus());
1031
1032 assertFetchRequests(requests, 1);
1033 }
1034
1035 @Test
1036 public void testInitialClone_PreAuthenticationTooLate() throws Exception {
1037 try (Repository dst = createBareRepository();
1038 Transport t = Transport.open(dst, authURI)) {
1039 assertFalse(dst.getObjectDatabase().has(A_txt));
1040 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1041 AppServer.username, AppServer.password);
1042 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1043 assertTrue(dst.getObjectDatabase().has(A_txt));
1044 assertEquals(B, dst.exactRef(master).getObjectId());
1045 fsck(dst, B);
1046 List<AccessEvent> requests = getRequests();
1047 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1048 assertFetchRequests(requests, 0);
1049 assertThrows(IllegalStateException.class,
1050 () -> ((TransportHttp) t).setPreemptiveBasicAuthentication(
1051 AppServer.username, AppServer.password));
1052 assertThrows(IllegalStateException.class, () -> ((TransportHttp) t)
1053 .setPreemptiveBasicAuthentication(null, null));
1054 }
1055 }
1056
1057 @Test
1058 public void testInitialClone_WithWrongPreAuthenticationAndCredentialProvider()
1059 throws Exception {
1060 try (Repository dst = createBareRepository();
1061 Transport t = Transport.open(dst, authURI)) {
1062 assertFalse(dst.getObjectDatabase().has(A_txt));
1063 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1064 AppServer.username, AppServer.password + 'x');
1065 t.setCredentialsProvider(testCredentials);
1066 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1067 assertTrue(dst.getObjectDatabase().has(A_txt));
1068 assertEquals(B, dst.exactRef(master).getObjectId());
1069 fsck(dst, B);
1070 }
1071
1072 List<AccessEvent> requests = getRequests();
1073 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1074
1075 AccessEvent info = requests.get(0);
1076 assertEquals("GET", info.getMethod());
1077 assertEquals(401, info.getStatus());
1078
1079 assertFetchRequests(requests, 1);
1080 }
1081
1082 @Test
1083 public void testInitialClone_WithWrongPreAuthentication() throws Exception {
1084 try (Repository dst = createBareRepository();
1085 Transport t = Transport.open(dst, authURI)) {
1086 assertFalse(dst.getObjectDatabase().has(A_txt));
1087 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1088 AppServer.username, AppServer.password + 'x');
1089 TransportException e = assertThrows(TransportException.class,
1090 () -> t.fetch(NullProgressMonitor.INSTANCE,
1091 mirror(master)));
1092 String msg = e.getMessage();
1093 assertTrue("Unexpected exception message: " + msg,
1094 msg.contains("no CredentialsProvider"));
1095 }
1096 List<AccessEvent> requests = getRequests();
1097 assertEquals(1, requests.size());
1098
1099 AccessEvent info = requests.get(0);
1100 assertEquals("GET", info.getMethod());
1101 assertEquals(401, info.getStatus());
1102 }
1103
1104 @Test
1105 public void testInitialClone_WithUserInfo() throws Exception {
1106 URIish withUserInfo = authURI.setUser(AppServer.username)
1107 .setPass(AppServer.password);
1108 try (Repository dst = createBareRepository();
1109 Transport t = Transport.open(dst, withUserInfo)) {
1110 assertFalse(dst.getObjectDatabase().has(A_txt));
1111 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1112 assertTrue(dst.getObjectDatabase().has(A_txt));
1113 assertEquals(B, dst.exactRef(master).getObjectId());
1114 fsck(dst, B);
1115 }
1116
1117 List<AccessEvent> requests = getRequests();
1118 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1119
1120 assertFetchRequests(requests, 0);
1121 }
1122
1123 @Test
1124 public void testInitialClone_PreAuthOverridesUserInfo() throws Exception {
1125 URIish withUserInfo = authURI.setUser(AppServer.username)
1126 .setPass(AppServer.password + 'x');
1127 try (Repository dst = createBareRepository();
1128 Transport t = Transport.open(dst, withUserInfo)) {
1129 assertFalse(dst.getObjectDatabase().has(A_txt));
1130 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1131 AppServer.username, AppServer.password);
1132 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1133 assertTrue(dst.getObjectDatabase().has(A_txt));
1134 assertEquals(B, dst.exactRef(master).getObjectId());
1135 fsck(dst, B);
1136 }
1137
1138 List<AccessEvent> requests = getRequests();
1139 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1140
1141 assertFetchRequests(requests, 0);
1142 }
1143
1144 @Test
1145 public void testInitialClone_WithAuthenticationNoCredentials()
1146 throws Exception {
1147 try (Repository dst = createBareRepository();
1148 Transport t = Transport.open(dst, authURI)) {
1149 assertFalse(dst.getObjectDatabase().has(A_txt));
1150 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1151 fail("Should not have succeeded -- no authentication");
1152 } catch (TransportException e) {
1153 String msg = e.getMessage();
1154 assertTrue("Unexpected exception message: " + msg,
1155 msg.contains("no CredentialsProvider"));
1156 }
1157 List<AccessEvent> requests = getRequests();
1158 assertEquals(1, requests.size());
1159
1160 AccessEvent info = requests.get(0);
1161 assertEquals("GET", info.getMethod());
1162 assertEquals(401, info.getStatus());
1163 }
1164
1165 @Test
1166 public void testInitialClone_WithAuthenticationWrongCredentials()
1167 throws Exception {
1168 try (Repository dst = createBareRepository();
1169 Transport t = Transport.open(dst, authURI)) {
1170 assertFalse(dst.getObjectDatabase().has(A_txt));
1171 t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
1172 AppServer.username, "wrongpassword"));
1173 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1174 fail("Should not have succeeded -- wrong password");
1175 } catch (TransportException e) {
1176 String msg = e.getMessage();
1177 assertTrue("Unexpected exception message: " + msg,
1178 msg.contains("auth"));
1179 }
1180 List<AccessEvent> requests = getRequests();
1181
1182 assertEquals(4, requests.size());
1183
1184 for (AccessEvent event : requests) {
1185 assertEquals("GET", event.getMethod());
1186 assertEquals(401, event.getStatus());
1187 }
1188 }
1189
1190 @Test
1191 public void testInitialClone_WithAuthenticationAfterRedirect()
1192 throws Exception {
1193 URIish cloneFrom = extendPath(redirectURI, "/target/auth");
1194 CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider(
1195 "unknown", "none") {
1196 @Override
1197 public boolean get(URIish uri, CredentialItem... items)
1198 throws UnsupportedCredentialItem {
1199
1200
1201
1202
1203
1204 if (uri.getPath().startsWith("/auth")) {
1205 return testCredentials.get(uri, items);
1206 }
1207 return super.get(uri, items);
1208 }
1209 };
1210 try (Repository dst = createBareRepository();
1211 Transport t = Transport.open(dst, cloneFrom)) {
1212 assertFalse(dst.getObjectDatabase().has(A_txt));
1213 t.setCredentialsProvider(uriSpecificCredentialsProvider);
1214 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1215 assertTrue(dst.getObjectDatabase().has(A_txt));
1216 assertEquals(B, dst.exactRef(master).getObjectId());
1217 fsck(dst, B);
1218 }
1219
1220 List<AccessEvent> requests = getRequests();
1221 assertEquals(enableProtocolV2 ? 5 : 4, requests.size());
1222
1223 int requestNumber = 0;
1224 AccessEvent redirect = requests.get(requestNumber++);
1225 assertEquals("GET", redirect.getMethod());
1226 assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
1227 assertEquals(301, redirect.getStatus());
1228
1229 AccessEvent info = requests.get(requestNumber++);
1230 assertEquals("GET", info.getMethod());
1231 assertEquals(join(authURI, "info/refs"), info.getPath());
1232 assertEquals(401, info.getStatus());
1233
1234 info = requests.get(requestNumber++);
1235 assertEquals("GET", info.getMethod());
1236 assertEquals(join(authURI, "info/refs"), info.getPath());
1237 assertEquals(1, info.getParameters().size());
1238 assertEquals("git-upload-pack", info.getParameter("service"));
1239 assertEquals(200, info.getStatus());
1240 assertEquals("application/x-git-upload-pack-advertisement",
1241 info.getResponseHeader(HDR_CONTENT_TYPE));
1242 if (!enableProtocolV2) {
1243 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
1244 } else {
1245 AccessEvent lsRefs = requests.get(requestNumber++);
1246 assertEquals("POST", lsRefs.getMethod());
1247 assertEquals(join(authURI, "git-upload-pack"), lsRefs.getPath());
1248 assertEquals(0, lsRefs.getParameters().size());
1249 assertNotNull("has content-length",
1250 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1251 assertNull("not chunked",
1252 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1253 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1254 assertEquals(200, lsRefs.getStatus());
1255 assertEquals("application/x-git-upload-pack-result",
1256 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1257 }
1258
1259 AccessEvent service = requests.get(requestNumber);
1260 assertEquals("POST", service.getMethod());
1261 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
1262 assertEquals(0, service.getParameters().size());
1263 assertNotNull("has content-length",
1264 service.getRequestHeader(HDR_CONTENT_LENGTH));
1265 assertNull("not chunked",
1266 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1267
1268 assertEquals(200, service.getStatus());
1269 assertEquals("application/x-git-upload-pack-result",
1270 service.getResponseHeader(HDR_CONTENT_TYPE));
1271 }
1272
1273 @Test
1274 public void testInitialClone_WithAuthenticationOnPostOnly()
1275 throws Exception {
1276 try (Repository dst = createBareRepository();
1277 Transport t = Transport.open(dst, authOnPostURI)) {
1278 assertFalse(dst.getObjectDatabase().has(A_txt));
1279 t.setCredentialsProvider(testCredentials);
1280 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1281 assertTrue(dst.getObjectDatabase().has(A_txt));
1282 assertEquals(B, dst.exactRef(master).getObjectId());
1283 fsck(dst, B);
1284 }
1285
1286 List<AccessEvent> requests = getRequests();
1287 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1288
1289 AccessEvent info = requests.get(0);
1290 assertEquals("GET", info.getMethod());
1291 assertEquals(join(authOnPostURI, "info/refs"), info.getPath());
1292 assertEquals(1, info.getParameters().size());
1293 assertEquals("git-upload-pack", info.getParameter("service"));
1294 assertEquals(200, info.getStatus());
1295 assertEquals("application/x-git-upload-pack-advertisement",
1296 info.getResponseHeader(HDR_CONTENT_TYPE));
1297 if (!enableProtocolV2) {
1298 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
1299 }
1300
1301 AccessEvent service = requests.get(1);
1302 assertEquals("POST", service.getMethod());
1303 assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
1304 assertEquals(401, service.getStatus());
1305
1306 for (int i = 2; i < requests.size(); i++) {
1307 service = requests.get(i);
1308 assertEquals("POST", service.getMethod());
1309 assertEquals(join(authOnPostURI, "git-upload-pack"),
1310 service.getPath());
1311 assertEquals(0, service.getParameters().size());
1312 assertNotNull("has content-length",
1313 service.getRequestHeader(HDR_CONTENT_LENGTH));
1314 assertNull("not chunked",
1315 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1316
1317 assertEquals(200, service.getStatus());
1318 assertEquals("application/x-git-upload-pack-result",
1319 service.getResponseHeader(HDR_CONTENT_TYPE));
1320 }
1321 }
1322
1323 @Test
1324 public void testFetch_FewLocalCommits() throws Exception {
1325
1326
1327 TestRepository dst = createTestRepository();
1328 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1329 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1330 }
1331 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1332 List<AccessEvent> cloneRequests = getRequests();
1333
1334
1335 TestRepository.BranchBuilder b = dst.branch(master);
1336 for (int i = 0; i < 4; i++)
1337 b.commit().tick(3600 ).message("c" + i).create();
1338
1339
1340
1341 RevCommit Z;
1342 try (TestRepository<Repository> tr = new TestRepository<>(
1343 remoteRepository)) {
1344 b = tr.branch(master);
1345 Z = b.commit().message("Z").create();
1346 }
1347
1348
1349
1350 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1351 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1352 }
1353 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1354
1355 List<AccessEvent> requests = getRequests();
1356 requests.removeAll(cloneRequests);
1357
1358 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1359
1360 int requestNumber = 0;
1361 AccessEvent info = requests.get(requestNumber++);
1362 assertEquals("GET", info.getMethod());
1363 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1364 assertEquals(1, info.getParameters().size());
1365 assertEquals("git-upload-pack", info.getParameter("service"));
1366 assertEquals(200, info.getStatus());
1367 assertEquals("application/x-git-upload-pack-advertisement",
1368 info.getResponseHeader(HDR_CONTENT_TYPE));
1369
1370 if (enableProtocolV2) {
1371 AccessEvent lsRefs = requests.get(requestNumber++);
1372 assertEquals("POST", lsRefs.getMethod());
1373 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
1374 assertEquals(0, lsRefs.getParameters().size());
1375 assertNotNull("has content-length",
1376 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1377 assertNull("not chunked",
1378 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1379 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1380 assertEquals(200, lsRefs.getStatus());
1381 assertEquals("application/x-git-upload-pack-result",
1382 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1383 }
1384
1385
1386
1387 AccessEvent service = requests.get(requestNumber);
1388 assertEquals("POST", service.getMethod());
1389 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1390 assertEquals(0, service.getParameters().size());
1391 assertNotNull("has content-length",
1392 service.getRequestHeader(HDR_CONTENT_LENGTH));
1393 assertNull("not chunked",
1394 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1395
1396 assertEquals(200, service.getStatus());
1397 assertEquals("application/x-git-upload-pack-result",
1398 service.getResponseHeader(HDR_CONTENT_TYPE));
1399 }
1400
1401 @Test
1402 public void testFetch_TooManyLocalCommits() throws Exception {
1403
1404
1405 TestRepository dst = createTestRepository();
1406 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1407 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1408 }
1409 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1410 List<AccessEvent> cloneRequests = getRequests();
1411
1412
1413
1414
1415
1416 TestRepository.BranchBuilder b = dst.branch(master);
1417 for (int i = 0; i < 32 - 1; i++)
1418 b.commit().tick(3600 ).message("c" + i).create();
1419
1420
1421
1422 RevCommit Z;
1423 try (TestRepository<Repository> tr = new TestRepository<>(
1424 remoteRepository)) {
1425 b = tr.branch(master);
1426 Z = b.commit().message("Z").create();
1427 }
1428
1429
1430
1431 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1432 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1433 }
1434 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1435
1436 List<AccessEvent> requests = getRequests();
1437 requests.removeAll(cloneRequests);
1438 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1439
1440 int requestNumber = 0;
1441 AccessEvent info = requests.get(requestNumber++);
1442 assertEquals("GET", info.getMethod());
1443 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1444 assertEquals(1, info.getParameters().size());
1445 assertEquals("git-upload-pack", info.getParameter("service"));
1446 assertEquals(200, info.getStatus());
1447 assertEquals("application/x-git-upload-pack-advertisement", info
1448 .getResponseHeader(HDR_CONTENT_TYPE));
1449
1450 if (enableProtocolV2) {
1451 AccessEvent lsRefs = requests.get(requestNumber++);
1452 assertEquals("POST", lsRefs.getMethod());
1453 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
1454 assertEquals(0, lsRefs.getParameters().size());
1455 assertNotNull("has content-length",
1456 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1457 assertNull("not chunked",
1458 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1459 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1460 assertEquals(200, lsRefs.getStatus());
1461 assertEquals("application/x-git-upload-pack-result",
1462 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1463 }
1464
1465
1466
1467
1468 AccessEvent service = requests.get(requestNumber++);
1469 assertEquals("POST", service.getMethod());
1470 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1471 assertEquals(0, service.getParameters().size());
1472 assertNotNull("has content-length", service
1473 .getRequestHeader(HDR_CONTENT_LENGTH));
1474 assertNull("not chunked", service
1475 .getRequestHeader(HDR_TRANSFER_ENCODING));
1476
1477 assertEquals(200, service.getStatus());
1478 assertEquals("application/x-git-upload-pack-result", service
1479 .getResponseHeader(HDR_CONTENT_TYPE));
1480
1481 service = requests.get(requestNumber);
1482 assertEquals("POST", service.getMethod());
1483 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1484 assertEquals(0, service.getParameters().size());
1485 assertNotNull("has content-length", service
1486 .getRequestHeader(HDR_CONTENT_LENGTH));
1487 assertNull("not chunked", service
1488 .getRequestHeader(HDR_TRANSFER_ENCODING));
1489
1490 assertEquals(200, service.getStatus());
1491 assertEquals("application/x-git-upload-pack-result", service
1492 .getResponseHeader(HDR_CONTENT_TYPE));
1493 }
1494
1495 @Test
1496 public void testFetch_MaxHavesCutoffAfterAckOnly() throws Exception {
1497
1498
1499 TestRepository dst = createTestRepository();
1500 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1501 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1502 }
1503 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517 TestRepository.BranchBuilder b = dst.branch(master);
1518
1519
1520
1521 for (int i = 0; i < 992; i++)
1522 b.commit().tick(3600 ).message("c" + i).create();
1523
1524
1525
1526 RevCommit Z;
1527 try (TestRepository<Repository> tr = new TestRepository<>(
1528 remoteRepository)) {
1529 b = tr.branch(master);
1530 Z = b.commit().message("Z").create();
1531 }
1532
1533
1534
1535 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
1536 Writer writer = new OutputStreamWriter(buffer, StandardCharsets.UTF_8);
1537 TextProgressMonitor monitor = new TextProgressMonitor(writer);
1538 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1539 t.fetch(monitor, mirror(master));
1540 }
1541 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1542
1543 String progressMessages = new String(buffer.toByteArray(),
1544 StandardCharsets.UTF_8);
1545 Pattern expected = Pattern
1546 .compile("Receiving objects:\\s+100% \\(1/1\\)\n");
1547 if (!expected.matcher(progressMessages).find()) {
1548 System.out.println(progressMessages);
1549 fail("Expected only one object to be sent");
1550 }
1551 }
1552
1553 @Test
1554 public void testInitialClone_BrokenServer() throws Exception {
1555 try (Repository dst = createBareRepository();
1556 Transport t = Transport.open(dst, brokenURI)) {
1557 assertFalse(dst.getObjectDatabase().has(A_txt));
1558 try {
1559 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1560 fail("fetch completed despite upload-pack being broken");
1561 } catch (TransportException err) {
1562 String exp = brokenURI + ": expected"
1563 + " Content-Type application/x-git-upload-pack-result;"
1564 + " received Content-Type text/plain;charset=utf-8";
1565 assertEquals(exp, err.getMessage());
1566 }
1567 }
1568
1569 List<AccessEvent> requests = getRequests();
1570 assertEquals(2, requests.size());
1571
1572 AccessEvent info = requests.get(0);
1573 assertEquals("GET", info.getMethod());
1574 assertEquals(join(brokenURI, "info/refs"), info.getPath());
1575 assertEquals(1, info.getParameters().size());
1576 assertEquals("git-upload-pack", info.getParameter("service"));
1577 assertEquals(200, info.getStatus());
1578 assertEquals("application/x-git-upload-pack-advertisement", info
1579 .getResponseHeader(HDR_CONTENT_TYPE));
1580
1581 AccessEvent service = requests.get(1);
1582 assertEquals("POST", service.getMethod());
1583 assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
1584 assertEquals(0, service.getParameters().size());
1585 assertEquals(200, service.getStatus());
1586 assertEquals("text/plain;charset=utf-8",
1587 service.getResponseHeader(HDR_CONTENT_TYPE));
1588 }
1589
1590 @Test
1591 public void testInvalidWant() throws Exception {
1592 ObjectId id;
1593 try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
1594 id = formatter.idFor(Constants.OBJ_BLOB,
1595 "testInvalidWant".getBytes(UTF_8));
1596 }
1597
1598 try (Repository dst = createBareRepository();
1599 Transport t = Transport.open(dst, remoteURI);
1600 FetchConnection c = t.openFetch()) {
1601 Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
1602 id);
1603 c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
1604 Collections.<ObjectId> emptySet());
1605 fail("Server accepted want " + id.name());
1606 } catch (TransportException err) {
1607 assertTrue(err.getMessage()
1608 .contains("want " + id.name() + " not valid"));
1609 }
1610 }
1611
1612 @Test
1613 public void testFetch_RefsUnreadableOnUpload() throws Exception {
1614 AppServer noRefServer = new AppServer();
1615 try {
1616 final String repoName = "refs-unreadable";
1617 RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
1618 new DfsRepositoryDescription(repoName));
1619 final TestRepository<Repository> repo = new TestRepository<>(
1620 badRefsRepo);
1621 badRefsRepo.getConfig().setInt("protocol", null, "version",
1622 enableProtocolV2 ? 2 : 0);
1623
1624 ServletContextHandler app = noRefServer.addContext("/git");
1625 GitServlet gs = new GitServlet();
1626 gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName));
1627 app.addServlet(new ServletHolder(gs), "/*");
1628 noRefServer.setUp();
1629
1630 RevBlob A2_txt = repo.blob("A2");
1631 RevCommit A2 = repo.commit().add("A2_txt", A2_txt).create();
1632 RevCommit B2 = repo.commit().parent(A2).add("A2_txt", "C2")
1633 .add("B2", "B2").create();
1634 repo.update(master, B2);
1635
1636 URIish badRefsURI = new URIish(noRefServer.getURI()
1637 .resolve(app.getContextPath() + "/" + repoName).toString());
1638
1639 try (Repository dst = createBareRepository();
1640 Transport t = Transport.open(dst, badRefsURI);
1641 FetchConnection c = t.openFetch()) {
1642
1643
1644 badRefsRepo.startFailing();
1645
1646 badRefsRepo.getRefDatabase().refresh();
1647 c.fetch(NullProgressMonitor.INSTANCE,
1648 Collections.singleton(c.getRef(master)),
1649 Collections.<ObjectId> emptySet());
1650 fail("Successfully served ref with value " + c.getRef(master));
1651 } catch (TransportException err) {
1652 assertTrue("Unexpected exception message " + err.getMessage(),
1653 err.getMessage().contains("Internal server error"));
1654 }
1655 } finally {
1656 noRefServer.tearDown();
1657 }
1658 }
1659
1660 @Test
1661 public void testPush_NotAuthorized() throws Exception {
1662 final TestRepository src = createTestRepository();
1663 final RevBlob Q_txt = src.blob("new text");
1664 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1665 final Repository db = src.getRepository();
1666 final String dstName = Constants.R_HEADS + "new.branch";
1667
1668
1669
1670 try (Transport t = Transport.open(db, remoteURI)) {
1671 final String srcExpr = Q.name();
1672 final boolean forceUpdate = false;
1673 final String localName = null;
1674 final ObjectId oldId = null;
1675
1676 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1677 srcExpr, dstName, forceUpdate, localName, oldId);
1678 try {
1679 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1680 fail("anonymous push incorrectly accepted without error");
1681 } catch (TransportException e) {
1682 final String exp = remoteURI + ": "
1683 + JGitText.get().authenticationNotSupported;
1684 assertEquals(exp, e.getMessage());
1685 }
1686 }
1687
1688 List<AccessEvent> requests = getRequests();
1689 assertEquals(1, requests.size());
1690
1691 AccessEvent info = requests.get(0);
1692 assertEquals("GET", info.getMethod());
1693 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1694 assertEquals(1, info.getParameters().size());
1695 assertEquals("git-receive-pack", info.getParameter("service"));
1696 assertEquals(401, info.getStatus());
1697 }
1698
1699 @Test
1700 public void testPush_CreateBranch() throws Exception {
1701 final TestRepository src = createTestRepository();
1702 final RevBlob Q_txt = src.blob("new text");
1703 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1704 final Repository db = src.getRepository();
1705 final String dstName = Constants.R_HEADS + "new.branch";
1706
1707 enableReceivePack();
1708
1709 try (Transport t = Transport.open(db, remoteURI)) {
1710 final String srcExpr = Q.name();
1711 final boolean forceUpdate = false;
1712 final String localName = null;
1713 final ObjectId oldId = null;
1714
1715 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1716 srcExpr, dstName, forceUpdate, localName, oldId);
1717 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1718 }
1719
1720 assertTrue(remoteRepository.getObjectDatabase().has(Q_txt));
1721 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1722 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1723 fsck(remoteRepository, Q);
1724
1725 final ReflogReader log = remoteRepository.getReflogReader(dstName);
1726 assertNotNull("has log for " + dstName, log);
1727
1728 final ReflogEntry last = log.getLastEntry();
1729 assertNotNull("has last entry", last);
1730 assertEquals(ObjectId.zeroId(), last.getOldId());
1731 assertEquals(Q, last.getNewId());
1732 assertEquals("anonymous", last.getWho().getName());
1733
1734
1735
1736
1737
1738 final String clientHost = remoteURI.getHost();
1739 assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
1740 assertEquals("push: created", last.getComment());
1741
1742 List<AccessEvent> requests = getRequests();
1743 assertEquals(2, requests.size());
1744
1745 AccessEvent info = requests.get(0);
1746 assertEquals("GET", info.getMethod());
1747 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1748 assertEquals(1, info.getParameters().size());
1749 assertEquals("git-receive-pack", info.getParameter("service"));
1750 assertEquals(200, info.getStatus());
1751 assertEquals("application/x-git-receive-pack-advertisement", info
1752 .getResponseHeader(HDR_CONTENT_TYPE));
1753
1754 AccessEvent service = requests.get(1);
1755 assertEquals("POST", service.getMethod());
1756 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1757 assertEquals(0, service.getParameters().size());
1758 assertNotNull("has content-length", service
1759 .getRequestHeader(HDR_CONTENT_LENGTH));
1760 assertNull("not chunked", service
1761 .getRequestHeader(HDR_TRANSFER_ENCODING));
1762
1763 assertEquals(200, service.getStatus());
1764 assertEquals("application/x-git-receive-pack-result", service
1765 .getResponseHeader(HDR_CONTENT_TYPE));
1766 }
1767
1768 @Test
1769 public void testPush_ChunkedEncoding() throws Exception {
1770 final TestRepository<Repository> src = createTestRepository();
1771 final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
1772 final RevCommit Q = src.commit().add("Q", Q_bin).create();
1773 final Repository db = src.getRepository();
1774 final String dstName = Constants.R_HEADS + "new.branch";
1775
1776 enableReceivePack();
1777
1778 final StoredConfig cfg = db.getConfig();
1779 cfg.setInt("core", null, "compression", 0);
1780 cfg.setInt("http", null, "postbuffer", 8 * 1024);
1781 cfg.save();
1782
1783 try (Transport t = Transport.open(db, remoteURI)) {
1784 final String srcExpr = Q.name();
1785 final boolean forceUpdate = false;
1786 final String localName = null;
1787 final ObjectId oldId = null;
1788
1789 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1790 srcExpr, dstName, forceUpdate, localName, oldId);
1791 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1792 }
1793
1794 assertTrue(remoteRepository.getObjectDatabase().has(Q_bin));
1795 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1796 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1797 fsck(remoteRepository, Q);
1798
1799 List<AccessEvent> requests = getRequests();
1800 assertEquals(2, requests.size());
1801
1802 AccessEvent info = requests.get(0);
1803 assertEquals("GET", info.getMethod());
1804 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1805 assertEquals(1, info.getParameters().size());
1806 assertEquals("git-receive-pack", info.getParameter("service"));
1807 assertEquals(200, info.getStatus());
1808 assertEquals("application/x-git-receive-pack-advertisement", info
1809 .getResponseHeader(HDR_CONTENT_TYPE));
1810
1811 AccessEvent service = requests.get(1);
1812 assertEquals("POST", service.getMethod());
1813 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1814 assertEquals(0, service.getParameters().size());
1815 assertNull("no content-length", service
1816 .getRequestHeader(HDR_CONTENT_LENGTH));
1817 assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
1818
1819 assertEquals(200, service.getStatus());
1820 assertEquals("application/x-git-receive-pack-result", service
1821 .getResponseHeader(HDR_CONTENT_TYPE));
1822 }
1823
1824 private void enableReceivePack() throws IOException {
1825 final StoredConfig cfg = remoteRepository.getConfig();
1826 cfg.setBoolean("http", null, "receivepack", true);
1827 cfg.save();
1828 }
1829 }