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 b2a4af30a..61b7fb619 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 void testRecvWantsWithAgent() "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 167b5b72c..bab4a36cc 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 void testLsRefsRefPrefixes() throws IOException { 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 713190585..df48afef3 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 @@ -2428,6 +2428,24 @@ public void testGetPeerAgentProtocolV0() throws Exception { assertEquals(up.getPeerUserAgent(), "JGit-test/1.2.3"); } + @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, @@ -2452,6 +2470,30 @@ public void testGetPeerAgentProtocolV2() throws Exception { 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 filter(Map 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 0663c5141..009a70b7b 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 wantIds, int depth, @NonNull Set clientShallowCommits, @NonNull FilterSpec filterSpec, @NonNull Set clientCapabilities, int deepenSince, - @NonNull List deepenNots, @Nullable String agent) { + @NonNull List 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 @@ List getDeepenNots() { 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 4decb7951..ca3639d03 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 clientShallowCommits, @NonNull FilterSpec filterSpec, @NonNull Set clientCapabilities, int deepenSince, - @NonNull List deepenNotRefs, @Nullable String agent) { + @NonNull List 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 @@ static final class Builder { String agent; + String clientSID; + /** * @param objectId * object id received in a "want" line @@ -148,6 +152,16 @@ Builder setAgent(String clientAgent) { return this; } + /** + * @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 @@ -160,7 +174,8 @@ Builder setFilterSpec(@NonNull FilterSpec filter) { 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 401744f6d..3d4f38131 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 clientCapabilities, @Nullable String agent, @NonNull List serverOptions, - boolean sidebandAll, @NonNull List packfileUriProtocols) { + boolean sidebandAll, @NonNull List 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 @@ static final class Builder { @Nullable String agent; + @Nullable + String clientSID; + final List serverOptions = new ArrayList<>(); boolean sidebandAll; @@ -316,6 +320,17 @@ Builder setAgent(@Nullable String agentValue) { return this; } + /** + * @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 @@ -354,7 +369,8 @@ FetchV2Request build() { 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 f68d9c813..856047ee1 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 serverOptions; private LsRefsV2Request(List refPrefixes, boolean symrefs, boolean peel, @Nullable String agent, - @NonNull List serverOptions) { + @NonNull List 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. */ @@ -74,6 +78,16 @@ public String getAgent() { return agent; } + /** + * @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. @@ -109,6 +123,8 @@ public static final class Builder { private String agent; + private String clientSID; + private Builder() { } @@ -171,11 +187,28 @@ public Builder setAgent(@Nullable String value) { 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 21a492577..9d055519a 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 @@ FetchV0Request recvWants(PacketLineIn pckIn) 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 b38deb69c..c4129ff4d 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_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 serverOptionConsumer, - Consumer agentConsumer) throws IOException { + Consumer agentConsumer, + Consumer 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 @@ private static String consumeCapabilities(PacketLineIn pckIn, .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 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn) 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 @@ LsRefsV2Request parseLsRefsRequest(PacketLineIn pckIn) 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 65dbf12b2..a7ce1d7c2 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_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 @@ private List getV2CapabilityAdvertisement() { : "") + OPTION_SHALLOW); caps.add(CAPABILITY_SERVER_OPTION); + if (transferConfig.isAllowReceiveClientSID()) { + caps.add(OPTION_SESSION_ID); + } + return caps; } @@ -1700,6 +1705,21 @@ public String getPeerUserAgent() { 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)