diff options
author | Josh Brown <sjoshbrown@google.com> | 2022-11-01 20:51:48 +0000 |
---|---|---|
committer | Josh Brown <sjoshbrown@google.com> | 2022-11-02 16:13:22 -0400 |
commit | fe9aeb02e616863bf0f19a4beb03c0916ca57c5d (patch) | |
tree | 649a1f295d25d081f70ecae9eb21ccd8c661f001 | |
parent | 7b0a71a5e9916f0782eb24056a35b376373fdf59 (diff) | |
download | jgit-fe9aeb02e616863bf0f19a4beb03c0916ca57c5d.tar.gz jgit-fe9aeb02e616863bf0f19a4beb03c0916ca57c5d.zip |
UploadPack: Receive and parse client session-id
Before this change JGit did not support the session-id capability
implemented by native Git in UploadPack. This change implements
advertising the capability from the server and parsing the session-id
received from the client during an UploadPack operation.
Enable the transfer.advertisesid config setting to advertise the
capability from the server. The client may send a session-id capability
in response. If received, the value from this is parsed and available
via the getClientSID method on the UploadPack object.
This change does not add the capability to send a session-id from the
JGit client.
https://git-scm.com/docs/gitprotocol-capabilities#_session_idsession_id
Change-Id: Ib1b6929ff1b3a4528e767925b5e5c44b5d18182f
Signed-off-by: Josh Brown <sjoshbrown@google.com>
10 files changed, 206 insertions, 12 deletions
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java index b2a4af30ab..61b7fb619a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java @@ -98,6 +98,25 @@ public class ProtocolV0ParserTest { "f900c8326a43303685c46b279b9f70411bff1a4b")); } + @Test + public void testRecvWantsWithSessionID() + throws PackProtocolException, IOException { + PacketLineIn pckIn = formatAsPacketLine(String.join(" ", "want", + "4624442d68ee402a94364191085b77137618633e", "thin-pack", + "agent=JGit.test/0.0.1", "session-id=client-session-id", "\n"), + "want f900c8326a43303685c46b279b9f70411bff1a4b\n", + PacketLineIn.end()); + ProtocolV0Parser parser = new ProtocolV0Parser(defaultConfig()); + FetchV0Request request = parser.recvWants(pckIn); + assertTrue(request.getClientCapabilities() + .contains(GitProtocolConstants.OPTION_THIN_PACK)); + assertEquals(1, request.getClientCapabilities().size()); + assertEquals("client-session-id", request.getClientSID()); + assertThat(request.getWantIds(), + hasOnlyObjectIds("4624442d68ee402a94364191085b77137618633e", + "f900c8326a43303685c46b279b9f70411bff1a4b")); + } + /* * First round of protocol v0 negotiation. Client send wants, no * capabilities. diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java index 167b5b72c6..bab4a36cc4 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java @@ -361,4 +361,28 @@ public class ProtocolV2ParserTest { assertEquals(2, req.getRefPrefixes().size()); assertThat(req.getRefPrefixes(), hasItems("refs/for", "refs/heads")); } + + @Test + public void testFetchWithSessionID() throws IOException { + PacketLineIn pckIn = formatAsPacketLine("session-id=the.client.sid", + PacketLineIn.end()); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.start().allowFilter().done()); + FetchV2Request request = parser.parseFetchRequest(pckIn); + + assertEquals("the.client.sid", request.getClientSID()); + } + + @Test + public void testLsRefsWithSessionID() throws IOException { + PacketLineIn pckIn = formatAsPacketLine("session-id=the.client.sid", + PacketLineIn.delimiter(), PacketLineIn.end()); + + ProtocolV2Parser parser = new ProtocolV2Parser( + ConfigBuilder.getDefault()); + LsRefsV2Request req = parser.parseLsRefsRequest(pckIn); + + assertEquals("the.client.sid", req.getClientSID()); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java index 7131905850..df48afef35 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java @@ -2429,6 +2429,24 @@ public class UploadPackTest { } @Test + public void testGetSessionIDValueProtocolV0() throws Exception { + RevCommit one = remote.commit().message("1").create(); + remote.update("one", one); + + UploadPack up = new UploadPack(server); + ByteArrayInputStream send = linesAsInputStream( + "want " + one.getName() + " agent=JGit-test/1.2.3" + + " session-id=client-session-id\n", + PacketLineIn.end(), + "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n"); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + + assertEquals(up.getClientSID(), "client-session-id"); + } + + @Test public void testGetPeerAgentProtocolV2() throws Exception { server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION, null, ConfigConstants.CONFIG_KEY_VERSION, @@ -2452,6 +2470,30 @@ public class UploadPackTest { assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.4"); } + @Test + public void testGetSessionIDValueProtocolV2() throws Exception { + server.getConfig().setString(ConfigConstants.CONFIG_PROTOCOL_SECTION, + null, ConfigConstants.CONFIG_KEY_VERSION, + TransferConfig.ProtocolVersion.V2.version()); + + RevCommit one = remote.commit().message("1").create(); + remote.update("one", one); + + UploadPack up = new UploadPack(server); + up.setExtraParameters(Sets.of("version=2")); + + ByteArrayInputStream send = linesAsInputStream("command=fetch\n", + "agent=JGit-test/1.2.4\n", "session-id=client-session-id\n", + PacketLineIn.delimiter(), "want " + one.getName() + "\n", + "have 11cedf1b796d44207da702f7d420684022fc0f09\n", "done\n", + PacketLineIn.end()); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + + assertEquals(up.getClientSID(), "client-session-id"); + } + private static class RejectAllRefFilter implements RefFilter { @Override public Map<String, Ref> filter(Map<String, Ref> refs) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java index 0663c5141c..009a70b7b3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java @@ -40,6 +40,9 @@ abstract class FetchRequest { @Nullable final String agent; + @Nullable + final String clientSID; + /** * Initialize the common fields of a fetch request. * @@ -61,12 +64,15 @@ abstract class FetchRequest { * specific time, instead of depth * @param agent * agent as reported by the client in the request body + * @param clientSID + * agent as reported by the client in the request body */ FetchRequest(@NonNull Set<ObjectId> wantIds, int depth, @NonNull Set<ObjectId> clientShallowCommits, @NonNull FilterSpec filterSpec, @NonNull Set<String> clientCapabilities, int deepenSince, - @NonNull List<String> deepenNots, @Nullable String agent) { + @NonNull List<String> deepenNots, @Nullable String agent, + @Nullable String clientSID) { this.wantIds = requireNonNull(wantIds); this.depth = depth; this.clientShallowCommits = requireNonNull(clientShallowCommits); @@ -75,6 +81,7 @@ abstract class FetchRequest { this.deepenSince = deepenSince; this.deepenNots = requireNonNull(deepenNots); this.agent = agent; + this.clientSID = clientSID; } /** @@ -160,4 +167,13 @@ abstract class FetchRequest { String getAgent() { return agent; } + + /** + * @return string identifying the client session ID (as sent in the request body by the + * client) + */ + @Nullable + String getClientSID() { + return clientSID; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java index 4decb79513..ca3639d03c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java @@ -30,9 +30,11 @@ final class FetchV0Request extends FetchRequest { @NonNull Set<ObjectId> clientShallowCommits, @NonNull FilterSpec filterSpec, @NonNull Set<String> clientCapabilities, int deepenSince, - @NonNull List<String> deepenNotRefs, @Nullable String agent) { + @NonNull List<String> deepenNotRefs, @Nullable String agent, + @Nullable String clientSID) { super(wantIds, depth, clientShallowCommits, filterSpec, - clientCapabilities, deepenSince, deepenNotRefs, agent); + clientCapabilities, deepenSince, deepenNotRefs, agent, + clientSID); } static final class Builder { @@ -53,6 +55,8 @@ final class FetchV0Request extends FetchRequest { String agent; + String clientSID; + /** * @param objectId * object id received in a "want" line @@ -149,6 +153,16 @@ final class FetchV0Request extends FetchRequest { } /** + * @param clientSID + * session-id line sent by the client in the request body + * @return this builder + */ + Builder setClientSID(String clientSID) { + this.clientSID = clientSID; + return this; + } + + /** * @param filter * the filter set in a filter line * @return this builder @@ -160,7 +174,8 @@ final class FetchV0Request extends FetchRequest { FetchV0Request build() { return new FetchV0Request(wantIds, depth, clientShallowCommits, - filterSpec, clientCaps, deepenSince, deepenNots, agent); + filterSpec, clientCaps, deepenSince, deepenNots, agent, + clientSID); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java index 401744f6dd..3d4f38131c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -55,10 +55,11 @@ public final class FetchV2Request extends FetchRequest { boolean doneReceived, boolean waitForDone, @NonNull Set<String> clientCapabilities, @Nullable String agent, @NonNull List<String> serverOptions, - boolean sidebandAll, @NonNull List<String> packfileUriProtocols) { + boolean sidebandAll, @NonNull List<String> packfileUriProtocols, + @Nullable String clientSID) { super(wantIds, depth, clientShallowCommits, filterSpec, clientCapabilities, deepenSince, - deepenNots, agent); + deepenNots, agent, clientSID); this.peerHas = requireNonNull(peerHas); this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; @@ -157,6 +158,9 @@ public final class FetchV2Request extends FetchRequest { @Nullable String agent; + @Nullable + String clientSID; + final List<String> serverOptions = new ArrayList<>(); boolean sidebandAll; @@ -317,6 +321,17 @@ public final class FetchV2Request extends FetchRequest { } /** + * @param clientSIDValue + * the client-supplied session capability, without the + * leading "session-id=" + * @return this builder + */ + Builder setClientSID(@Nullable String clientSIDValue) { + clientSID = clientSIDValue; + return this; + } + + /** * Records an application-specific option supplied in a server-option * line, for later retrieval with * {@link FetchV2Request#getServerOptions}. @@ -354,7 +369,8 @@ public final class FetchV2Request extends FetchRequest { depth, filterSpec, doneReceived, waitForDone, clientCapabilities, agent, Collections.unmodifiableList(serverOptions), sidebandAll, - Collections.unmodifiableList(packfileUriProtocols)); + Collections.unmodifiableList(packfileUriProtocols), + clientSID); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java index f68d9c8135..856047ee19 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java @@ -36,17 +36,21 @@ public final class LsRefsV2Request { @Nullable private final String agent; + private final String clientSID; + @NonNull private final List<String> serverOptions; private LsRefsV2Request(List<String> refPrefixes, boolean symrefs, boolean peel, @Nullable String agent, - @NonNull List<String> serverOptions) { + @NonNull List<String> serverOptions, + @Nullable String clientSID) { this.refPrefixes = refPrefixes; this.symrefs = symrefs; this.peel = peel; this.agent = agent; this.serverOptions = requireNonNull(serverOptions); + this.clientSID = clientSID; } /** @return ref prefixes that the client requested. */ @@ -75,6 +79,16 @@ public final class LsRefsV2Request { } /** + * @return session-id as reported by the client + * + * @since 6.4 + */ + @Nullable + public String getClientSID() { + return clientSID; + } + + /** * Get application-specific options provided by the client using * --server-option. * <p> @@ -109,6 +123,8 @@ public final class LsRefsV2Request { private String agent; + private String clientSID; + private Builder() { } @@ -171,11 +187,28 @@ public final class LsRefsV2Request { return this; } + /** + * Value of a session-id line received after the command and before the + * arguments. E.g. "session-id=a.b.c" should set "a.b.c". + * + * @param value + * the client-supplied session-id capability, without leading + * "session-id=" + * @return this builder + * + * @since 6.4 + */ + public Builder setClientSID(@Nullable String value) { + clientSID = value; + return this; + } + /** @return LsRefsV2Request */ public LsRefsV2Request build() { return new LsRefsV2Request( Collections.unmodifiableList(refPrefixes), symrefs, peel, - agent, Collections.unmodifiableList(serverOptions)); + agent, Collections.unmodifiableList(serverOptions), + clientSID); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java index 21a492577f..9d055519a5 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java @@ -152,6 +152,7 @@ final class ProtocolV0Parser { FirstWant firstLine = FirstWant.fromLine(line); reqBuilder.addClientCapabilities(firstLine.getCapabilities()); reqBuilder.setAgent(firstLine.getAgent()); + reqBuilder.setClientSID(firstLine.getClientSID()); line = firstLine.getLine(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java index b38deb69c0..c4129ff4d0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -20,6 +20,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_AL import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID; import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN; import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_NOT; import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_DEEPEN_SINCE; @@ -63,10 +64,12 @@ final class ProtocolV2Parser { */ private static String consumeCapabilities(PacketLineIn pckIn, Consumer<String> serverOptionConsumer, - Consumer<String> agentConsumer) throws IOException { + Consumer<String> agentConsumer, + Consumer<String> clientSIDConsumer) throws IOException { String serverOptionPrefix = OPTION_SERVER_OPTION + '='; String agentPrefix = OPTION_AGENT + '='; + String clientSIDPrefix = OPTION_SESSION_ID + '='; String line = pckIn.readString(); while (!PacketLineIn.isDelimiter(line) && !PacketLineIn.isEnd(line)) { @@ -75,6 +78,9 @@ final class ProtocolV2Parser { .accept(line.substring(serverOptionPrefix.length())); } else if (line.startsWith(agentPrefix)) { agentConsumer.accept(line.substring(agentPrefix.length())); + } else if (line.startsWith(clientSIDPrefix)) { + clientSIDConsumer + .accept(line.substring(clientSIDPrefix.length())); } else { // Unrecognized capability. Ignore it. } @@ -108,7 +114,8 @@ final class ProtocolV2Parser { String line = consumeCapabilities(pckIn, serverOption -> reqBuilder.addServerOption(serverOption), - agent -> reqBuilder.setAgent(agent)); + agent -> reqBuilder.setAgent(agent), + clientSID -> reqBuilder.setClientSID(clientSID)); if (PacketLineIn.isEnd(line)) { return reqBuilder.build(); @@ -235,7 +242,8 @@ final class ProtocolV2Parser { String line = consumeCapabilities(pckIn, serverOption -> builder.addServerOption(serverOption), - agent -> builder.setAgent(agent)); + agent -> builder.setAgent(agent), + clientSID -> builder.setClientSID(clientSID)); if (PacketLineIn.isEnd(line)) { return builder.build(); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 65dbf12b2f..a7ce1d7c21 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -34,6 +34,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SHALLOW; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDE_BAND_64K; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SESSION_ID; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_THIN_PACK; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_WAIT_FOR_DONE; import static org.eclipse.jgit.transport.GitProtocolConstants.PACKET_ACK; @@ -1382,6 +1383,10 @@ public class UploadPack implements Closeable { : "") + OPTION_SHALLOW); caps.add(CAPABILITY_SERVER_OPTION); + if (transferConfig.isAllowReceiveClientSID()) { + caps.add(OPTION_SESSION_ID); + } + return caps; } @@ -1700,6 +1705,21 @@ public class UploadPack implements Closeable { return userAgent; } + /** + * Get the session ID if received from the client. + * + * @return The session ID if it has been received from the client. + * @since 6.4 + */ + @Nullable + public String getClientSID() { + if (currentRequest == null) { + return null; + } + + return currentRequest.getClientSID(); + } + private boolean negotiate(FetchRequest req, PackStatistics.Accumulator accumulator, PacketLineOut pckOut) |