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.NoRemoteRepositoryException;
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 (NoRemoteRepositoryException notFound) {
500 assertEquals(uri + ": " + uri
501 + "/info/refs?service=git-upload-pack not found: Not Found",
502 notFound.getMessage());
503 }
504 }
505
506 List<AccessEvent> requests = getRequests();
507 assertEquals(1, requests.size());
508
509 AccessEvent info = requests.get(0);
510 assertEquals("GET", info.getMethod());
511 assertEquals(join(uri, "info/refs"), info.getPath());
512 assertEquals(1, info.getParameters().size());
513 assertEquals("git-upload-pack", info.getParameter("service"));
514 assertEquals(404, info.getStatus());
515 assertEquals("application/x-git-upload-pack-advertisement",
516 info.getResponseHeader(HDR_CONTENT_TYPE));
517 }
518
519 @Test
520 public void testFetchBySHA1() throws Exception {
521 try (Repository dst = createBareRepository();
522 Transport t = Transport.open(dst, remoteURI)) {
523 assertFalse(dst.getObjectDatabase().has(A_txt));
524 t.fetch(NullProgressMonitor.INSTANCE,
525 Collections.singletonList(new RefSpec(B.name())));
526 assertTrue(dst.getObjectDatabase().has(A_txt));
527 }
528 }
529
530 @Test
531 public void testFetchBySHA1Unreachable() throws Exception {
532 try (Repository dst = createBareRepository();
533 Transport t = Transport.open(dst, remoteURI)) {
534 assertFalse(dst.getObjectDatabase().has(A_txt));
535 Exception e = assertThrows(TransportException.class,
536 () -> t.fetch(NullProgressMonitor.INSTANCE,
537 Collections.singletonList(
538 new RefSpec(unreachableCommit.name()))));
539 assertTrue(e.getMessage().contains(
540 "want " + unreachableCommit.name() + " not valid"));
541 }
542 assertLastRequestStatusCode(200);
543 }
544
545 @Test
546 public void testFetchBySHA1UnreachableByAdvertiseRefsHook()
547 throws Exception {
548 advertiseRefsHook = new AbstractAdvertiseRefsHook() {
549 @Override
550 protected Map<String, Ref> getAdvertisedRefs(Repository repository,
551 RevWalk revWalk) {
552 return Collections.emptyMap();
553 }
554 };
555
556 try (Repository dst = createBareRepository();
557 Transport t = Transport.open(dst, remoteURI)) {
558 assertFalse(dst.getObjectDatabase().has(A_txt));
559 Exception e = assertThrows(TransportException.class,
560 () -> t.fetch(NullProgressMonitor.INSTANCE,
561 Collections.singletonList(new RefSpec(A.name()))));
562 assertTrue(
563 e.getMessage().contains("want " + A.name() + " not valid"));
564 }
565 assertLastRequestStatusCode(200);
566 }
567
568 @Test
569 public void testTimeoutExpired() throws Exception {
570 try (Repository dst = createBareRepository();
571 Transport t = Transport.open(dst, slowURI)) {
572 t.setTimeout(1);
573 TransportException expected = assertThrows(TransportException.class,
574 () -> t.fetch(NullProgressMonitor.INSTANCE,
575 mirror(master)));
576 assertTrue("Unexpected exception message: " + expected.toString(),
577 expected.getMessage().contains("time"));
578 }
579 }
580
581 @Test
582 public void testTimeoutExpiredWithAuth() throws Exception {
583 try (Repository dst = createBareRepository();
584 Transport t = Transport.open(dst, slowAuthURI)) {
585 t.setTimeout(1);
586 t.setCredentialsProvider(testCredentials);
587 TransportException expected = assertThrows(TransportException.class,
588 () -> t.fetch(NullProgressMonitor.INSTANCE,
589 mirror(master)));
590 assertTrue("Unexpected exception message: " + expected.toString(),
591 expected.getMessage().contains("time"));
592 assertFalse("Unexpected exception message: " + expected.toString(),
593 expected.getMessage().contains("auth"));
594 }
595 }
596
597 @Test
598 public void testInitialClone_Small() throws Exception {
599 try (Repository dst = createBareRepository();
600 Transport t = Transport.open(dst, remoteURI)) {
601 assertFalse(dst.getObjectDatabase().has(A_txt));
602 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
603 assertTrue(dst.getObjectDatabase().has(A_txt));
604 assertEquals(B, dst.exactRef(master).getObjectId());
605 fsck(dst, B);
606 }
607
608 List<AccessEvent> requests = getRequests();
609 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
610
611 int requestNumber = 0;
612 AccessEvent info = requests.get(requestNumber++);
613 assertEquals("GET", info.getMethod());
614 assertEquals(join(remoteURI, "info/refs"), info.getPath());
615 assertEquals(1, info.getParameters().size());
616 assertEquals("git-upload-pack", info.getParameter("service"));
617 assertEquals(200, info.getStatus());
618 assertEquals("application/x-git-upload-pack-advertisement", info
619 .getResponseHeader(HDR_CONTENT_TYPE));
620 if (!enableProtocolV2) {
621 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
622 } else {
623 AccessEvent lsRefs = requests.get(requestNumber++);
624 assertEquals("POST", lsRefs.getMethod());
625 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
626 assertEquals(0, lsRefs.getParameters().size());
627 assertNotNull("has content-length",
628 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
629 assertNull("not chunked",
630 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
631 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
632 assertEquals(200, lsRefs.getStatus());
633 assertEquals("application/x-git-upload-pack-result",
634 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
635 }
636
637 AccessEvent service = requests.get(requestNumber);
638 assertEquals("POST", service.getMethod());
639 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
640 assertEquals(0, service.getParameters().size());
641 assertNotNull("has content-length", service
642 .getRequestHeader(HDR_CONTENT_LENGTH));
643 assertNull("not chunked", service
644 .getRequestHeader(HDR_TRANSFER_ENCODING));
645
646 assertEquals(200, service.getStatus());
647 assertEquals("application/x-git-upload-pack-result", service
648 .getResponseHeader(HDR_CONTENT_TYPE));
649 }
650
651 @Test
652 public void test_CloneWithCustomFactory() throws Exception {
653 HttpConnectionFactory globalFactory = HttpTransport
654 .getConnectionFactory();
655 HttpConnectionFactory failingConnectionFactory = new HttpConnectionFactory() {
656
657 @Override
658 public HttpConnection create(URL url) throws IOException {
659 throw new IOException("Should not be reached");
660 }
661
662 @Override
663 public HttpConnection create(URL url, Proxy proxy)
664 throws IOException {
665 throw new IOException("Should not be reached");
666 }
667 };
668 HttpTransport.setConnectionFactory(failingConnectionFactory);
669 try {
670 File tmp = createTempDirectory("cloneViaApi");
671 boolean[] localFactoryUsed = { false };
672 TransportConfigCallback callback = new TransportConfigCallback() {
673
674 @Override
675 public void configure(Transport transport) {
676 if (transport instanceof TransportHttp) {
677 ((TransportHttp) transport).setHttpConnectionFactory(
678 new HttpConnectionFactory() {
679
680 @Override
681 public HttpConnection create(URL url)
682 throws IOException {
683 localFactoryUsed[0] = true;
684 return globalFactory.create(url);
685 }
686
687 @Override
688 public HttpConnection create(URL url,
689 Proxy proxy) throws IOException {
690 localFactoryUsed[0] = true;
691 return globalFactory.create(url, proxy);
692 }
693 });
694 }
695 }
696 };
697 try (Git git = Git.cloneRepository().setDirectory(tmp)
698 .setTransportConfigCallback(callback)
699 .setURI(remoteURI.toPrivateString()).call()) {
700 assertTrue("Should have used the local HttpConnectionFactory",
701 localFactoryUsed[0]);
702 }
703 } finally {
704 HttpTransport.setConnectionFactory(globalFactory);
705 }
706 }
707
708 private void initialClone_Redirect(int nofRedirects, int code)
709 throws Exception {
710 initialClone_Redirect(nofRedirects, code, false);
711 }
712
713 private void initialClone_Redirect(int nofRedirects, int code,
714 boolean localRedirect) throws Exception {
715 URIish cloneFrom = redirectURI;
716 if (localRedirect) {
717 cloneFrom = extendPath(cloneFrom, "/local");
718 }
719 if (code != 301 || nofRedirects > 1) {
720 cloneFrom = extendPath(cloneFrom,
721 "/response/" + nofRedirects + "/" + code);
722 }
723
724 try (Repository dst = createBareRepository();
725 Transport t = Transport.open(dst, cloneFrom)) {
726 assertFalse(dst.getObjectDatabase().has(A_txt));
727 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
728 assertTrue(dst.getObjectDatabase().has(A_txt));
729 assertEquals(B, dst.exactRef(master).getObjectId());
730 fsck(dst, B);
731 }
732
733 List<AccessEvent> requests = getRequests();
734 assertEquals((enableProtocolV2 ? 3 : 2) + nofRedirects,
735 requests.size());
736
737 int n = 0;
738 while (n < nofRedirects) {
739 AccessEvent redirect = requests.get(n++);
740 assertEquals(code, redirect.getStatus());
741 }
742
743 AccessEvent info = requests.get(n++);
744 assertEquals("GET", info.getMethod());
745 assertEquals(join(remoteURI, "info/refs"), info.getPath());
746 assertEquals(1, info.getParameters().size());
747 assertEquals("git-upload-pack", info.getParameter("service"));
748 assertEquals(200, info.getStatus());
749 assertEquals("application/x-git-upload-pack-advertisement",
750 info.getResponseHeader(HDR_CONTENT_TYPE));
751 if (!enableProtocolV2) {
752 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
753 } else {
754 AccessEvent lsRefs = requests.get(n++);
755 assertEquals("POST", lsRefs.getMethod());
756 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
757 assertEquals(0, lsRefs.getParameters().size());
758 assertNotNull("has content-length",
759 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
760 assertNull("not chunked",
761 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
762 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
763 assertEquals(200, lsRefs.getStatus());
764 assertEquals("application/x-git-upload-pack-result",
765 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
766 }
767
768 AccessEvent service = requests.get(n++);
769 assertEquals("POST", service.getMethod());
770 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
771 assertEquals(0, service.getParameters().size());
772 assertNotNull("has content-length",
773 service.getRequestHeader(HDR_CONTENT_LENGTH));
774 assertNull("not chunked",
775 service.getRequestHeader(HDR_TRANSFER_ENCODING));
776
777 assertEquals(200, service.getStatus());
778 assertEquals("application/x-git-upload-pack-result",
779 service.getResponseHeader(HDR_CONTENT_TYPE));
780 }
781
782 @Test
783 public void testInitialClone_Redirect301Small() throws Exception {
784 initialClone_Redirect(1, 301);
785 }
786
787 @Test
788 public void testInitialClone_Redirect301Local() throws Exception {
789 initialClone_Redirect(1, 301, true);
790 }
791
792 @Test
793 public void testInitialClone_Redirect302Small() throws Exception {
794 initialClone_Redirect(1, 302);
795 }
796
797 @Test
798 public void testInitialClone_Redirect303Small() throws Exception {
799 initialClone_Redirect(1, 303);
800 }
801
802 @Test
803 public void testInitialClone_Redirect307Small() throws Exception {
804 initialClone_Redirect(1, 307);
805 }
806
807 @Test
808 public void testInitialClone_RedirectMultiple() throws Exception {
809 initialClone_Redirect(4, 302);
810 }
811
812 @Test
813 public void testInitialClone_RedirectMax() throws Exception {
814 StoredConfig userConfig = SystemReader.getInstance()
815 .getUserConfig();
816 userConfig.setInt("http", null, "maxRedirects", 4);
817 userConfig.save();
818 initialClone_Redirect(4, 302);
819 }
820
821 @Test
822 public void testInitialClone_RedirectTooOften() throws Exception {
823 StoredConfig userConfig = SystemReader.getInstance()
824 .getUserConfig();
825 userConfig.setInt("http", null, "maxRedirects", 3);
826 userConfig.save();
827
828 URIish cloneFrom = extendPath(redirectURI, "/response/4/302");
829 String remoteUri = cloneFrom.toString();
830 try (Repository dst = createBareRepository();
831 Transport t = Transport.open(dst, cloneFrom)) {
832 assertFalse(dst.getObjectDatabase().has(A_txt));
833 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
834 fail("Should have failed (too many redirects)");
835 } catch (TransportException e) {
836 String expectedMessageBegin = remoteUri.toString() + ": "
837 + MessageFormat.format(JGitText.get().redirectLimitExceeded,
838 "3", remoteUri.replace("/4/", "/1/") + '/', "");
839 String message = e.getMessage();
840 if (message.length() > expectedMessageBegin.length()) {
841 message = message.substring(0, expectedMessageBegin.length());
842 }
843 assertEquals(expectedMessageBegin, message);
844 }
845 }
846
847 @Test
848 public void testInitialClone_RedirectLoop() throws Exception {
849 URIish cloneFrom = extendPath(redirectURI, "/loop");
850 try (Repository dst = createBareRepository();
851 Transport t = Transport.open(dst, cloneFrom)) {
852 assertFalse(dst.getObjectDatabase().has(A_txt));
853 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
854 fail("Should have failed (redirect loop)");
855 } catch (TransportException e) {
856 assertTrue(e.getMessage().contains("Redirected more than"));
857 }
858 }
859
860 @Test
861 public void testInitialClone_RedirectOnPostAllowed() throws Exception {
862 StoredConfig userConfig = SystemReader.getInstance()
863 .getUserConfig();
864 userConfig.setString("http", null, "followRedirects", "true");
865 userConfig.save();
866
867 URIish cloneFrom = extendPath(remoteURI, "/post");
868 try (Repository dst = createBareRepository();
869 Transport t = Transport.open(dst, cloneFrom)) {
870 assertFalse(dst.getObjectDatabase().has(A_txt));
871 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
872 assertTrue(dst.getObjectDatabase().has(A_txt));
873 assertEquals(B, dst.exactRef(master).getObjectId());
874 fsck(dst, B);
875 }
876
877 List<AccessEvent> requests = getRequests();
878 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
879
880 AccessEvent info = requests.get(0);
881 assertEquals("GET", info.getMethod());
882 assertEquals(join(cloneFrom, "info/refs"), info.getPath());
883 assertEquals(1, info.getParameters().size());
884 assertEquals("git-upload-pack", info.getParameter("service"));
885 assertEquals(200, info.getStatus());
886 assertEquals("application/x-git-upload-pack-advertisement",
887 info.getResponseHeader(HDR_CONTENT_TYPE));
888 if (!enableProtocolV2) {
889 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
890 }
891
892 AccessEvent redirect = requests.get(1);
893 assertEquals("POST", redirect.getMethod());
894 assertEquals(301, redirect.getStatus());
895
896 for (int i = 2; i < requests.size(); i++) {
897 AccessEvent service = requests.get(i);
898 assertEquals("POST", service.getMethod());
899 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
900 assertEquals(0, service.getParameters().size());
901 assertNotNull("has content-length",
902 service.getRequestHeader(HDR_CONTENT_LENGTH));
903 assertNull("not chunked",
904 service.getRequestHeader(HDR_TRANSFER_ENCODING));
905 assertEquals(200, service.getStatus());
906 assertEquals("application/x-git-upload-pack-result",
907 service.getResponseHeader(HDR_CONTENT_TYPE));
908 }
909 }
910
911 @Test
912 public void testInitialClone_RedirectOnPostForbidden() throws Exception {
913 URIish cloneFrom = extendPath(remoteURI, "/post");
914 try (Repository dst = createBareRepository();
915 Transport t = Transport.open(dst, cloneFrom)) {
916 assertFalse(dst.getObjectDatabase().has(A_txt));
917 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
918 fail("Should have failed (redirect on POST)");
919 } catch (TransportException e) {
920 assertTrue(e.getMessage().contains("301"));
921 }
922 assertLastRequestStatusCode(301);
923 }
924
925 @Test
926 public void testInitialClone_RedirectForbidden() throws Exception {
927 StoredConfig userConfig = SystemReader.getInstance()
928 .getUserConfig();
929 userConfig.setString("http", null, "followRedirects", "false");
930 userConfig.save();
931
932 try (Repository dst = createBareRepository();
933 Transport t = Transport.open(dst, redirectURI)) {
934 assertFalse(dst.getObjectDatabase().has(A_txt));
935 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
936 fail("Should have failed (redirects forbidden)");
937 } catch (TransportException e) {
938 assertTrue(
939 e.getMessage().contains("http.followRedirects is false"));
940 }
941 assertLastRequestStatusCode(301);
942 }
943
944 private void assertFetchRequests(List<AccessEvent> requests, int index) {
945 AccessEvent info = requests.get(index++);
946 assertEquals("GET", info.getMethod());
947 assertEquals(join(authURI, "info/refs"), info.getPath());
948 assertEquals(1, info.getParameters().size());
949 assertEquals("git-upload-pack", info.getParameter("service"));
950 assertEquals(200, info.getStatus());
951 assertEquals("application/x-git-upload-pack-advertisement",
952 info.getResponseHeader(HDR_CONTENT_TYPE));
953 if (!enableProtocolV2) {
954 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
955 }
956
957 for (int i = index; i < requests.size(); i++) {
958 AccessEvent service = requests.get(i);
959 assertEquals("POST", service.getMethod());
960 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
961 assertEquals(0, service.getParameters().size());
962 assertNotNull("has content-length",
963 service.getRequestHeader(HDR_CONTENT_LENGTH));
964 assertNull("not chunked",
965 service.getRequestHeader(HDR_TRANSFER_ENCODING));
966
967 assertEquals(200, service.getStatus());
968 assertEquals("application/x-git-upload-pack-result",
969 service.getResponseHeader(HDR_CONTENT_TYPE));
970 }
971 }
972
973 @Test
974 public void testInitialClone_WithAuthentication() throws Exception {
975 try (Repository dst = createBareRepository();
976 Transport t = Transport.open(dst, authURI)) {
977 assertFalse(dst.getObjectDatabase().has(A_txt));
978 t.setCredentialsProvider(testCredentials);
979 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
980 assertTrue(dst.getObjectDatabase().has(A_txt));
981 assertEquals(B, dst.exactRef(master).getObjectId());
982 fsck(dst, B);
983 }
984
985 List<AccessEvent> requests = getRequests();
986 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
987
988 AccessEvent info = requests.get(0);
989 assertEquals("GET", info.getMethod());
990 assertEquals(401, info.getStatus());
991
992 assertFetchRequests(requests, 1);
993 }
994
995 @Test
996 public void testInitialClone_WithPreAuthentication() throws Exception {
997 try (Repository dst = createBareRepository();
998 Transport t = Transport.open(dst, authURI)) {
999 assertFalse(dst.getObjectDatabase().has(A_txt));
1000 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1001 AppServer.username, AppServer.password);
1002 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1003 assertTrue(dst.getObjectDatabase().has(A_txt));
1004 assertEquals(B, dst.exactRef(master).getObjectId());
1005 fsck(dst, B);
1006 }
1007
1008 List<AccessEvent> requests = getRequests();
1009 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1010
1011 assertFetchRequests(requests, 0);
1012 }
1013
1014 @Test
1015 public void testInitialClone_WithPreAuthenticationCleared()
1016 throws Exception {
1017 try (Repository dst = createBareRepository();
1018 Transport t = Transport.open(dst, authURI)) {
1019 assertFalse(dst.getObjectDatabase().has(A_txt));
1020 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1021 AppServer.username, AppServer.password);
1022 ((TransportHttp) t).setPreemptiveBasicAuthentication(null, null);
1023 t.setCredentialsProvider(testCredentials);
1024 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1025 assertTrue(dst.getObjectDatabase().has(A_txt));
1026 assertEquals(B, dst.exactRef(master).getObjectId());
1027 fsck(dst, B);
1028 }
1029
1030 List<AccessEvent> requests = getRequests();
1031 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1032
1033 AccessEvent info = requests.get(0);
1034 assertEquals("GET", info.getMethod());
1035 assertEquals(401, info.getStatus());
1036
1037 assertFetchRequests(requests, 1);
1038 }
1039
1040 @Test
1041 public void testInitialClone_PreAuthenticationTooLate() throws Exception {
1042 try (Repository dst = createBareRepository();
1043 Transport t = Transport.open(dst, authURI)) {
1044 assertFalse(dst.getObjectDatabase().has(A_txt));
1045 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1046 AppServer.username, AppServer.password);
1047 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1048 assertTrue(dst.getObjectDatabase().has(A_txt));
1049 assertEquals(B, dst.exactRef(master).getObjectId());
1050 fsck(dst, B);
1051 List<AccessEvent> requests = getRequests();
1052 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1053 assertFetchRequests(requests, 0);
1054 assertThrows(IllegalStateException.class,
1055 () -> ((TransportHttp) t).setPreemptiveBasicAuthentication(
1056 AppServer.username, AppServer.password));
1057 assertThrows(IllegalStateException.class, () -> ((TransportHttp) t)
1058 .setPreemptiveBasicAuthentication(null, null));
1059 }
1060 }
1061
1062 @Test
1063 public void testInitialClone_WithWrongPreAuthenticationAndCredentialProvider()
1064 throws Exception {
1065 try (Repository dst = createBareRepository();
1066 Transport t = Transport.open(dst, authURI)) {
1067 assertFalse(dst.getObjectDatabase().has(A_txt));
1068 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1069 AppServer.username, AppServer.password + 'x');
1070 t.setCredentialsProvider(testCredentials);
1071 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1072 assertTrue(dst.getObjectDatabase().has(A_txt));
1073 assertEquals(B, dst.exactRef(master).getObjectId());
1074 fsck(dst, B);
1075 }
1076
1077 List<AccessEvent> requests = getRequests();
1078 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1079
1080 AccessEvent info = requests.get(0);
1081 assertEquals("GET", info.getMethod());
1082 assertEquals(401, info.getStatus());
1083
1084 assertFetchRequests(requests, 1);
1085 }
1086
1087 @Test
1088 public void testInitialClone_WithWrongPreAuthentication() throws Exception {
1089 try (Repository dst = createBareRepository();
1090 Transport t = Transport.open(dst, authURI)) {
1091 assertFalse(dst.getObjectDatabase().has(A_txt));
1092 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1093 AppServer.username, AppServer.password + 'x');
1094 TransportException e = assertThrows(TransportException.class,
1095 () -> t.fetch(NullProgressMonitor.INSTANCE,
1096 mirror(master)));
1097 String msg = e.getMessage();
1098 assertTrue("Unexpected exception message: " + msg,
1099 msg.contains("no CredentialsProvider"));
1100 }
1101 List<AccessEvent> requests = getRequests();
1102 assertEquals(1, requests.size());
1103
1104 AccessEvent info = requests.get(0);
1105 assertEquals("GET", info.getMethod());
1106 assertEquals(401, info.getStatus());
1107 }
1108
1109 @Test
1110 public void testInitialClone_WithUserInfo() throws Exception {
1111 URIish withUserInfo = authURI.setUser(AppServer.username)
1112 .setPass(AppServer.password);
1113 try (Repository dst = createBareRepository();
1114 Transport t = Transport.open(dst, withUserInfo)) {
1115 assertFalse(dst.getObjectDatabase().has(A_txt));
1116 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1117 assertTrue(dst.getObjectDatabase().has(A_txt));
1118 assertEquals(B, dst.exactRef(master).getObjectId());
1119 fsck(dst, B);
1120 }
1121
1122 List<AccessEvent> requests = getRequests();
1123 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1124
1125 assertFetchRequests(requests, 0);
1126 }
1127
1128 @Test
1129 public void testInitialClone_PreAuthOverridesUserInfo() throws Exception {
1130 URIish withUserInfo = authURI.setUser(AppServer.username)
1131 .setPass(AppServer.password + 'x');
1132 try (Repository dst = createBareRepository();
1133 Transport t = Transport.open(dst, withUserInfo)) {
1134 assertFalse(dst.getObjectDatabase().has(A_txt));
1135 ((TransportHttp) t).setPreemptiveBasicAuthentication(
1136 AppServer.username, AppServer.password);
1137 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1138 assertTrue(dst.getObjectDatabase().has(A_txt));
1139 assertEquals(B, dst.exactRef(master).getObjectId());
1140 fsck(dst, B);
1141 }
1142
1143 List<AccessEvent> requests = getRequests();
1144 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1145
1146 assertFetchRequests(requests, 0);
1147 }
1148
1149 @Test
1150 public void testInitialClone_WithAuthenticationNoCredentials()
1151 throws Exception {
1152 try (Repository dst = createBareRepository();
1153 Transport t = Transport.open(dst, authURI)) {
1154 assertFalse(dst.getObjectDatabase().has(A_txt));
1155 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1156 fail("Should not have succeeded -- no authentication");
1157 } catch (TransportException e) {
1158 String msg = e.getMessage();
1159 assertTrue("Unexpected exception message: " + msg,
1160 msg.contains("no CredentialsProvider"));
1161 }
1162 List<AccessEvent> requests = getRequests();
1163 assertEquals(1, requests.size());
1164
1165 AccessEvent info = requests.get(0);
1166 assertEquals("GET", info.getMethod());
1167 assertEquals(401, info.getStatus());
1168 }
1169
1170 @Test
1171 public void testInitialClone_WithAuthenticationWrongCredentials()
1172 throws Exception {
1173 try (Repository dst = createBareRepository();
1174 Transport t = Transport.open(dst, authURI)) {
1175 assertFalse(dst.getObjectDatabase().has(A_txt));
1176 t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
1177 AppServer.username, "wrongpassword"));
1178 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1179 fail("Should not have succeeded -- wrong password");
1180 } catch (TransportException e) {
1181 String msg = e.getMessage();
1182 assertTrue("Unexpected exception message: " + msg,
1183 msg.contains("auth"));
1184 }
1185 List<AccessEvent> requests = getRequests();
1186
1187 assertEquals(4, requests.size());
1188
1189 for (AccessEvent event : requests) {
1190 assertEquals("GET", event.getMethod());
1191 assertEquals(401, event.getStatus());
1192 }
1193 }
1194
1195 @Test
1196 public void testInitialClone_WithAuthenticationAfterRedirect()
1197 throws Exception {
1198 URIish cloneFrom = extendPath(redirectURI, "/target/auth");
1199 CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider(
1200 "unknown", "none") {
1201 @Override
1202 public boolean get(URIish uri, CredentialItem... items)
1203 throws UnsupportedCredentialItem {
1204
1205
1206
1207
1208
1209 if (uri.getPath().startsWith("/auth")) {
1210 return testCredentials.get(uri, items);
1211 }
1212 return super.get(uri, items);
1213 }
1214 };
1215 try (Repository dst = createBareRepository();
1216 Transport t = Transport.open(dst, cloneFrom)) {
1217 assertFalse(dst.getObjectDatabase().has(A_txt));
1218 t.setCredentialsProvider(uriSpecificCredentialsProvider);
1219 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1220 assertTrue(dst.getObjectDatabase().has(A_txt));
1221 assertEquals(B, dst.exactRef(master).getObjectId());
1222 fsck(dst, B);
1223 }
1224
1225 List<AccessEvent> requests = getRequests();
1226 assertEquals(enableProtocolV2 ? 5 : 4, requests.size());
1227
1228 int requestNumber = 0;
1229 AccessEvent redirect = requests.get(requestNumber++);
1230 assertEquals("GET", redirect.getMethod());
1231 assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
1232 assertEquals(301, redirect.getStatus());
1233
1234 AccessEvent info = requests.get(requestNumber++);
1235 assertEquals("GET", info.getMethod());
1236 assertEquals(join(authURI, "info/refs"), info.getPath());
1237 assertEquals(401, info.getStatus());
1238
1239 info = requests.get(requestNumber++);
1240 assertEquals("GET", info.getMethod());
1241 assertEquals(join(authURI, "info/refs"), info.getPath());
1242 assertEquals(1, info.getParameters().size());
1243 assertEquals("git-upload-pack", info.getParameter("service"));
1244 assertEquals(200, info.getStatus());
1245 assertEquals("application/x-git-upload-pack-advertisement",
1246 info.getResponseHeader(HDR_CONTENT_TYPE));
1247 if (!enableProtocolV2) {
1248 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
1249 } else {
1250 AccessEvent lsRefs = requests.get(requestNumber++);
1251 assertEquals("POST", lsRefs.getMethod());
1252 assertEquals(join(authURI, "git-upload-pack"), lsRefs.getPath());
1253 assertEquals(0, lsRefs.getParameters().size());
1254 assertNotNull("has content-length",
1255 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1256 assertNull("not chunked",
1257 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1258 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1259 assertEquals(200, lsRefs.getStatus());
1260 assertEquals("application/x-git-upload-pack-result",
1261 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1262 }
1263
1264 AccessEvent service = requests.get(requestNumber);
1265 assertEquals("POST", service.getMethod());
1266 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
1267 assertEquals(0, service.getParameters().size());
1268 assertNotNull("has content-length",
1269 service.getRequestHeader(HDR_CONTENT_LENGTH));
1270 assertNull("not chunked",
1271 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1272
1273 assertEquals(200, service.getStatus());
1274 assertEquals("application/x-git-upload-pack-result",
1275 service.getResponseHeader(HDR_CONTENT_TYPE));
1276 }
1277
1278 @Test
1279 public void testInitialClone_WithAuthenticationOnPostOnly()
1280 throws Exception {
1281 try (Repository dst = createBareRepository();
1282 Transport t = Transport.open(dst, authOnPostURI)) {
1283 assertFalse(dst.getObjectDatabase().has(A_txt));
1284 t.setCredentialsProvider(testCredentials);
1285 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1286 assertTrue(dst.getObjectDatabase().has(A_txt));
1287 assertEquals(B, dst.exactRef(master).getObjectId());
1288 fsck(dst, B);
1289 }
1290
1291 List<AccessEvent> requests = getRequests();
1292 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1293
1294 AccessEvent info = requests.get(0);
1295 assertEquals("GET", info.getMethod());
1296 assertEquals(join(authOnPostURI, "info/refs"), info.getPath());
1297 assertEquals(1, info.getParameters().size());
1298 assertEquals("git-upload-pack", info.getParameter("service"));
1299 assertEquals(200, info.getStatus());
1300 assertEquals("application/x-git-upload-pack-advertisement",
1301 info.getResponseHeader(HDR_CONTENT_TYPE));
1302 if (!enableProtocolV2) {
1303 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
1304 }
1305
1306 AccessEvent service = requests.get(1);
1307 assertEquals("POST", service.getMethod());
1308 assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
1309 assertEquals(401, service.getStatus());
1310
1311 for (int i = 2; i < requests.size(); i++) {
1312 service = requests.get(i);
1313 assertEquals("POST", service.getMethod());
1314 assertEquals(join(authOnPostURI, "git-upload-pack"),
1315 service.getPath());
1316 assertEquals(0, service.getParameters().size());
1317 assertNotNull("has content-length",
1318 service.getRequestHeader(HDR_CONTENT_LENGTH));
1319 assertNull("not chunked",
1320 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1321
1322 assertEquals(200, service.getStatus());
1323 assertEquals("application/x-git-upload-pack-result",
1324 service.getResponseHeader(HDR_CONTENT_TYPE));
1325 }
1326 }
1327
1328 @Test
1329 public void testFetch_FewLocalCommits() throws Exception {
1330
1331
1332 TestRepository dst = createTestRepository();
1333 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1334 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1335 }
1336 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1337 List<AccessEvent> cloneRequests = getRequests();
1338
1339
1340 TestRepository.BranchBuilder b = dst.branch(master);
1341 for (int i = 0; i < 4; i++)
1342 b.commit().tick(3600 ).message("c" + i).create();
1343
1344
1345
1346 RevCommit Z;
1347 try (TestRepository<Repository> tr = new TestRepository<>(
1348 remoteRepository)) {
1349 b = tr.branch(master);
1350 Z = b.commit().message("Z").create();
1351 }
1352
1353
1354
1355 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1356 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1357 }
1358 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1359
1360 List<AccessEvent> requests = getRequests();
1361 requests.removeAll(cloneRequests);
1362
1363 assertEquals(enableProtocolV2 ? 3 : 2, requests.size());
1364
1365 int requestNumber = 0;
1366 AccessEvent info = requests.get(requestNumber++);
1367 assertEquals("GET", info.getMethod());
1368 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1369 assertEquals(1, info.getParameters().size());
1370 assertEquals("git-upload-pack", info.getParameter("service"));
1371 assertEquals(200, info.getStatus());
1372 assertEquals("application/x-git-upload-pack-advertisement",
1373 info.getResponseHeader(HDR_CONTENT_TYPE));
1374
1375 if (enableProtocolV2) {
1376 AccessEvent lsRefs = requests.get(requestNumber++);
1377 assertEquals("POST", lsRefs.getMethod());
1378 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
1379 assertEquals(0, lsRefs.getParameters().size());
1380 assertNotNull("has content-length",
1381 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1382 assertNull("not chunked",
1383 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1384 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1385 assertEquals(200, lsRefs.getStatus());
1386 assertEquals("application/x-git-upload-pack-result",
1387 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1388 }
1389
1390
1391
1392 AccessEvent service = requests.get(requestNumber);
1393 assertEquals("POST", service.getMethod());
1394 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1395 assertEquals(0, service.getParameters().size());
1396 assertNotNull("has content-length",
1397 service.getRequestHeader(HDR_CONTENT_LENGTH));
1398 assertNull("not chunked",
1399 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1400
1401 assertEquals(200, service.getStatus());
1402 assertEquals("application/x-git-upload-pack-result",
1403 service.getResponseHeader(HDR_CONTENT_TYPE));
1404 }
1405
1406 @Test
1407 public void testFetch_TooManyLocalCommits() throws Exception {
1408
1409
1410 TestRepository dst = createTestRepository();
1411 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1412 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1413 }
1414 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1415 List<AccessEvent> cloneRequests = getRequests();
1416
1417
1418
1419
1420
1421 TestRepository.BranchBuilder b = dst.branch(master);
1422 for (int i = 0; i < 32 - 1; i++)
1423 b.commit().tick(3600 ).message("c" + i).create();
1424
1425
1426
1427 RevCommit Z;
1428 try (TestRepository<Repository> tr = new TestRepository<>(
1429 remoteRepository)) {
1430 b = tr.branch(master);
1431 Z = b.commit().message("Z").create();
1432 }
1433
1434
1435
1436 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1437 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1438 }
1439 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1440
1441 List<AccessEvent> requests = getRequests();
1442 requests.removeAll(cloneRequests);
1443 assertEquals(enableProtocolV2 ? 4 : 3, requests.size());
1444
1445 int requestNumber = 0;
1446 AccessEvent info = requests.get(requestNumber++);
1447 assertEquals("GET", info.getMethod());
1448 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1449 assertEquals(1, info.getParameters().size());
1450 assertEquals("git-upload-pack", info.getParameter("service"));
1451 assertEquals(200, info.getStatus());
1452 assertEquals("application/x-git-upload-pack-advertisement", info
1453 .getResponseHeader(HDR_CONTENT_TYPE));
1454
1455 if (enableProtocolV2) {
1456 AccessEvent lsRefs = requests.get(requestNumber++);
1457 assertEquals("POST", lsRefs.getMethod());
1458 assertEquals(join(remoteURI, "git-upload-pack"), lsRefs.getPath());
1459 assertEquals(0, lsRefs.getParameters().size());
1460 assertNotNull("has content-length",
1461 lsRefs.getRequestHeader(HDR_CONTENT_LENGTH));
1462 assertNull("not chunked",
1463 lsRefs.getRequestHeader(HDR_TRANSFER_ENCODING));
1464 assertEquals("version=2", lsRefs.getRequestHeader("Git-Protocol"));
1465 assertEquals(200, lsRefs.getStatus());
1466 assertEquals("application/x-git-upload-pack-result",
1467 lsRefs.getResponseHeader(HDR_CONTENT_TYPE));
1468 }
1469
1470
1471
1472
1473 AccessEvent service = requests.get(requestNumber++);
1474 assertEquals("POST", service.getMethod());
1475 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1476 assertEquals(0, service.getParameters().size());
1477 assertNotNull("has content-length", service
1478 .getRequestHeader(HDR_CONTENT_LENGTH));
1479 assertNull("not chunked", service
1480 .getRequestHeader(HDR_TRANSFER_ENCODING));
1481
1482 assertEquals(200, service.getStatus());
1483 assertEquals("application/x-git-upload-pack-result", service
1484 .getResponseHeader(HDR_CONTENT_TYPE));
1485
1486 service = requests.get(requestNumber);
1487 assertEquals("POST", service.getMethod());
1488 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1489 assertEquals(0, service.getParameters().size());
1490 assertNotNull("has content-length", service
1491 .getRequestHeader(HDR_CONTENT_LENGTH));
1492 assertNull("not chunked", service
1493 .getRequestHeader(HDR_TRANSFER_ENCODING));
1494
1495 assertEquals(200, service.getStatus());
1496 assertEquals("application/x-git-upload-pack-result", service
1497 .getResponseHeader(HDR_CONTENT_TYPE));
1498 }
1499
1500 @Test
1501 public void testFetch_MaxHavesCutoffAfterAckOnly() throws Exception {
1502
1503
1504 TestRepository dst = createTestRepository();
1505 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1506 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1507 }
1508 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522 TestRepository.BranchBuilder b = dst.branch(master);
1523
1524
1525
1526 for (int i = 0; i < 992; i++)
1527 b.commit().tick(3600 ).message("c" + i).create();
1528
1529
1530
1531 RevCommit Z;
1532 try (TestRepository<Repository> tr = new TestRepository<>(
1533 remoteRepository)) {
1534 b = tr.branch(master);
1535 Z = b.commit().message("Z").create();
1536 }
1537
1538
1539
1540 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
1541 Writer writer = new OutputStreamWriter(buffer, StandardCharsets.UTF_8);
1542 TextProgressMonitor monitor = new TextProgressMonitor(writer);
1543 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1544 t.fetch(monitor, mirror(master));
1545 }
1546 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1547
1548 String progressMessages = new String(buffer.toByteArray(),
1549 StandardCharsets.UTF_8);
1550 Pattern expected = Pattern
1551 .compile("Receiving objects:\\s+100% \\(1/1\\)\n");
1552 if (!expected.matcher(progressMessages).find()) {
1553 System.out.println(progressMessages);
1554 fail("Expected only one object to be sent");
1555 }
1556 }
1557
1558 @Test
1559 public void testInitialClone_BrokenServer() throws Exception {
1560 try (Repository dst = createBareRepository();
1561 Transport t = Transport.open(dst, brokenURI)) {
1562 assertFalse(dst.getObjectDatabase().has(A_txt));
1563 try {
1564 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1565 fail("fetch completed despite upload-pack being broken");
1566 } catch (TransportException err) {
1567 String exp = brokenURI + ": expected"
1568 + " Content-Type application/x-git-upload-pack-result;"
1569 + " received Content-Type text/plain;charset=utf-8";
1570 assertEquals(exp, err.getMessage());
1571 }
1572 }
1573
1574 List<AccessEvent> requests = getRequests();
1575 assertEquals(2, requests.size());
1576
1577 AccessEvent info = requests.get(0);
1578 assertEquals("GET", info.getMethod());
1579 assertEquals(join(brokenURI, "info/refs"), info.getPath());
1580 assertEquals(1, info.getParameters().size());
1581 assertEquals("git-upload-pack", info.getParameter("service"));
1582 assertEquals(200, info.getStatus());
1583 assertEquals("application/x-git-upload-pack-advertisement", info
1584 .getResponseHeader(HDR_CONTENT_TYPE));
1585
1586 AccessEvent service = requests.get(1);
1587 assertEquals("POST", service.getMethod());
1588 assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
1589 assertEquals(0, service.getParameters().size());
1590 assertEquals(200, service.getStatus());
1591 assertEquals("text/plain;charset=utf-8",
1592 service.getResponseHeader(HDR_CONTENT_TYPE));
1593 }
1594
1595 @Test
1596 public void testInvalidWant() throws Exception {
1597 ObjectId id;
1598 try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
1599 id = formatter.idFor(Constants.OBJ_BLOB,
1600 "testInvalidWant".getBytes(UTF_8));
1601 }
1602
1603 try (Repository dst = createBareRepository();
1604 Transport t = Transport.open(dst, remoteURI);
1605 FetchConnection c = t.openFetch()) {
1606 Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
1607 id);
1608 c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
1609 Collections.<ObjectId> emptySet());
1610 fail("Server accepted want " + id.name());
1611 } catch (TransportException err) {
1612 assertTrue(err.getMessage()
1613 .contains("want " + id.name() + " not valid"));
1614 }
1615 assertLastRequestStatusCode(200);
1616 }
1617
1618 @Test
1619 public void testFetch_RefsUnreadableOnUpload() throws Exception {
1620 AppServer noRefServer = new AppServer();
1621 try {
1622 final String repoName = "refs-unreadable";
1623 RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
1624 new DfsRepositoryDescription(repoName));
1625 final TestRepository<Repository> repo = new TestRepository<>(
1626 badRefsRepo);
1627 badRefsRepo.getConfig().setInt("protocol", null, "version",
1628 enableProtocolV2 ? 2 : 0);
1629
1630 ServletContextHandler app = noRefServer.addContext("/git");
1631 GitServlet gs = new GitServlet();
1632 gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName));
1633 app.addServlet(new ServletHolder(gs), "/*");
1634 noRefServer.setUp();
1635
1636 RevBlob A2_txt = repo.blob("A2");
1637 RevCommit A2 = repo.commit().add("A2_txt", A2_txt).create();
1638 RevCommit B2 = repo.commit().parent(A2).add("A2_txt", "C2")
1639 .add("B2", "B2").create();
1640 repo.update(master, B2);
1641
1642 URIish badRefsURI = new URIish(noRefServer.getURI()
1643 .resolve(app.getContextPath() + "/" + repoName).toString());
1644
1645 try (Repository dst = createBareRepository();
1646 Transport t = Transport.open(dst, badRefsURI);
1647 FetchConnection c = t.openFetch()) {
1648
1649
1650 badRefsRepo.startFailing();
1651
1652 badRefsRepo.getRefDatabase().refresh();
1653 c.fetch(NullProgressMonitor.INSTANCE,
1654 Collections.singleton(c.getRef(master)),
1655 Collections.<ObjectId> emptySet());
1656 fail("Successfully served ref with value " + c.getRef(master));
1657 } catch (TransportException err) {
1658 assertTrue("Unexpected exception message " + err.getMessage(),
1659 err.getMessage().contains("Server Error"));
1660 }
1661 } finally {
1662 noRefServer.tearDown();
1663 }
1664 }
1665
1666 @Test
1667 public void testPush_NotAuthorized() throws Exception {
1668 final TestRepository src = createTestRepository();
1669 final RevBlob Q_txt = src.blob("new text");
1670 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1671 final Repository db = src.getRepository();
1672 final String dstName = Constants.R_HEADS + "new.branch";
1673
1674
1675
1676 try (Transport t = Transport.open(db, remoteURI)) {
1677 final String srcExpr = Q.name();
1678 final boolean forceUpdate = false;
1679 final String localName = null;
1680 final ObjectId oldId = null;
1681
1682 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1683 srcExpr, dstName, forceUpdate, localName, oldId);
1684 try {
1685 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1686 fail("anonymous push incorrectly accepted without error");
1687 } catch (TransportException e) {
1688 final String exp = remoteURI + ": "
1689 + JGitText.get().authenticationNotSupported;
1690 assertEquals(exp, e.getMessage());
1691 }
1692 }
1693
1694 List<AccessEvent> requests = getRequests();
1695 assertEquals(1, requests.size());
1696
1697 AccessEvent info = requests.get(0);
1698 assertEquals("GET", info.getMethod());
1699 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1700 assertEquals(1, info.getParameters().size());
1701 assertEquals("git-receive-pack", info.getParameter("service"));
1702 assertEquals(401, info.getStatus());
1703 }
1704
1705 @Test
1706 public void testPush_CreateBranch() throws Exception {
1707 final TestRepository src = createTestRepository();
1708 final RevBlob Q_txt = src.blob("new text");
1709 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1710 final Repository db = src.getRepository();
1711 final String dstName = Constants.R_HEADS + "new.branch";
1712
1713 enableReceivePack();
1714
1715 try (Transport t = Transport.open(db, remoteURI)) {
1716 final String srcExpr = Q.name();
1717 final boolean forceUpdate = false;
1718 final String localName = null;
1719 final ObjectId oldId = null;
1720
1721 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1722 srcExpr, dstName, forceUpdate, localName, oldId);
1723 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1724 }
1725
1726 assertTrue(remoteRepository.getObjectDatabase().has(Q_txt));
1727 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1728 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1729 fsck(remoteRepository, Q);
1730
1731 final ReflogReader log = remoteRepository.getReflogReader(dstName);
1732 assertNotNull("has log for " + dstName, log);
1733
1734 final ReflogEntry last = log.getLastEntry();
1735 assertNotNull("has last entry", last);
1736 assertEquals(ObjectId.zeroId(), last.getOldId());
1737 assertEquals(Q, last.getNewId());
1738 assertEquals("anonymous", last.getWho().getName());
1739
1740
1741
1742
1743
1744 final String clientHost = remoteURI.getHost();
1745 assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
1746 assertEquals("push: created", last.getComment());
1747
1748 List<AccessEvent> requests = getRequests();
1749 assertEquals(2, requests.size());
1750
1751 AccessEvent info = requests.get(0);
1752 assertEquals("GET", info.getMethod());
1753 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1754 assertEquals(1, info.getParameters().size());
1755 assertEquals("git-receive-pack", info.getParameter("service"));
1756 assertEquals(200, info.getStatus());
1757 assertEquals("application/x-git-receive-pack-advertisement", info
1758 .getResponseHeader(HDR_CONTENT_TYPE));
1759
1760 AccessEvent service = requests.get(1);
1761 assertEquals("POST", service.getMethod());
1762 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1763 assertEquals(0, service.getParameters().size());
1764 assertNotNull("has content-length", service
1765 .getRequestHeader(HDR_CONTENT_LENGTH));
1766 assertNull("not chunked", service
1767 .getRequestHeader(HDR_TRANSFER_ENCODING));
1768
1769 assertEquals(200, service.getStatus());
1770 assertEquals("application/x-git-receive-pack-result", service
1771 .getResponseHeader(HDR_CONTENT_TYPE));
1772 }
1773
1774 @Test
1775 public void testPush_ChunkedEncoding() throws Exception {
1776 final TestRepository<Repository> src = createTestRepository();
1777 final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
1778 final RevCommit Q = src.commit().add("Q", Q_bin).create();
1779 final Repository db = src.getRepository();
1780 final String dstName = Constants.R_HEADS + "new.branch";
1781
1782 enableReceivePack();
1783
1784 final StoredConfig cfg = db.getConfig();
1785 cfg.setInt("core", null, "compression", 0);
1786 cfg.setInt("http", null, "postbuffer", 8 * 1024);
1787 cfg.save();
1788
1789 try (Transport t = Transport.open(db, remoteURI)) {
1790 final String srcExpr = Q.name();
1791 final boolean forceUpdate = false;
1792 final String localName = null;
1793 final ObjectId oldId = null;
1794
1795 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1796 srcExpr, dstName, forceUpdate, localName, oldId);
1797 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1798 }
1799
1800 assertTrue(remoteRepository.getObjectDatabase().has(Q_bin));
1801 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1802 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1803 fsck(remoteRepository, Q);
1804
1805 List<AccessEvent> requests = getRequests();
1806 assertEquals(2, requests.size());
1807
1808 AccessEvent info = requests.get(0);
1809 assertEquals("GET", info.getMethod());
1810 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1811 assertEquals(1, info.getParameters().size());
1812 assertEquals("git-receive-pack", info.getParameter("service"));
1813 assertEquals(200, info.getStatus());
1814 assertEquals("application/x-git-receive-pack-advertisement", info
1815 .getResponseHeader(HDR_CONTENT_TYPE));
1816
1817 AccessEvent service = requests.get(1);
1818 assertEquals("POST", service.getMethod());
1819 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1820 assertEquals(0, service.getParameters().size());
1821 assertNull("no content-length", service
1822 .getRequestHeader(HDR_CONTENT_LENGTH));
1823 assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
1824
1825 assertEquals(200, service.getStatus());
1826 assertEquals("application/x-git-receive-pack-result", service
1827 .getResponseHeader(HDR_CONTENT_TYPE));
1828 }
1829
1830 private void assertLastRequestStatusCode(int statusCode) {
1831 List<AccessEvent> requests = getRequests();
1832 assertEquals(statusCode, requests.get(requests.size() - 1).getStatus());
1833 }
1834
1835 private void enableReceivePack() throws IOException {
1836 final StoredConfig cfg = remoteRepository.getConfig();
1837 cfg.setBoolean("http", null, "receivepack", true);
1838 cfg.save();
1839 }
1840 }