diff options
author | Shawn O. Pearce <spearce@spearce.org> | 2011-03-14 18:18:49 -0700 |
---|---|---|
committer | Shawn O. Pearce <spearce@spearce.org> | 2011-04-21 16:14:31 -0700 |
commit | b209671d04611ad9821cc538c46651452dea0ace (patch) | |
tree | d9f59569c5546b60ac196f373446b05d1220e528 /org.eclipse.jgit.http.test/tst | |
parent | 33e65ec6911795cf2816af1f64b5699dd898d59f (diff) | |
download | jgit-b209671d04611ad9821cc538c46651452dea0ace.tar.gz jgit-b209671d04611ad9821cc538c46651452dea0ace.zip |
Implement the no-done capability
Smart HTTP clients may request both multi_ack_detailed and no-done in
the same request to prevent the client from needing to send a "done"
line to the server in response to a server's "ACK %s ready".
For smart HTTP, this can save 1 full HTTP RPC in the fetch exchange,
improving overall latency when incrementally updating a client that
has not diverged very far from the remote repository.
Unfortuantely this capability cannot be enabled for the traditional
bi-directional connections. multi_ack_detailed has the client sending
more "have" lines at the same time that the server is creating the
"ACK %s ready" and writing out the PACK stream, resulting in some race
conditions and/or deadlock, depending on how the pipe buffers are
implemented. For very small updates, a server might actually be able
to send "ACK %s ready", then the PACK, and disconnect before the
client even finishes sending its first batch of "have" lines. This
may cause the client to fail with a broken pipe exception. To avoid
all of these potential problems, "no-done" is restricted only to the
smart HTTP variant of the protocol.
Change-Id: Ie0d0a39320202bc096fec2e97cb58e9efd061b2d
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.jgit.http.test/tst')
-rw-r--r-- | org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java | 67 |
1 files changed, 64 insertions, 3 deletions
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java index c3590a44fb..cd127cdeaf 100644 --- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java +++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java @@ -292,8 +292,6 @@ public class SmartClientSmartServerTest extends HttpTestCase { .getRequestHeader(HDR_CONTENT_LENGTH)); assertNull("not chunked", service .getRequestHeader(HDR_TRANSFER_ENCODING)); - assertNull("no compression (too small)", service - .getRequestHeader(HDR_CONTENT_ENCODING)); assertEquals(200, service.getStatus()); assertEquals("application/x-git-upload-pack-result", service @@ -301,7 +299,70 @@ public class SmartClientSmartServerTest extends HttpTestCase { } @Test - public void testFetchUpdateExisting() throws Exception { + public void testFetch_FewLocalCommits() throws Exception { + // Bootstrap by doing the clone. + // + TestRepository dst = createTestRepository(); + Transport t = Transport.open(dst.getRepository(), remoteURI); + try { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } finally { + t.close(); + } + assertEquals(B, dst.getRepository().getRef(master).getObjectId()); + List<AccessEvent> cloneRequests = getRequests(); + + // Only create a few new commits. + TestRepository.BranchBuilder b = dst.branch(master); + for (int i = 0; i < 4; i++) + b.commit().tick(3600 /* 1 hour */).message("c" + i).create(); + + // Create a new commit on the remote. + // + b = new TestRepository(remoteRepository).branch(master); + RevCommit Z = b.commit().message("Z").create(); + + // Now incrementally update. + // + t = Transport.open(dst.getRepository(), remoteURI); + try { + t.fetch(NullProgressMonitor.INSTANCE, mirror(master)); + } finally { + t.close(); + } + assertEquals(Z, dst.getRepository().getRef(master).getObjectId()); + + List<AccessEvent> requests = getRequests(); + requests.removeAll(cloneRequests); + assertEquals(2, requests.size()); + + AccessEvent info = requests.get(0); + assertEquals("GET", info.getMethod()); + assertEquals(join(remoteURI, "info/refs"), info.getPath()); + assertEquals(1, info.getParameters().size()); + assertEquals("git-upload-pack", info.getParameter("service")); + assertEquals(200, info.getStatus()); + assertEquals("application/x-git-upload-pack-advertisement", + info.getResponseHeader(HDR_CONTENT_TYPE)); + + // We should have needed one request to perform the fetch. + // + AccessEvent service = requests.get(1); + assertEquals("POST", service.getMethod()); + assertEquals(join(remoteURI, "git-upload-pack"), service.getPath()); + assertEquals(0, service.getParameters().size()); + assertNotNull("has content-length", + service.getRequestHeader(HDR_CONTENT_LENGTH)); + assertNull("not chunked", + service.getRequestHeader(HDR_TRANSFER_ENCODING)); + + assertEquals(200, service.getStatus()); + assertEquals("application/x-git-upload-pack-result", + service.getResponseHeader(HDR_CONTENT_TYPE)); + } + + @Test + public void testFetch_TooManyLocalCommits() throws Exception { // Bootstrap by doing the clone. // TestRepository dst = createTestRepository(); |