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