View Javadoc
1   /*
2    * Copyright (C) 2010, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.http.test;
45  
46  import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
47  import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_LENGTH;
48  import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_TYPE;
49  import static org.junit.Assert.assertEquals;
50  import static org.junit.Assert.assertFalse;
51  import static org.junit.Assert.assertNotNull;
52  import static org.junit.Assert.assertNull;
53  import static org.junit.Assert.assertTrue;
54  import static org.junit.Assert.fail;
55  
56  import java.io.IOException;
57  import java.io.PrintWriter;
58  import java.net.URISyntaxException;
59  import java.util.Arrays;
60  import java.util.Collection;
61  import java.util.Collections;
62  import java.util.EnumSet;
63  import java.util.List;
64  import java.util.Map;
65  
66  import javax.servlet.DispatcherType;
67  import javax.servlet.Filter;
68  import javax.servlet.FilterChain;
69  import javax.servlet.FilterConfig;
70  import javax.servlet.ServletException;
71  import javax.servlet.ServletRequest;
72  import javax.servlet.ServletResponse;
73  import javax.servlet.http.HttpServletRequest;
74  import javax.servlet.http.HttpServletResponse;
75  
76  import org.eclipse.jetty.servlet.FilterHolder;
77  import org.eclipse.jetty.servlet.ServletContextHandler;
78  import org.eclipse.jetty.servlet.ServletHolder;
79  import org.eclipse.jgit.errors.RemoteRepositoryException;
80  import org.eclipse.jgit.errors.RepositoryNotFoundException;
81  import org.eclipse.jgit.errors.TransportException;
82  import org.eclipse.jgit.http.server.GitServlet;
83  import org.eclipse.jgit.internal.JGitText;
84  import org.eclipse.jgit.junit.TestRepository;
85  import org.eclipse.jgit.junit.TestRng;
86  import org.eclipse.jgit.junit.http.AccessEvent;
87  import org.eclipse.jgit.junit.http.HttpTestCase;
88  import org.eclipse.jgit.lib.ConfigConstants;
89  import org.eclipse.jgit.lib.Constants;
90  import org.eclipse.jgit.lib.NullProgressMonitor;
91  import org.eclipse.jgit.lib.ObjectId;
92  import org.eclipse.jgit.lib.Ref;
93  import org.eclipse.jgit.lib.ReflogEntry;
94  import org.eclipse.jgit.lib.ReflogReader;
95  import org.eclipse.jgit.lib.Repository;
96  import org.eclipse.jgit.lib.StoredConfig;
97  import org.eclipse.jgit.revwalk.RevBlob;
98  import org.eclipse.jgit.revwalk.RevCommit;
99  import org.eclipse.jgit.transport.FetchConnection;
100 import org.eclipse.jgit.transport.HttpTransport;
101 import org.eclipse.jgit.transport.RemoteRefUpdate;
102 import org.eclipse.jgit.transport.Transport;
103 import org.eclipse.jgit.transport.TransportHttp;
104 import org.eclipse.jgit.transport.URIish;
105 import org.eclipse.jgit.transport.http.HttpConnectionFactory;
106 import org.eclipse.jgit.transport.http.JDKHttpConnectionFactory;
107 import org.eclipse.jgit.transport.http.apache.HttpClientConnectionFactory;
108 import org.eclipse.jgit.transport.resolver.RepositoryResolver;
109 import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
110 import org.junit.Before;
111 import org.junit.Test;
112 import org.junit.runner.RunWith;
113 import org.junit.runners.Parameterized;
114 import org.junit.runners.Parameterized.Parameters;
115 
116 @RunWith(Parameterized.class)
117 public class SmartClientSmartServerTest extends HttpTestCase {
118 	private static final String HDR_TRANSFER_ENCODING = "Transfer-Encoding";
119 
120 	private Repository remoteRepository;
121 
122 	private URIish remoteURI;
123 
124 	private URIish brokenURI;
125 
126 	private RevBlob A_txt;
127 
128 	private RevCommit A, B;
129 
130 	@Parameters
131 	public static Collection<Object[]> data() {
132 		// run all tests with both connection factories we have
133 		return Arrays.asList(new Object[][] {
134 				{ new JDKHttpConnectionFactory() },
135 				{ new HttpClientConnectionFactory() } });
136 	}
137 
138 	public SmartClientSmartServerTest(HttpConnectionFactory cf) {
139 		HttpTransport.setConnectionFactory(cf);
140 	}
141 
142 	@Before
143 	public void setUp() throws Exception {
144 		super.setUp();
145 
146 		final TestRepository<Repository> src = createTestRepository();
147 		final String srcName = src.getRepository().getDirectory().getName();
148 		src.getRepository()
149 				.getConfig()
150 				.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, null,
151 						ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
152 
153 		ServletContextHandler app = server.addContext("/git");
154 		GitServlet gs = new GitServlet();
155 		gs.setRepositoryResolver(new RepositoryResolver<HttpServletRequest>() {
156 			public Repository open(HttpServletRequest req, String name)
157 					throws RepositoryNotFoundException,
158 					ServiceNotEnabledException {
159 				if (!name.equals(srcName))
160 					throw new RepositoryNotFoundException(name);
161 
162 				final Repository db = src.getRepository();
163 				db.incrementOpen();
164 				return db;
165 			}
166 		});
167 		app.addServlet(new ServletHolder(gs), "/*");
168 
169 		ServletContextHandler broken = server.addContext("/bad");
170 		broken.addFilter(new FilterHolder(new Filter() {
171 			public void doFilter(ServletRequest request,
172 					ServletResponse response, FilterChain chain)
173 					throws IOException, ServletException {
174 				final HttpServletResponse r = (HttpServletResponse) response;
175 				r.setContentType("text/plain");
176 				r.setCharacterEncoding("UTF-8");
177 				PrintWriter w = r.getWriter();
178 				w.print("OK");
179 				w.close();
180 			}
181 
182 			public void init(FilterConfig filterConfig) throws ServletException {
183 				//
184 			}
185 
186 			public void destroy() {
187 				//
188 			}
189 		}), "/" + srcName + "/git-upload-pack",
190 				EnumSet.of(DispatcherType.REQUEST));
191 		broken.addServlet(new ServletHolder(gs), "/*");
192 
193 		server.setUp();
194 
195 		remoteRepository = src.getRepository();
196 		remoteURI = toURIish(app, srcName);
197 		brokenURI = toURIish(broken, srcName);
198 
199 		A_txt = src.blob("A");
200 		A = src.commit().add("A_txt", A_txt).create();
201 		B = src.commit().parent(A).add("A_txt", "C").add("B", "B").create();
202 		src.update(master, B);
203 
204 		src.update("refs/garbage/a/very/long/ref/name/to/compress", B);
205 	}
206 
207 	@Test
208 	public void testListRemote() throws IOException {
209 		Repository dst = createBareRepository();
210 
211 		assertEquals("http", remoteURI.getScheme());
212 
213 		Map<String, Ref> map;
214 		Transport t = Transport.open(dst, remoteURI);
215 		try {
216 			// I didn't make up these public interface names, I just
217 			// approved them for inclusion into the code base. Sorry.
218 			// --spearce
219 			//
220 			assertTrue("isa TransportHttp", t instanceof TransportHttp);
221 			assertTrue("isa HttpTransport", t instanceof HttpTransport);
222 
223 			FetchConnection c = t.openFetch();
224 			try {
225 				map = c.getRefsMap();
226 			} finally {
227 				c.close();
228 			}
229 		} finally {
230 			t.close();
231 		}
232 
233 		assertNotNull("have map of refs", map);
234 		assertEquals(3, map.size());
235 
236 		assertNotNull("has " + master, map.get(master));
237 		assertEquals(B, map.get(master).getObjectId());
238 
239 		assertNotNull("has " + Constants.HEAD, map.get(Constants.HEAD));
240 		assertEquals(B, map.get(Constants.HEAD).getObjectId());
241 
242 		List<AccessEvent> requests = getRequests();
243 		assertEquals(1, requests.size());
244 
245 		AccessEvent info = requests.get(0);
246 		assertEquals("GET", info.getMethod());
247 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
248 		assertEquals(1, info.getParameters().size());
249 		assertEquals("git-upload-pack", info.getParameter("service"));
250 		assertEquals(200, info.getStatus());
251 		assertEquals("application/x-git-upload-pack-advertisement", info
252 				.getResponseHeader(HDR_CONTENT_TYPE));
253 		assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
254 	}
255 
256 	@Test
257 	public void testListRemote_BadName() throws IOException, URISyntaxException {
258 		Repository dst = createBareRepository();
259 		URIish uri = new URIish(this.remoteURI.toString() + ".invalid");
260 		Transport t = Transport.open(dst, uri);
261 		try {
262 			try {
263 				t.openFetch();
264 				fail("fetch connection opened");
265 			} catch (RemoteRepositoryException notFound) {
266 				assertEquals(uri + ": Git repository not found",
267 						notFound.getMessage());
268 			}
269 		} finally {
270 			t.close();
271 		}
272 
273 		List<AccessEvent> requests = getRequests();
274 		assertEquals(1, requests.size());
275 
276 		AccessEvent info = requests.get(0);
277 		assertEquals("GET", info.getMethod());
278 		assertEquals(join(uri, "info/refs"), info.getPath());
279 		assertEquals(1, info.getParameters().size());
280 		assertEquals("git-upload-pack", info.getParameter("service"));
281 		assertEquals(200, info.getStatus());
282 		assertEquals("application/x-git-upload-pack-advertisement",
283 				info.getResponseHeader(HDR_CONTENT_TYPE));
284 	}
285 
286 	@Test
287 	public void testInitialClone_Small() throws Exception {
288 		Repository dst = createBareRepository();
289 		assertFalse(dst.hasObject(A_txt));
290 
291 		Transport t = Transport.open(dst, remoteURI);
292 		try {
293 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
294 		} finally {
295 			t.close();
296 		}
297 
298 		assertTrue(dst.hasObject(A_txt));
299 		assertEquals(B, dst.getRef(master).getObjectId());
300 		fsck(dst, B);
301 
302 		List<AccessEvent> requests = getRequests();
303 		assertEquals(2, requests.size());
304 
305 		AccessEvent info = requests.get(0);
306 		assertEquals("GET", info.getMethod());
307 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
308 		assertEquals(1, info.getParameters().size());
309 		assertEquals("git-upload-pack", info.getParameter("service"));
310 		assertEquals(200, info.getStatus());
311 		assertEquals("application/x-git-upload-pack-advertisement", info
312 				.getResponseHeader(HDR_CONTENT_TYPE));
313 		assertEquals("gzip", info.getResponseHeader(HDR_CONTENT_ENCODING));
314 
315 		AccessEvent service = requests.get(1);
316 		assertEquals("POST", service.getMethod());
317 		assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
318 		assertEquals(0, service.getParameters().size());
319 		assertNotNull("has content-length", service
320 				.getRequestHeader(HDR_CONTENT_LENGTH));
321 		assertNull("not chunked", service
322 				.getRequestHeader(HDR_TRANSFER_ENCODING));
323 
324 		assertEquals(200, service.getStatus());
325 		assertEquals("application/x-git-upload-pack-result", service
326 				.getResponseHeader(HDR_CONTENT_TYPE));
327 	}
328 
329 	@Test
330 	public void testFetch_FewLocalCommits() throws Exception {
331 		// Bootstrap by doing the clone.
332 		//
333 		TestRepository dst = createTestRepository();
334 		Transport t = Transport.open(dst.getRepository(), remoteURI);
335 		try {
336 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
337 		} finally {
338 			t.close();
339 		}
340 		assertEquals(B, dst.getRepository().getRef(master).getObjectId());
341 		List<AccessEvent> cloneRequests = getRequests();
342 
343 		// Only create a few new commits.
344 		TestRepository.BranchBuilder b = dst.branch(master);
345 		for (int i = 0; i < 4; i++)
346 			b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
347 
348 		// Create a new commit on the remote.
349 		//
350 		b = new TestRepository<Repository>(remoteRepository).branch(master);
351 		RevCommit Z = b.commit().message("Z").create();
352 
353 		// Now incrementally update.
354 		//
355 		t = Transport.open(dst.getRepository(), remoteURI);
356 		try {
357 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
358 		} finally {
359 			t.close();
360 		}
361 		assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
362 
363 		List<AccessEvent> requests = getRequests();
364 		requests.removeAll(cloneRequests);
365 		assertEquals(2, requests.size());
366 
367 		AccessEvent info = requests.get(0);
368 		assertEquals("GET", info.getMethod());
369 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
370 		assertEquals(1, info.getParameters().size());
371 		assertEquals("git-upload-pack", info.getParameter("service"));
372 		assertEquals(200, info.getStatus());
373 		assertEquals("application/x-git-upload-pack-advertisement",
374 				info.getResponseHeader(HDR_CONTENT_TYPE));
375 
376 		// We should have needed one request to perform the fetch.
377 		//
378 		AccessEvent service = requests.get(1);
379 		assertEquals("POST", service.getMethod());
380 		assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
381 		assertEquals(0, service.getParameters().size());
382 		assertNotNull("has content-length",
383 				service.getRequestHeader(HDR_CONTENT_LENGTH));
384 		assertNull("not chunked",
385 				service.getRequestHeader(HDR_TRANSFER_ENCODING));
386 
387 		assertEquals(200, service.getStatus());
388 		assertEquals("application/x-git-upload-pack-result",
389 				service.getResponseHeader(HDR_CONTENT_TYPE));
390 	}
391 
392 	@Test
393 	public void testFetch_TooManyLocalCommits() throws Exception {
394 		// Bootstrap by doing the clone.
395 		//
396 		TestRepository dst = createTestRepository();
397 		Transport t = Transport.open(dst.getRepository(), remoteURI);
398 		try {
399 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
400 		} finally {
401 			t.close();
402 		}
403 		assertEquals(B, dst.getRepository().getRef(master).getObjectId());
404 		List<AccessEvent> cloneRequests = getRequests();
405 
406 		// Force enough into the local client that enumeration will
407 		// need multiple packets, but not too many to overflow and
408 		// not pick up the ACK_COMMON message.
409 		//
410 		TestRepository.BranchBuilder b = dst.branch(master);
411 		for (int i = 0; i < 32 - 1; i++)
412 			b.commit().tick(3600 /* 1 hour */).message("c" + i).create();
413 
414 		// Create a new commit on the remote.
415 		//
416 		b = new TestRepository<Repository>(remoteRepository).branch(master);
417 		RevCommit Z = b.commit().message("Z").create();
418 
419 		// Now incrementally update.
420 		//
421 		t = Transport.open(dst.getRepository(), remoteURI);
422 		try {
423 			t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
424 		} finally {
425 			t.close();
426 		}
427 		assertEquals(Z, dst.getRepository().getRef(master).getObjectId());
428 
429 		List<AccessEvent> requests = getRequests();
430 		requests.removeAll(cloneRequests);
431 		assertEquals(3, requests.size());
432 
433 		AccessEvent info = requests.get(0);
434 		assertEquals("GET", info.getMethod());
435 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
436 		assertEquals(1, info.getParameters().size());
437 		assertEquals("git-upload-pack", info.getParameter("service"));
438 		assertEquals(200, info.getStatus());
439 		assertEquals("application/x-git-upload-pack-advertisement", info
440 				.getResponseHeader(HDR_CONTENT_TYPE));
441 
442 		// We should have needed two requests to perform the fetch
443 		// due to the high number of local unknown commits.
444 		//
445 		AccessEvent service = requests.get(1);
446 		assertEquals("POST", service.getMethod());
447 		assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
448 		assertEquals(0, service.getParameters().size());
449 		assertNotNull("has content-length", service
450 				.getRequestHeader(HDR_CONTENT_LENGTH));
451 		assertNull("not chunked", service
452 				.getRequestHeader(HDR_TRANSFER_ENCODING));
453 
454 		assertEquals(200, service.getStatus());
455 		assertEquals("application/x-git-upload-pack-result", service
456 				.getResponseHeader(HDR_CONTENT_TYPE));
457 
458 		service = requests.get(2);
459 		assertEquals("POST", service.getMethod());
460 		assertEquals(join(remoteURI, "git-upload-pack"), service.getPath());
461 		assertEquals(0, service.getParameters().size());
462 		assertNotNull("has content-length", service
463 				.getRequestHeader(HDR_CONTENT_LENGTH));
464 		assertNull("not chunked", service
465 				.getRequestHeader(HDR_TRANSFER_ENCODING));
466 
467 		assertEquals(200, service.getStatus());
468 		assertEquals("application/x-git-upload-pack-result", service
469 				.getResponseHeader(HDR_CONTENT_TYPE));
470 	}
471 
472 	@Test
473 	public void testInitialClone_BrokenServer() throws Exception {
474 		Repository dst = createBareRepository();
475 		assertFalse(dst.hasObject(A_txt));
476 
477 		Transport t = Transport.open(dst, brokenURI);
478 		try {
479 			try {
480 				t.fetch(NullProgressMonitor.INSTANCE, mirror(master));
481 				fail("fetch completed despite upload-pack being broken");
482 			} catch (TransportException err) {
483 				String exp = brokenURI + ": expected"
484 						+ " Content-Type application/x-git-upload-pack-result;"
485 						+ " received Content-Type text/plain; charset=UTF-8";
486 				assertEquals(exp, err.getMessage());
487 			}
488 		} finally {
489 			t.close();
490 		}
491 
492 		List<AccessEvent> requests = getRequests();
493 		assertEquals(2, requests.size());
494 
495 		AccessEvent info = requests.get(0);
496 		assertEquals("GET", info.getMethod());
497 		assertEquals(join(brokenURI, "info/refs"), info.getPath());
498 		assertEquals(1, info.getParameters().size());
499 		assertEquals("git-upload-pack", info.getParameter("service"));
500 		assertEquals(200, info.getStatus());
501 		assertEquals("application/x-git-upload-pack-advertisement", info
502 				.getResponseHeader(HDR_CONTENT_TYPE));
503 
504 		AccessEvent service = requests.get(1);
505 		assertEquals("POST", service.getMethod());
506 		assertEquals(join(brokenURI, "git-upload-pack"), service.getPath());
507 		assertEquals(0, service.getParameters().size());
508 		assertEquals(200, service.getStatus());
509 		assertEquals("text/plain; charset=UTF-8",
510 				service.getResponseHeader(HDR_CONTENT_TYPE));
511 	}
512 
513 	@Test
514 	public void testPush_NotAuthorized() throws Exception {
515 		final TestRepository src = createTestRepository();
516 		final RevBlob Q_txt = src.blob("new text");
517 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
518 		final Repository db = src.getRepository();
519 		final String dstName = Constants.R_HEADS + "new.branch";
520 		Transport t;
521 
522 		// push anonymous shouldn't be allowed.
523 		//
524 		t = Transport.open(db, remoteURI);
525 		try {
526 			final String srcExpr = Q.name();
527 			final boolean forceUpdate = false;
528 			final String localName = null;
529 			final ObjectId oldId = null;
530 
531 			RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
532 					srcExpr, dstName, forceUpdate, localName, oldId);
533 			try {
534 				t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
535 				fail("anonymous push incorrectly accepted without error");
536 			} catch (TransportException e) {
537 				final String exp = remoteURI + ": "
538 						+ JGitText.get().authenticationNotSupported;
539 				assertEquals(exp, e.getMessage());
540 			}
541 		} finally {
542 			t.close();
543 		}
544 
545 		List<AccessEvent> requests = getRequests();
546 		assertEquals(1, requests.size());
547 
548 		AccessEvent info = requests.get(0);
549 		assertEquals("GET", info.getMethod());
550 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
551 		assertEquals(1, info.getParameters().size());
552 		assertEquals("git-receive-pack", info.getParameter("service"));
553 		assertEquals(401, info.getStatus());
554 	}
555 
556 	@Test
557 	public void testPush_CreateBranch() throws Exception {
558 		final TestRepository src = createTestRepository();
559 		final RevBlob Q_txt = src.blob("new text");
560 		final RevCommit Q = src.commit().add("Q", Q_txt).create();
561 		final Repository db = src.getRepository();
562 		final String dstName = Constants.R_HEADS + "new.branch";
563 		Transport t;
564 
565 		enableReceivePack();
566 
567 		t = Transport.open(db, remoteURI);
568 		try {
569 			final String srcExpr = Q.name();
570 			final boolean forceUpdate = false;
571 			final String localName = null;
572 			final ObjectId oldId = null;
573 
574 			RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
575 					srcExpr, dstName, forceUpdate, localName, oldId);
576 			t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
577 		} finally {
578 			t.close();
579 		}
580 
581 		assertTrue(remoteRepository.hasObject(Q_txt));
582 		assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
583 		assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
584 		fsck(remoteRepository, Q);
585 
586 		final ReflogReader log = remoteRepository.getReflogReader(dstName);
587 		assertNotNull("has log for " + dstName);
588 
589 		final ReflogEntry last = log.getLastEntry();
590 		assertNotNull("has last entry", last);
591 		assertEquals(ObjectId.zeroId(), last.getOldId());
592 		assertEquals(Q, last.getNewId());
593 		assertEquals("anonymous", last.getWho().getName());
594 
595 		// Assumption: The host name we use to contact the server should
596 		// be the server's own host name, because it should be the loopback
597 		// network interface.
598 		//
599 		final String clientHost = remoteURI.getHost();
600 		assertEquals("anonymous@" + clientHost, last.getWho().getEmailAddress());
601 		assertEquals("push: created", last.getComment());
602 
603 		List<AccessEvent> requests = getRequests();
604 		assertEquals(2, requests.size());
605 
606 		AccessEvent info = requests.get(0);
607 		assertEquals("GET", info.getMethod());
608 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
609 		assertEquals(1, info.getParameters().size());
610 		assertEquals("git-receive-pack", info.getParameter("service"));
611 		assertEquals(200, info.getStatus());
612 		assertEquals("application/x-git-receive-pack-advertisement", info
613 				.getResponseHeader(HDR_CONTENT_TYPE));
614 
615 		AccessEvent service = requests.get(1);
616 		assertEquals("POST", service.getMethod());
617 		assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
618 		assertEquals(0, service.getParameters().size());
619 		assertNotNull("has content-length", service
620 				.getRequestHeader(HDR_CONTENT_LENGTH));
621 		assertNull("not chunked", service
622 				.getRequestHeader(HDR_TRANSFER_ENCODING));
623 
624 		assertEquals(200, service.getStatus());
625 		assertEquals("application/x-git-receive-pack-result", service
626 				.getResponseHeader(HDR_CONTENT_TYPE));
627 	}
628 
629 	@Test
630 	public void testPush_ChunkedEncoding() throws Exception {
631 		final TestRepository<Repository> src = createTestRepository();
632 		final RevBlob Q_bin = src.blob(new TestRng("Q").nextBytes(128 * 1024));
633 		final RevCommit Q = src.commit().add("Q", Q_bin).create();
634 		final Repository db = src.getRepository();
635 		final String dstName = Constants.R_HEADS + "new.branch";
636 		Transport t;
637 
638 		enableReceivePack();
639 
640 		final StoredConfig cfg = db.getConfig();
641 		cfg.setInt("core", null, "compression", 0);
642 		cfg.setInt("http", null, "postbuffer", 8 * 1024);
643 		cfg.save();
644 
645 		t = Transport.open(db, remoteURI);
646 		try {
647 			final String srcExpr = Q.name();
648 			final boolean forceUpdate = false;
649 			final String localName = null;
650 			final ObjectId oldId = null;
651 
652 			RemoteRefUpdate u = new RemoteRefUpdate(src.getRepository(),
653 					srcExpr, dstName, forceUpdate, localName, oldId);
654 			t.push(NullProgressMonitor.INSTANCE, Collections.singleton(u));
655 		} finally {
656 			t.close();
657 		}
658 
659 		assertTrue(remoteRepository.hasObject(Q_bin));
660 		assertNotNull("has " + dstName, remoteRepository.getRef(dstName));
661 		assertEquals(Q, remoteRepository.getRef(dstName).getObjectId());
662 		fsck(remoteRepository, Q);
663 
664 		List<AccessEvent> requests = getRequests();
665 		assertEquals(2, requests.size());
666 
667 		AccessEvent info = requests.get(0);
668 		assertEquals("GET", info.getMethod());
669 		assertEquals(join(remoteURI, "info/refs"), info.getPath());
670 		assertEquals(1, info.getParameters().size());
671 		assertEquals("git-receive-pack", info.getParameter("service"));
672 		assertEquals(200, info.getStatus());
673 		assertEquals("application/x-git-receive-pack-advertisement", info
674 				.getResponseHeader(HDR_CONTENT_TYPE));
675 
676 		AccessEvent service = requests.get(1);
677 		assertEquals("POST", service.getMethod());
678 		assertEquals(join(remoteURI, "git-receive-pack"), service.getPath());
679 		assertEquals(0, service.getParameters().size());
680 		assertNull("no content-length", service
681 				.getRequestHeader(HDR_CONTENT_LENGTH));
682 		assertEquals("chunked", service.getRequestHeader(HDR_TRANSFER_ENCODING));
683 
684 		assertEquals(200, service.getStatus());
685 		assertEquals("application/x-git-receive-pack-result", service
686 				.getResponseHeader(HDR_CONTENT_TYPE));
687 	}
688 
689 	private void enableReceivePack() throws IOException {
690 		final StoredConfig cfg = remoteRepository.getConfig();
691 		cfg.setBoolean("http", null, "receivepack", true);
692 		cfg.save();
693 	}
694 }