1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44 package org.eclipse.jgit.http.test;
45
46 import static java.nio.charset.StandardCharsets.UTF_8;
47 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
48 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
49 import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertFalse;
52 import static org.junit.Assert.assertNotNull;
53 import static org.junit.Assert.assertNull;
54 import static org.junit.Assert.assertTrue;
55 import static org.junit.Assert.fail;
56
57 import java.io.IOException;
58 import java.io.PrintWriter;
59 import java.net.URI;
60 import java.net.URISyntaxException;
61 import java.text.MessageFormat;
62 import java.util.Collections;
63 import java.util.EnumSet;
64 import java.util.List;
65 import java.util.Map;
66 import java.util.regex.Matcher;
67 import java.util.regex.Pattern;
68
69 import javax.servlet.DispatcherType;
70 import javax.servlet.Filter;
71 import javax.servlet.FilterChain;
72 import javax.servlet.FilterConfig;
73 import javax.servlet.RequestDispatcher;
74 import javax.servlet.ServletException;
75 import javax.servlet.ServletRequest;
76 import javax.servlet.ServletResponse;
77 import javax.servlet.http.HttpServletRequest;
78 import javax.servlet.http.HttpServletResponse;
79
80 import org.eclipse.jetty.servlet.FilterHolder;
81 import org.eclipse.jetty.servlet.ServletContextHandler;
82 import org.eclipse.jetty.servlet.ServletHolder;
83 import org.eclipse.jgit.errors.RemoteRepositoryException;
84 import org.eclipse.jgit.errors.TransportException;
85 import org.eclipse.jgit.errors.UnsupportedCredentialItem;
86 import org.eclipse.jgit.http.server.GitServlet;
87 import org.eclipse.jgit.http.server.resolver.DefaultUploadPackFactory;
88 import org.eclipse.jgit.internal.JGitText;
89 import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
90 import org.eclipse.jgit.junit.TestRepository;
91 import org.eclipse.jgit.junit.TestRng;
92 import org.eclipse.jgit.junit.http.AccessEvent;
93 import org.eclipse.jgit.junit.http.AppServer;
94 import org.eclipse.jgit.lib.ConfigConstants;
95 import org.eclipse.jgit.lib.Constants;
96 import org.eclipse.jgit.lib.NullProgressMonitor;
97 import org.eclipse.jgit.lib.ObjectId;
98 import org.eclipse.jgit.lib.ObjectIdRef;
99 import org.eclipse.jgit.lib.ObjectInserter;
100 import org.eclipse.jgit.lib.Ref;
101 import org.eclipse.jgit.lib.ReflogEntry;
102 import org.eclipse.jgit.lib.ReflogReader;
103 import org.eclipse.jgit.lib.Repository;
104 import org.eclipse.jgit.lib.StoredConfig;
105 import org.eclipse.jgit.revwalk.RevBlob;
106 import org.eclipse.jgit.revwalk.RevCommit;
107 import org.eclipse.jgit.revwalk.RevWalk;
108 import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
109 import org.eclipse.jgit.transport.AdvertiseRefsHook;
110 import org.eclipse.jgit.transport.CredentialItem;
111 import org.eclipse.jgit.transport.CredentialsProvider;
112 import org.eclipse.jgit.transport.FetchConnection;
113 import org.eclipse.jgit.transport.HttpTransport;
114 import org.eclipse.jgit.transport.RefSpec;
115 import org.eclipse.jgit.transport.RemoteRefUpdate;
116 import org.eclipse.jgit.transport.Transport;
117 import org.eclipse.jgit.transport.TransportHttp;
118 import org.eclipse.jgit.transport.URIish;
119 import org.eclipse.jgit.transport.UploadPack;
120 import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
121 import org.eclipse.jgit.transport.http.HttpConnectionFactory;
122 import org.eclipse.jgit.util.HttpSupport;
123 import org.eclipse.jgit.util.SystemReader;
124 import org.hamcrest.Matchers;
125 import org.junit.Before;
126 import org.junit.Rule;
127 import org.junit.Test;
128 import org.junit.rules.ExpectedException;
129
130 public class SmartClientSmartServerTest extends AllFactoriesHttpTestCase {
131 private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
132
133 @Rule
134 public ExpectedException thrown = ExpectedException.none();
135
136 private AdvertiseRefsHook advertiseRefsHook;
137
138 private Repository remoteRepository;
139
140 private CredentialsProvider testCredentials = new UsernamePasswordCredentialsProvider(
141 AppServer.username, AppServer.password);
142
143 private URIish remoteURI;
144
145 private URIish brokenURI;
146
147 private URIish redirectURI;
148
149 private URIish authURI;
150
151 private URIish authOnPostURI;
152
153 private RevBlob A_txt;
154
155 private RevCommit A, B, unreachableCommit;
156
157 public SmartClientSmartServerTest(HttpConnectionFactory cf) {
158 super(cf);
159 }
160
161 @Override
162 @Before
163 public void setUp() throws Exception {
164 super.setUp();
165
166 final TestRepository<Repository> src = createTestRepository();
167 final String srcName = src.getRepository().getDirectory().getName();
168 src.getRepository()
169 .getConfig()
170 .setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
171 ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
172
173 GitServlet gs = new GitServlet();
174 gs.setUploadPackFactory((HttpServletRequest req, Repository db) -> {
175 DefaultUploadPackFactory f = new DefaultUploadPackFactory();
176 UploadPack up = f.create(req, db);
177 if (advertiseRefsHook != null) {
178 up.setAdvertiseRefsHook(advertiseRefsHook);
179 }
180 return up;
181 });
182
183 ServletContextHandler app = addNormalContext(gs, src, srcName);
184
185 ServletContextHandler broken = addBrokenContext(gs, srcName);
186
187 ServletContextHandler redirect = addRedirectContext(gs);
188
189 ServletContextHandler auth = addAuthContext(gs, "auth");
190
191 ServletContextHandler authOnPost = addAuthContext(gs, "pauth", "POST");
192
193 server.setUp();
194
195 remoteRepository = src.getRepository();
196 remoteURI = toURIish(app, srcName);
197 brokenURI = toURIish(broken, srcName);
198 redirectURI = toURIish(redirect, srcName);
199 authURI = toURIish(auth, srcName);
200 authOnPostURI = toURIish(authOnPost, srcName);
201
202 A_txt = src.blob("A");
203 A = src.commit().add("A_txt", A_txt).create();
204 B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
205 src.update(master, B);
206
207 unreachableCommit = src.commit().add("A_txt", A_txt).create();
208
209 src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
210 }
211
212 private ServletContextHandler addNormalContext(GitServlet gs, TestRepository<Repository> src, String srcName) {
213 ServletContextHandler app = server.addContext("/git");
214 app.addFilter(new FilterHolder(new Filter() {
215
216 @Override
217 public void init(FilterConfig filterConfig)
218 throws ServletException {
219
220 }
221
222
223
224
225 @Override
226 public void doFilter(ServletRequest request,
227 ServletResponse response, FilterChain chain)
228 throws IOException, ServletException {
229 final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
230 final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
231 final StringBuffer fullUrl = httpServletRequest.getRequestURL();
232 if (httpServletRequest.getQueryString() != null) {
233 fullUrl.append("?")
234 .append(httpServletRequest.getQueryString());
235 }
236 String urlString = fullUrl.toString();
237 if ("POST".equalsIgnoreCase(httpServletRequest.getMethod())) {
238 httpServletResponse.setStatus(
239 HttpServletResponse.SC_MOVED_PERMANENTLY);
240 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
241 urlString.replace("/post/", "/"));
242 } else {
243 String path = httpServletRequest.getPathInfo();
244 path = path.replace("/post/", "/");
245 if (httpServletRequest.getQueryString() != null) {
246 path += '?' + httpServletRequest.getQueryString();
247 }
248 RequestDispatcher dispatcher = httpServletRequest
249 .getRequestDispatcher(path);
250 dispatcher.forward(httpServletRequest, httpServletResponse);
251 }
252 }
253
254 @Override
255 public void destroy() {
256
257 }
258 }), "/post/*", EnumSet.of(DispatcherType.REQUEST));
259 gs.setRepositoryResolver(new TestRepositoryResolver(src, srcName));
260 app.addServlet(new ServletHolder(gs), "/*");
261 return app;
262 }
263
264 private ServletContextHandler addBrokenContext(GitServlet gs,
265 String srcName) {
266 ServletContextHandler broken = server.addContext("/bad");
267 broken.addFilter(new FilterHolder(new Filter() {
268
269 @Override
270 public void doFilter(ServletRequest request,
271 ServletResponse response, FilterChain chain)
272 throws IOException, ServletException {
273 final HttpServletResponse r = (HttpServletResponse) response;
274 r.setContentType("text/plain");
275 r.setCharacterEncoding(UTF_8.name());
276 try (PrintWriter w = r.getWriter()) {
277 w.print("OK");
278 }
279 }
280
281 @Override
282 public void init(FilterConfig filterConfig)
283 throws ServletException {
284
285 }
286
287 @Override
288 public void destroy() {
289
290 }
291 }), "/" + srcName + "/git-upload-pack",
292 EnumSet.of(DispatcherType.REQUEST));
293 broken.addServlet(new ServletHolder(gs), "/*");
294 return broken;
295 }
296
297 private ServletContextHandler addAuthContext(GitServlet gs,
298 String contextPath, String... methods) {
299 ServletContextHandler auth = server.addContext('/' + contextPath);
300 auth.addServlet(new ServletHolder(gs), "/*");
301 return server.authBasic(auth, methods);
302 }
303
304 private ServletContextHandler addRedirectContext(GitServlet gs) {
305 ServletContextHandler redirect = server.addContext("/redirect");
306 redirect.addFilter(new FilterHolder(new Filter() {
307
308
309
310
311 private Pattern responsePattern = Pattern
312 .compile("/response/(\\d+)/(30[1237])/");
313
314
315
316
317 private Pattern targetPattern = Pattern.compile("/target(/\\w+)/");
318
319 @Override
320 public void init(FilterConfig filterConfig)
321 throws ServletException {
322
323 }
324
325 private String local(String url, boolean toLocal) {
326 if (!toLocal) {
327 return url;
328 }
329 try {
330 URI u = new URI(url);
331 String fragment = u.getRawFragment();
332 if (fragment != null) {
333 return u.getRawPath() + '#' + fragment;
334 } else {
335 return u.getRawPath();
336 }
337 } catch (URISyntaxException e) {
338 return url;
339 }
340 }
341
342 @Override
343 public void doFilter(ServletRequest request,
344 ServletResponse response, FilterChain chain)
345 throws IOException, ServletException {
346 final HttpServletResponse httpServletResponse = (HttpServletResponse) response;
347 final HttpServletRequest httpServletRequest = (HttpServletRequest) request;
348 final StringBuffer fullUrl = httpServletRequest.getRequestURL();
349 if (httpServletRequest.getQueryString() != null) {
350 fullUrl.append("?")
351 .append(httpServletRequest.getQueryString());
352 }
353 String urlString = fullUrl.toString();
354 boolean localRedirect = false;
355 if (urlString.contains("/local")) {
356 urlString = urlString.replace("/local", "");
357 localRedirect = true;
358 }
359 if (urlString.contains("/loop/")) {
360 urlString = urlString.replace("/loop/", "/loop/x/");
361 if (urlString.contains("/loop/x/x/x/x/x/x/x/x/")) {
362
363 urlString = urlString.replace("/loop/x/x/x/x/x/x/x/x/",
364 "/loop/");
365 }
366 httpServletResponse.setStatus(
367 HttpServletResponse.SC_MOVED_TEMPORARILY);
368 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
369 local(urlString, localRedirect));
370 return;
371 }
372 int responseCode = HttpServletResponse.SC_MOVED_PERMANENTLY;
373 int nofRedirects = 0;
374 Matcher matcher = responsePattern.matcher(urlString);
375 if (matcher.find()) {
376 nofRedirects = Integer
377 .parseUnsignedInt(matcher.group(1));
378 responseCode = Integer.parseUnsignedInt(matcher.group(2));
379 if (--nofRedirects <= 0) {
380 urlString = urlString.substring(0, matcher.start())
381 + '/' + urlString.substring(matcher.end());
382 } else {
383 urlString = urlString.substring(0, matcher.start())
384 + "/response/" + nofRedirects + "/"
385 + responseCode + '/'
386 + urlString.substring(matcher.end());
387 }
388 }
389 httpServletResponse.setStatus(responseCode);
390 if (nofRedirects <= 0) {
391 String targetContext = "/git";
392 matcher = targetPattern.matcher(urlString);
393 if (matcher.find()) {
394 urlString = urlString.substring(0, matcher.start())
395 + '/' + urlString.substring(matcher.end());
396 targetContext = matcher.group(1);
397 }
398 urlString = urlString.replace("/redirect", targetContext);
399
400 }
401 httpServletResponse.setHeader(HttpSupport.HDR_LOCATION,
402 local(urlString, localRedirect));
403 }
404
405 @Override
406 public void destroy() {
407
408 }
409 }), "/*", EnumSet.of(DispatcherType.REQUEST));
410 redirect.addServlet(new ServletHolder(gs), "/*");
411 return redirect;
412 }
413
414 @Test
415 public void testListRemote() throws IOException {
416 assertEquals("http", remoteURI.getScheme());
417
418 Map<String, Ref> map;
419 try (Repository dst = createBareRepository();
420 Transport t = Transport.open(dst, remoteURI)) {
421
422
423
424
425 assertTrue("isa TransportHttp", t instanceof TransportHttp);
426 assertTrue("isa HttpTransport", t instanceof HttpTransport);
427
428 try (FetchConnection c = t.openFetch()) {
429 map = c.getRefsMap();
430 }
431 }
432
433 assertNotNull("have map of refs", map);
434 assertEquals(3, map.size());
435
436 assertNotNull("has " + master, map.get(master));
437 assertEquals(B, map.get(master).getObjectId());
438
439 assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
440 assertEquals(B, map.get(Constants.HEAD).getObjectId());
441
442 List<AccessEvent> requests = getRequests();
443 assertEquals(1, requests.size());
444
445 AccessEvent info = requests.get(0);
446 assertEquals("GET", info.getMethod());
447 assertEquals(join(remoteURI, "info/refs"), info.getPath());
448 assertEquals(1, info.getParameters().size());
449 assertEquals("git-upload-pack", info.getParameter("service"));
450 assertEquals(200, info.getStatus());
451 assertEquals("application/x-git-upload-pack-advertisement", info
452 .getResponseHeader(HDR_CONTENT_TYPE));
453 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
454 }
455
456 @Test
457 public void testListRemote_BadName() throws IOException, URISyntaxException {
458 URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
459 try (Repository dst = createBareRepository();
460 Transport t = Transport.open(dst, uri)) {
461 try {
462 t.openFetch();
463 fail("fetch connection opened");
464 } catch (RemoteRepositoryException notFound) {
465 assertEquals(uri + ": Git repository not found",
466 notFound.getMessage());
467 }
468 }
469
470 List<AccessEvent> requests = getRequests();
471 assertEquals(1, requests.size());
472
473 AccessEvent info = requests.get(0);
474 assertEquals("GET", info.getMethod());
475 assertEquals(join(uri, "info/refs"), info.getPath());
476 assertEquals(1, info.getParameters().size());
477 assertEquals("git-upload-pack", info.getParameter("service"));
478 assertEquals(200, info.getStatus());
479 assertEquals("application/x-git-upload-pack-advertisement",
480 info.getResponseHeader(HDR_CONTENT_TYPE));
481 }
482
483 @Test
484 public void testFetchBySHA1() throws Exception {
485 try (Repository dst = createBareRepository();
486 Transport t = Transport.open(dst, remoteURI)) {
487 assertFalse(dst.getObjectDatabase().has(A_txt));
488 t.fetch(NullProgressMonitor.INSTANCE,
489 Collections.singletonList(new RefSpec(B.name())));
490 assertTrue(dst.getObjectDatabase().has(A_txt));
491 }
492 }
493
494 @Test
495 public void testFetchBySHA1Unreachable() throws Exception {
496 try (Repository dst = createBareRepository();
497 Transport t = Transport.open(dst, remoteURI)) {
498 assertFalse(dst.getObjectDatabase().has(A_txt));
499 thrown.expect(TransportException.class);
500 thrown.expectMessage(Matchers.containsString(
501 "want " + unreachableCommit.name() + " not valid"));
502 t.fetch(NullProgressMonitor.INSTANCE, Collections
503 .singletonList(new RefSpec(unreachableCommit.name())));
504 }
505 }
506
507 @Test
508 public void testFetchBySHA1UnreachableByAdvertiseRefsHook()
509 throws Exception {
510 advertiseRefsHook = new AbstractAdvertiseRefsHook() {
511 @Override
512 protected Map<String, Ref> getAdvertisedRefs(Repository repository,
513 RevWalk revWalk) {
514 return Collections.emptyMap();
515 }
516 };
517
518 try (Repository dst = createBareRepository();
519 Transport t = Transport.open(dst, remoteURI)) {
520 assertFalse(dst.getObjectDatabase().has(A_txt));
521 thrown.expect(TransportException.class);
522 thrown.expectMessage(Matchers.containsString(
523 "want " + A.name() + " not valid"));
524 t.fetch(NullProgressMonitor.INSTANCE, Collections
525 .singletonList(new RefSpec(A.name())));
526 }
527 }
528
529 @Test
530 public void testInitialClone_Small() throws Exception {
531 try (Repository dst = createBareRepository();
532 Transport t = Transport.open(dst, remoteURI)) {
533 assertFalse(dst.getObjectDatabase().has(A_txt));
534 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
535 assertTrue(dst.getObjectDatabase().has(A_txt));
536 assertEquals(B, dst.exactRef(master).getObjectId());
537 fsck(dst, B);
538 }
539
540 List<AccessEvent> requests = getRequests();
541 assertEquals(2, requests.size());
542
543 AccessEvent info = requests.get(0);
544 assertEquals("GET", info.getMethod());
545 assertEquals(join(remoteURI, "info/refs"), info.getPath());
546 assertEquals(1, info.getParameters().size());
547 assertEquals("git-upload-pack", info.getParameter("service"));
548 assertEquals(200, info.getStatus());
549 assertEquals("application/x-git-upload-pack-advertisement", info
550 .getResponseHeader(HDR_CONTENT_TYPE));
551 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
552
553 AccessEvent service = requests.get(1);
554 assertEquals("POST", service.getMethod());
555 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
556 assertEquals(0, service.getParameters().size());
557 assertNotNull("has content-length", service
558 .getRequestHeader(HDR_CONTENT_LENGTH));
559 assertNull("not chunked", service
560 .getRequestHeader(HDR_TRANSFER_ENCODING));
561
562 assertEquals(200, service.getStatus());
563 assertEquals("application/x-git-upload-pack-result", service
564 .getResponseHeader(HDR_CONTENT_TYPE));
565 }
566
567 private void initialClone_Redirect(int nofRedirects, int code)
568 throws Exception {
569 initialClone_Redirect(nofRedirects, code, false);
570 }
571
572 private void initialClone_Redirect(int nofRedirects, int code,
573 boolean localRedirect) throws Exception {
574 URIish cloneFrom = redirectURI;
575 if (localRedirect) {
576 cloneFrom = extendPath(cloneFrom, "/local");
577 }
578 if (code != 301 || nofRedirects > 1) {
579 cloneFrom = extendPath(cloneFrom,
580 "/response/" + nofRedirects + "/" + code);
581 }
582
583 try (Repository dst = createBareRepository();
584 Transport t = Transport.open(dst, cloneFrom)) {
585 assertFalse(dst.getObjectDatabase().has(A_txt));
586 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
587 assertTrue(dst.getObjectDatabase().has(A_txt));
588 assertEquals(B, dst.exactRef(master).getObjectId());
589 fsck(dst, B);
590 }
591
592 List<AccessEvent> requests = getRequests();
593 assertEquals(2 + nofRedirects, requests.size());
594
595 int n = 0;
596 while (n < nofRedirects) {
597 AccessEvent redirect = requests.get(n++);
598 assertEquals(code, redirect.getStatus());
599 }
600
601 AccessEvent info = requests.get(n++);
602 assertEquals("GET", info.getMethod());
603 assertEquals(join(remoteURI, "info/refs"), info.getPath());
604 assertEquals(1, info.getParameters().size());
605 assertEquals("git-upload-pack", info.getParameter("service"));
606 assertEquals(200, info.getStatus());
607 assertEquals("application/x-git-upload-pack-advertisement",
608 info.getResponseHeader(HDR_CONTENT_TYPE));
609 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
610
611 AccessEvent service = requests.get(n++);
612 assertEquals("POST", service.getMethod());
613 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
614 assertEquals(0, service.getParameters().size());
615 assertNotNull("has content-length",
616 service.getRequestHeader(HDR_CONTENT_LENGTH));
617 assertNull("not chunked",
618 service.getRequestHeader(HDR_TRANSFER_ENCODING));
619
620 assertEquals(200, service.getStatus());
621 assertEquals("application/x-git-upload-pack-result",
622 service.getResponseHeader(HDR_CONTENT_TYPE));
623 }
624
625 @Test
626 public void testInitialClone_Redirect301Small() throws Exception {
627 initialClone_Redirect(1, 301);
628 }
629
630 @Test
631 public void testInitialClone_Redirect301Local() throws Exception {
632 initialClone_Redirect(1, 301, true);
633 }
634
635 @Test
636 public void testInitialClone_Redirect302Small() throws Exception {
637 initialClone_Redirect(1, 302);
638 }
639
640 @Test
641 public void testInitialClone_Redirect303Small() throws Exception {
642 initialClone_Redirect(1, 303);
643 }
644
645 @Test
646 public void testInitialClone_Redirect307Small() throws Exception {
647 initialClone_Redirect(1, 307);
648 }
649
650 @Test
651 public void testInitialClone_RedirectMultiple() throws Exception {
652 initialClone_Redirect(4, 302);
653 }
654
655 @Test
656 public void testInitialClone_RedirectMax() throws Exception {
657 StoredConfig userConfig = SystemReader.getInstance()
658 .getUserConfig();
659 userConfig.setInt("http", null, "maxRedirects", 4);
660 userConfig.save();
661 initialClone_Redirect(4, 302);
662 }
663
664 @Test
665 public void testInitialClone_RedirectTooOften() throws Exception {
666 StoredConfig userConfig = SystemReader.getInstance()
667 .getUserConfig();
668 userConfig.setInt("http", null, "maxRedirects", 3);
669 userConfig.save();
670
671 URIish cloneFrom = extendPath(redirectURI, "/response/4/302");
672 String remoteUri = cloneFrom.toString();
673 try (Repository dst = createBareRepository();
674 Transport t = Transport.open(dst, cloneFrom)) {
675 assertFalse(dst.getObjectDatabase().has(A_txt));
676 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
677 fail("Should have failed (too many redirects)");
678 } catch (TransportException e) {
679 String expectedMessageBegin = remoteUri.toString() + ": "
680 + MessageFormat.format(JGitText.get().redirectLimitExceeded,
681 "3", remoteUri.replace("/4/", "/1/") + '/', "");
682 String message = e.getMessage();
683 if (message.length() > expectedMessageBegin.length()) {
684 message = message.substring(0, expectedMessageBegin.length());
685 }
686 assertEquals(expectedMessageBegin, message);
687 }
688 }
689
690 @Test
691 public void testInitialClone_RedirectLoop() throws Exception {
692 URIish cloneFrom = extendPath(redirectURI, "/loop");
693 try (Repository dst = createBareRepository();
694 Transport t = Transport.open(dst, cloneFrom)) {
695 assertFalse(dst.getObjectDatabase().has(A_txt));
696 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
697 fail("Should have failed (redirect loop)");
698 } catch (TransportException e) {
699 assertTrue(e.getMessage().contains("Redirected more than"));
700 }
701 }
702
703 @Test
704 public void testInitialClone_RedirectOnPostAllowed() throws Exception {
705 StoredConfig userConfig = SystemReader.getInstance()
706 .getUserConfig();
707 userConfig.setString("http", null, "followRedirects", "true");
708 userConfig.save();
709
710 URIish cloneFrom = extendPath(remoteURI, "/post");
711 try (Repository dst = createBareRepository();
712 Transport t = Transport.open(dst, cloneFrom)) {
713 assertFalse(dst.getObjectDatabase().has(A_txt));
714 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
715 assertTrue(dst.getObjectDatabase().has(A_txt));
716 assertEquals(B, dst.exactRef(master).getObjectId());
717 fsck(dst, B);
718 }
719
720 List<AccessEvent> requests = getRequests();
721 assertEquals(3, requests.size());
722
723 AccessEvent info = requests.get(0);
724 assertEquals("GET", info.getMethod());
725 assertEquals(join(cloneFrom, "info/refs"), info.getPath());
726 assertEquals(1, info.getParameters().size());
727 assertEquals("git-upload-pack", info.getParameter("service"));
728 assertEquals(200, info.getStatus());
729 assertEquals("application/x-git-upload-pack-advertisement",
730 info.getResponseHeader(HDR_CONTENT_TYPE));
731 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
732
733 AccessEvent redirect = requests.get(1);
734 assertEquals("POST", redirect.getMethod());
735 assertEquals(301, redirect.getStatus());
736
737 AccessEvent service = requests.get(2);
738 assertEquals("POST", service.getMethod());
739 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
740 assertEquals(0, service.getParameters().size());
741 assertNotNull("has content-length",
742 service.getRequestHeader(HDR_CONTENT_LENGTH));
743 assertNull("not chunked",
744 service.getRequestHeader(HDR_TRANSFER_ENCODING));
745
746 assertEquals(200, service.getStatus());
747 assertEquals("application/x-git-upload-pack-result",
748 service.getResponseHeader(HDR_CONTENT_TYPE));
749 }
750
751 @Test
752 public void testInitialClone_RedirectOnPostForbidden() throws Exception {
753 URIish cloneFrom = extendPath(remoteURI, "/post");
754 try (Repository dst = createBareRepository();
755 Transport t = Transport.open(dst, cloneFrom)) {
756 assertFalse(dst.getObjectDatabase().has(A_txt));
757 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
758 fail("Should have failed (redirect on POST)");
759 } catch (TransportException e) {
760 assertTrue(e.getMessage().contains("301"));
761 }
762 }
763
764 @Test
765 public void testInitialClone_RedirectForbidden() throws Exception {
766 StoredConfig userConfig = SystemReader.getInstance()
767 .getUserConfig();
768 userConfig.setString("http", null, "followRedirects", "false");
769 userConfig.save();
770
771 try (Repository dst = createBareRepository();
772 Transport t = Transport.open(dst, redirectURI)) {
773 assertFalse(dst.getObjectDatabase().has(A_txt));
774 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
775 fail("Should have failed (redirects forbidden)");
776 } catch (TransportException e) {
777 assertTrue(
778 e.getMessage().contains("http.followRedirects is false"));
779 }
780 }
781
782 @Test
783 public void testInitialClone_WithAuthentication() throws Exception {
784 try (Repository dst = createBareRepository();
785 Transport t = Transport.open(dst, authURI)) {
786 assertFalse(dst.getObjectDatabase().has(A_txt));
787 t.setCredentialsProvider(testCredentials);
788 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
789 assertTrue(dst.getObjectDatabase().has(A_txt));
790 assertEquals(B, dst.exactRef(master).getObjectId());
791 fsck(dst, B);
792 }
793
794 List<AccessEvent> requests = getRequests();
795 assertEquals(3, requests.size());
796
797 AccessEvent info = requests.get(0);
798 assertEquals("GET", info.getMethod());
799 assertEquals(401, info.getStatus());
800
801 info = requests.get(1);
802 assertEquals("GET", info.getMethod());
803 assertEquals(join(authURI, "info/refs"), info.getPath());
804 assertEquals(1, info.getParameters().size());
805 assertEquals("git-upload-pack", info.getParameter("service"));
806 assertEquals(200, info.getStatus());
807 assertEquals("application/x-git-upload-pack-advertisement",
808 info.getResponseHeader(HDR_CONTENT_TYPE));
809 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
810
811 AccessEvent service = requests.get(2);
812 assertEquals("POST", service.getMethod());
813 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
814 assertEquals(0, service.getParameters().size());
815 assertNotNull("has content-length",
816 service.getRequestHeader(HDR_CONTENT_LENGTH));
817 assertNull("not chunked",
818 service.getRequestHeader(HDR_TRANSFER_ENCODING));
819
820 assertEquals(200, service.getStatus());
821 assertEquals("application/x-git-upload-pack-result",
822 service.getResponseHeader(HDR_CONTENT_TYPE));
823 }
824
825 @Test
826 public void testInitialClone_WithAuthenticationNoCredentials()
827 throws Exception {
828 try (Repository dst = createBareRepository();
829 Transport t = Transport.open(dst, authURI)) {
830 assertFalse(dst.getObjectDatabase().has(A_txt));
831 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
832 fail("Should not have succeeded -- no authentication");
833 } catch (TransportException e) {
834 String msg = e.getMessage();
835 assertTrue("Unexpected exception message: " + msg,
836 msg.contains("no CredentialsProvider"));
837 }
838 List<AccessEvent> requests = getRequests();
839 assertEquals(1, requests.size());
840
841 AccessEvent info = requests.get(0);
842 assertEquals("GET", info.getMethod());
843 assertEquals(401, info.getStatus());
844 }
845
846 @Test
847 public void testInitialClone_WithAuthenticationWrongCredentials()
848 throws Exception {
849 try (Repository dst = createBareRepository();
850 Transport t = Transport.open(dst, authURI)) {
851 assertFalse(dst.getObjectDatabase().has(A_txt));
852 t.setCredentialsProvider(new UsernamePasswordCredentialsProvider(
853 AppServer.username, "wrongpassword"));
854 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
855 fail("Should not have succeeded -- wrong password");
856 } catch (TransportException e) {
857 String msg = e.getMessage();
858 assertTrue("Unexpected exception message: " + msg,
859 msg.contains("auth"));
860 }
861 List<AccessEvent> requests = getRequests();
862
863 assertEquals(4, requests.size());
864
865 for (AccessEvent event : requests) {
866 assertEquals("GET", event.getMethod());
867 assertEquals(401, event.getStatus());
868 }
869 }
870
871 @Test
872 public void testInitialClone_WithAuthenticationAfterRedirect()
873 throws Exception {
874 URIish cloneFrom = extendPath(redirectURI, "/target/auth");
875 CredentialsProvider uriSpecificCredentialsProvider = new UsernamePasswordCredentialsProvider(
876 "unknown", "none") {
877 @Override
878 public boolean get(URIish uri, CredentialItem... items)
879 throws UnsupportedCredentialItem {
880
881
882
883
884
885 if (uri.getPath().startsWith("/auth")) {
886 return testCredentials.get(uri, items);
887 }
888 return super.get(uri, items);
889 }
890 };
891 try (Repository dst = createBareRepository();
892 Transport t = Transport.open(dst, cloneFrom)) {
893 assertFalse(dst.getObjectDatabase().has(A_txt));
894 t.setCredentialsProvider(uriSpecificCredentialsProvider);
895 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
896 assertTrue(dst.getObjectDatabase().has(A_txt));
897 assertEquals(B, dst.exactRef(master).getObjectId());
898 fsck(dst, B);
899 }
900
901 List<AccessEvent> requests = getRequests();
902 assertEquals(4, requests.size());
903
904 AccessEvent redirect = requests.get(0);
905 assertEquals("GET", redirect.getMethod());
906 assertEquals(join(cloneFrom, "info/refs"), redirect.getPath());
907 assertEquals(301, redirect.getStatus());
908
909 AccessEvent info = requests.get(1);
910 assertEquals("GET", info.getMethod());
911 assertEquals(join(authURI, "info/refs"), info.getPath());
912 assertEquals(401, info.getStatus());
913
914 info = requests.get(2);
915 assertEquals("GET", info.getMethod());
916 assertEquals(join(authURI, "info/refs"), info.getPath());
917 assertEquals(1, info.getParameters().size());
918 assertEquals("git-upload-pack", info.getParameter("service"));
919 assertEquals(200, info.getStatus());
920 assertEquals("application/x-git-upload-pack-advertisement",
921 info.getResponseHeader(HDR_CONTENT_TYPE));
922 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
923
924 AccessEvent service = requests.get(3);
925 assertEquals("POST", service.getMethod());
926 assertEquals(join(authURI, "git-upload-pack"), service.getPath());
927 assertEquals(0, service.getParameters().size());
928 assertNotNull("has content-length",
929 service.getRequestHeader(HDR_CONTENT_LENGTH));
930 assertNull("not chunked",
931 service.getRequestHeader(HDR_TRANSFER_ENCODING));
932
933 assertEquals(200, service.getStatus());
934 assertEquals("application/x-git-upload-pack-result",
935 service.getResponseHeader(HDR_CONTENT_TYPE));
936 }
937
938 @Test
939 public void testInitialClone_WithAuthenticationOnPostOnly()
940 throws Exception {
941 try (Repository dst = createBareRepository();
942 Transport t = Transport.open(dst, authOnPostURI)) {
943 assertFalse(dst.getObjectDatabase().has(A_txt));
944 t.setCredentialsProvider(testCredentials);
945 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
946 assertTrue(dst.getObjectDatabase().has(A_txt));
947 assertEquals(B, dst.exactRef(master).getObjectId());
948 fsck(dst, B);
949 }
950
951 List<AccessEvent> requests = getRequests();
952 assertEquals(3, requests.size());
953
954 AccessEvent info = requests.get(0);
955 assertEquals("GET", info.getMethod());
956 assertEquals(join(authOnPostURI, "info/refs"), info.getPath());
957 assertEquals(1, info.getParameters().size());
958 assertEquals("git-upload-pack", info.getParameter("service"));
959 assertEquals(200, info.getStatus());
960 assertEquals("application/x-git-upload-pack-advertisement",
961 info.getResponseHeader(HDR_CONTENT_TYPE));
962 assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
963
964 AccessEvent service = requests.get(1);
965 assertEquals("POST", service.getMethod());
966 assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
967 assertEquals(401, service.getStatus());
968
969 service = requests.get(2);
970 assertEquals("POST", service.getMethod());
971 assertEquals(join(authOnPostURI, "git-upload-pack"), service.getPath());
972 assertEquals(0, service.getParameters().size());
973 assertNotNull("has content-length",
974 service.getRequestHeader(HDR_CONTENT_LENGTH));
975 assertNull("not chunked",
976 service.getRequestHeader(HDR_TRANSFER_ENCODING));
977
978 assertEquals(200, service.getStatus());
979 assertEquals("application/x-git-upload-pack-result",
980 service.getResponseHeader(HDR_CONTENT_TYPE));
981 }
982
983 @Test
984 public void testFetch_FewLocalCommits() throws Exception {
985
986
987 TestRepository dst = createTestRepository();
988 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
989 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
990 }
991 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
992 List<AccessEvent> cloneRequests = getRequests();
993
994
995 TestRepository.BranchBuilder b = dst.branch(master);
996 for (int i = 0; i < 4; i++)
997 b.commit().tick(3600 ).message("c" + i).create();
998
999
1000
1001 RevCommit Z;
1002 try (TestRepository<Repository> tr = new TestRepository<>(
1003 remoteRepository)) {
1004 b = tr.branch(master);
1005 Z = b.commit().message("Z").create();
1006 }
1007
1008
1009
1010 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1011 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1012 }
1013 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1014
1015 List<AccessEvent> requests = getRequests();
1016 requests.removeAll(cloneRequests);
1017 assertEquals(2, requests.size());
1018
1019 AccessEvent info = requests.get(0);
1020 assertEquals("GET", info.getMethod());
1021 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1022 assertEquals(1, info.getParameters().size());
1023 assertEquals("git-upload-pack", info.getParameter("service"));
1024 assertEquals(200, info.getStatus());
1025 assertEquals("application/x-git-upload-pack-advertisement",
1026 info.getResponseHeader(HDR_CONTENT_TYPE));
1027
1028
1029
1030 AccessEvent service = requests.get(1);
1031 assertEquals("POST", service.getMethod());
1032 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1033 assertEquals(0, service.getParameters().size());
1034 assertNotNull("has content-length",
1035 service.getRequestHeader(HDR_CONTENT_LENGTH));
1036 assertNull("not chunked",
1037 service.getRequestHeader(HDR_TRANSFER_ENCODING));
1038
1039 assertEquals(200, service.getStatus());
1040 assertEquals("application/x-git-upload-pack-result",
1041 service.getResponseHeader(HDR_CONTENT_TYPE));
1042 }
1043
1044 @Test
1045 public void testFetch_TooManyLocalCommits() throws Exception {
1046
1047
1048 TestRepository dst = createTestRepository();
1049 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1050 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1051 }
1052 assertEquals(B, dst.getRepository().exactRef(master).getObjectId());
1053 List<AccessEvent> cloneRequests = getRequests();
1054
1055
1056
1057
1058
1059 TestRepository.BranchBuilder b = dst.branch(master);
1060 for (int i = 0; i < 32 - 1; i++)
1061 b.commit().tick(3600 ).message("c" + i).create();
1062
1063
1064
1065 RevCommit Z;
1066 try (TestRepository<Repository> tr = new TestRepository<>(
1067 remoteRepository)) {
1068 b = tr.branch(master);
1069 Z = b.commit().message("Z").create();
1070 }
1071
1072
1073
1074 try (Transport t = Transport.open(dst.getRepository(), remoteURI)) {
1075 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1076 }
1077 assertEquals(Z, dst.getRepository().exactRef(master).getObjectId());
1078
1079 List<AccessEvent> requests = getRequests();
1080 requests.removeAll(cloneRequests);
1081 assertEquals(3, requests.size());
1082
1083 AccessEvent info = requests.get(0);
1084 assertEquals("GET", info.getMethod());
1085 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1086 assertEquals(1, info.getParameters().size());
1087 assertEquals("git-upload-pack", info.getParameter("service"));
1088 assertEquals(200, info.getStatus());
1089 assertEquals("application/x-git-upload-pack-advertisement", info
1090 .getResponseHeader(HDR_CONTENT_TYPE));
1091
1092
1093
1094
1095 AccessEvent service = requests.get(1);
1096 assertEquals("POST", service.getMethod());
1097 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1098 assertEquals(0, service.getParameters().size());
1099 assertNotNull("has content-length", service
1100 .getRequestHeader(HDR_CONTENT_LENGTH));
1101 assertNull("not chunked", service
1102 .getRequestHeader(HDR_TRANSFER_ENCODING));
1103
1104 assertEquals(200, service.getStatus());
1105 assertEquals("application/x-git-upload-pack-result", service
1106 .getResponseHeader(HDR_CONTENT_TYPE));
1107
1108 service = requests.get(2);
1109 assertEquals("POST", service.getMethod());
1110 assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
1111 assertEquals(0, service.getParameters().size());
1112 assertNotNull("has content-length", service
1113 .getRequestHeader(HDR_CONTENT_LENGTH));
1114 assertNull("not chunked", service
1115 .getRequestHeader(HDR_TRANSFER_ENCODING));
1116
1117 assertEquals(200, service.getStatus());
1118 assertEquals("application/x-git-upload-pack-result", service
1119 .getResponseHeader(HDR_CONTENT_TYPE));
1120 }
1121
1122 @Test
1123 public void testInitialClone_BrokenServer() throws Exception {
1124 try (Repository dst = createBareRepository();
1125 Transport t = Transport.open(dst, brokenURI)) {
1126 assertFalse(dst.getObjectDatabase().has(A_txt));
1127 try {
1128 t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
1129 fail("fetch completed despite upload-pack being broken");
1130 } catch (TransportException err) {
1131 String exp = brokenURI + ": expected"
1132 + " Content-Type application/x-git-upload-pack-result;"
1133 + " received Content-Type text/plain;charset=utf-8";
1134 assertEquals(exp, err.getMessage());
1135 }
1136 }
1137
1138 List<AccessEvent> requests = getRequests();
1139 assertEquals(2, requests.size());
1140
1141 AccessEvent info = requests.get(0);
1142 assertEquals("GET", info.getMethod());
1143 assertEquals(join(brokenURI, "info/refs"), info.getPath());
1144 assertEquals(1, info.getParameters().size());
1145 assertEquals("git-upload-pack", info.getParameter("service"));
1146 assertEquals(200, info.getStatus());
1147 assertEquals("application/x-git-upload-pack-advertisement", info
1148 .getResponseHeader(HDR_CONTENT_TYPE));
1149
1150 AccessEvent service = requests.get(1);
1151 assertEquals("POST", service.getMethod());
1152 assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
1153 assertEquals(0, service.getParameters().size());
1154 assertEquals(200, service.getStatus());
1155 assertEquals("text/plain;charset=utf-8",
1156 service.getResponseHeader(HDR_CONTENT_TYPE));
1157 }
1158
1159 @Test
1160 public void testInvalidWant() throws Exception {
1161 ObjectId id;
1162 try (ObjectInserter.Formatter formatter = new ObjectInserter.Formatter()) {
1163 id = formatter.idFor(Constants.OBJ_BLOB,
1164 "testInvalidWant".getBytes(UTF_8));
1165 }
1166
1167 try (Repository dst = createBareRepository();
1168 Transport t = Transport.open(dst, remoteURI);
1169 FetchConnection c = t.openFetch()) {
1170 Ref want = new ObjectIdRef.Unpeeled(Ref.Storage.NETWORK, id.name(),
1171 id);
1172 c.fetch(NullProgressMonitor.INSTANCE, Collections.singleton(want),
1173 Collections.<ObjectId> emptySet());
1174 fail("Server accepted want " + id.name());
1175 } catch (TransportException err) {
1176 assertEquals("want " + id.name() + " not valid", err.getMessage());
1177 }
1178 }
1179
1180 @Test
1181 public void testFetch_RefsUnreadableOnUpload() throws Exception {
1182 AppServer noRefServer = new AppServer();
1183 try {
1184 final String repoName = "refs-unreadable";
1185 RefsUnreadableInMemoryRepository badRefsRepo = new RefsUnreadableInMemoryRepository(
1186 new DfsRepositoryDescription(repoName));
1187 final TestRepository<Repository> repo = new TestRepository<>(
1188 badRefsRepo);
1189
1190 ServletContextHandler app = noRefServer.addContext("/git");
1191 GitServlet gs = new GitServlet();
1192 gs.setRepositoryResolver(new TestRepositoryResolver(repo, repoName));
1193 app.addServlet(new ServletHolder(gs), "/*");
1194 noRefServer.setUp();
1195
1196 RevBlob A2_txt = repo.blob("A2");
1197 RevCommit A2 = repo.commit().add("A2_txt", A2_txt).create();
1198 RevCommit B2 = repo.commit().parent(A2).add("A2_txt", "C2")
1199 .add("B2", "B2").create();
1200 repo.update(master, B2);
1201
1202 URIish badRefsURI = new URIish(noRefServer.getURI()
1203 .resolve(app.getContextPath() + "/" + repoName).toString());
1204
1205 try (Repository dst = createBareRepository();
1206 Transport t = Transport.open(dst, badRefsURI);
1207 FetchConnection c = t.openFetch()) {
1208
1209
1210 badRefsRepo.startFailing();
1211
1212 badRefsRepo.getRefDatabase().refresh();
1213 c.fetch(NullProgressMonitor.INSTANCE,
1214 Collections.singleton(c.getRef(master)),
1215 Collections.<ObjectId> emptySet());
1216 fail("Successfully served ref with value " + c.getRef(master));
1217 } catch (TransportException err) {
1218 assertEquals("internal server error", err.getMessage());
1219 }
1220 } finally {
1221 noRefServer.tearDown();
1222 }
1223 }
1224
1225 @Test
1226 public void testPush_NotAuthorized() throws Exception {
1227 final TestRepository src = createTestRepository();
1228 final RevBlob Q_txt = src.blob("new text");
1229 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1230 final Repository db = src.getRepository();
1231 final String dstName = Constants.R_HEADS + "new.branch";
1232
1233
1234
1235 try (Transport t = Transport.open(db, remoteURI)) {
1236 final String srcExpr = Q.name();
1237 final boolean forceUpdate = false;
1238 final String localName = null;
1239 final ObjectId oldId = null;
1240
1241 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1242 srcExpr, dstName, forceUpdate, localName, oldId);
1243 try {
1244 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1245 fail("anonymous push incorrectly accepted without error");
1246 } catch (TransportException e) {
1247 final String exp = remoteURI + ": "
1248 + JGitText.get().authenticationNotSupported;
1249 assertEquals(exp, e.getMessage());
1250 }
1251 }
1252
1253 List<AccessEvent> requests = getRequests();
1254 assertEquals(1, requests.size());
1255
1256 AccessEvent info = requests.get(0);
1257 assertEquals("GET", info.getMethod());
1258 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1259 assertEquals(1, info.getParameters().size());
1260 assertEquals("git-receive-pack", info.getParameter("service"));
1261 assertEquals(401, info.getStatus());
1262 }
1263
1264 @Test
1265 public void testPush_CreateBranch() throws Exception {
1266 final TestRepository src = createTestRepository();
1267 final RevBlob Q_txt = src.blob("new text");
1268 final RevCommit Q = src.commit().add("Q", Q_txt).create();
1269 final Repository db = src.getRepository();
1270 final String dstName = Constants.R_HEADS + "new.branch";
1271
1272 enableReceivePack();
1273
1274 try (Transport t = Transport.open(db, remoteURI)) {
1275 final String srcExpr = Q.name();
1276 final boolean forceUpdate = false;
1277 final String localName = null;
1278 final ObjectId oldId = null;
1279
1280 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1281 srcExpr, dstName, forceUpdate, localName, oldId);
1282 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1283 }
1284
1285 assertTrue(remoteRepository.getObjectDatabase().has(Q_txt));
1286 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1287 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1288 fsck(remoteRepository, Q);
1289
1290 final ReflogReader log = remoteRepository.getReflogReader(dstName);
1291 assertNotNull("has log for " + dstName, log);
1292
1293 final ReflogEntry last = log.getLastEntry();
1294 assertNotNull("has last entry", last);
1295 assertEquals(ObjectId.zeroId(), last.getOldId());
1296 assertEquals(Q, last.getNewId());
1297 assertEquals("anonymous", last.getWho().getName());
1298
1299
1300
1301
1302
1303 final String clientHost = remoteURI.getHost();
1304 assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
1305 assertEquals("push: created", last.getComment());
1306
1307 List<AccessEvent> requests = getRequests();
1308 assertEquals(2, requests.size());
1309
1310 AccessEvent info = requests.get(0);
1311 assertEquals("GET", info.getMethod());
1312 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1313 assertEquals(1, info.getParameters().size());
1314 assertEquals("git-receive-pack", info.getParameter("service"));
1315 assertEquals(200, info.getStatus());
1316 assertEquals("application/x-git-receive-pack-advertisement", info
1317 .getResponseHeader(HDR_CONTENT_TYPE));
1318
1319 AccessEvent service = requests.get(1);
1320 assertEquals("POST", service.getMethod());
1321 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1322 assertEquals(0, service.getParameters().size());
1323 assertNotNull("has content-length", service
1324 .getRequestHeader(HDR_CONTENT_LENGTH));
1325 assertNull("not chunked", service
1326 .getRequestHeader(HDR_TRANSFER_ENCODING));
1327
1328 assertEquals(200, service.getStatus());
1329 assertEquals("application/x-git-receive-pack-result", service
1330 .getResponseHeader(HDR_CONTENT_TYPE));
1331 }
1332
1333 @Test
1334 public void testPush_ChunkedEncoding() throws Exception {
1335 final TestRepository<Repository> src = createTestRepository();
1336 final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
1337 final RevCommit Q = src.commit().add("Q", Q_bin).create();
1338 final Repository db = src.getRepository();
1339 final String dstName = Constants.R_HEADS + "new.branch";
1340
1341 enableReceivePack();
1342
1343 final StoredConfig cfg = db.getConfig();
1344 cfg.setInt("core", null, "compression", 0);
1345 cfg.setInt("http", null, "postbuffer", 8 * 1024);
1346 cfg.save();
1347
1348 try (Transport t = Transport.open(db, remoteURI)) {
1349 final String srcExpr = Q.name();
1350 final boolean forceUpdate = false;
1351 final String localName = null;
1352 final ObjectId oldId = null;
1353
1354 RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
1355 srcExpr, dstName, forceUpdate, localName, oldId);
1356 t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
1357 }
1358
1359 assertTrue(remoteRepository.getObjectDatabase().has(Q_bin));
1360 assertNotNull("has " + dstName, remoteRepository.exactRef(dstName));
1361 assertEquals(Q, remoteRepository.exactRef(dstName).getObjectId());
1362 fsck(remoteRepository, Q);
1363
1364 List<AccessEvent> requests = getRequests();
1365 assertEquals(2, requests.size());
1366
1367 AccessEvent info = requests.get(0);
1368 assertEquals("GET", info.getMethod());
1369 assertEquals(join(remoteURI, "info/refs"), info.getPath());
1370 assertEquals(1, info.getParameters().size());
1371 assertEquals("git-receive-pack", info.getParameter("service"));
1372 assertEquals(200, info.getStatus());
1373 assertEquals("application/x-git-receive-pack-advertisement", info
1374 .getResponseHeader(HDR_CONTENT_TYPE));
1375
1376 AccessEvent service = requests.get(1);
1377 assertEquals("POST", service.getMethod());
1378 assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
1379 assertEquals(0, service.getParameters().size());
1380 assertNull("no content-length", service
1381 .getRequestHeader(HDR_CONTENT_LENGTH));
1382 assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
1383
1384 assertEquals(200, service.getStatus());
1385 assertEquals("application/x-git-receive-pack-result", service
1386 .getResponseHeader(HDR_CONTENT_TYPE));
1387 }
1388
1389 private void enableReceivePack() throws IOException {
1390 final StoredConfig cfg = remoteRepository.getConfig();
1391 cfg.setBoolean("http", null, "receivepack", true);
1392 cfg.save();
1393 }
1394
1395 }