]> source.dussan.org Git - jgit.git/commitdiff
UploadPack: Receive and parse client session-id 97/196497/12
authorJosh Brown <sjoshbrown@google.com>
Tue, 1 Nov 2022 20:51:48 +0000 (20:51 +0000)
committerJosh Brown <sjoshbrown@google.com>
Wed, 2 Nov 2022 20:13:22 +0000 (16:13 -0400)
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>
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV0ParserTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/ProtocolV2ParserTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackTest.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchRequest.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV0Request.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/LsRefsV2Request.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV0Parser.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java
org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java

index b2a4af30ab16638891c454831f6b8e7980550ebe..61b7fb619aabed68368486146add529631152758 100644 (file)
@@ -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.
index 167b5b72c6bf464a54e8e97358ca526cda89f8a4..bab4a36cc4bd5ce79ff266e29f49463745fa1c6e 100644 (file)
@@ -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());
+       }
 }
index 7131905850007285bd518eb8394e7ea32bbc9f4d..df48afef35e7f955ebe8695860aeb7a1a8f031be 100644 (file)
@@ -2428,6 +2428,24 @@ public class UploadPackTest {
                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 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) {
index 0663c5141cab3ed7f55c201bfc27892afefbb808..009a70b7b3e4b4f87f66658b263d0734eb79e45a 100644 (file)
@@ -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;
+       }
 }
index 4decb7951356b44a974757642d7b16bf48ff4ccd..ca3639d03c30271f450e85c111a4a135bb78153f 100644 (file)
@@ -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
@@ -148,6 +152,16 @@ final class FetchV0Request extends FetchRequest {
                        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 @@ final class FetchV0Request extends FetchRequest {
 
                FetchV0Request build() {
                        return new FetchV0Request(wantIds, depth, clientShallowCommits,
-                                       filterSpec, clientCaps, deepenSince, deepenNots, agent);
+                                       filterSpec, clientCaps, deepenSince, deepenNots, agent,
+                                       clientSID);
                }
 
        }
index 401744f6dda971a200411e4212b8fe1ade787c20..3d4f38131cdd54d90a99b90de1efbef089a7e037 100644 (file)
@@ -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;
@@ -316,6 +320,17 @@ public final class FetchV2Request extends FetchRequest {
                        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 @@ public final class FetchV2Request extends FetchRequest {
                                        depth, filterSpec, doneReceived, waitForDone, clientCapabilities,
                                        agent, Collections.unmodifiableList(serverOptions),
                                        sidebandAll,
-                                       Collections.unmodifiableList(packfileUriProtocols));
+                                       Collections.unmodifiableList(packfileUriProtocols),
+                                       clientSID);
                }
        }
 }
index f68d9c81358cf6f296b77c0cc2d82f8e5af0e0bf..856047ee1927e95abbd656a013285b1b9259767d 100644 (file)
@@ -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. */
@@ -74,6 +78,16 @@ public final class LsRefsV2Request {
                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 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);
                }
        }
 }
index 21a492577f4b3963f79733c17f825ce6a7af62de..9d055519a5ea35dde77df356d196c4b8d7ba79b9 100644 (file)
@@ -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();
                                }
                        }
index b38deb69c018724cf259cd7db823497bf9cb9ad9..c4129ff4d0b6c8816e5e71aa95d92c72be115139 100644 (file)
@@ -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();
index 65dbf12b2ff7565ae49dc0b3058be713bb028b5d..a7ce1d7c21ccde974d43fc72a8b13ddad6ac6ccc 100644 (file)
@@ -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)