diff options
author | Jonathan Tan <jonathantanmy@google.com> | 2018-02-22 10:24:19 -0800 |
---|---|---|
committer | Jonathan Nieder <jrn@google.com> | 2018-04-20 16:17:40 -0700 |
commit | 2661bc081340ae83d2a2ecba11994d3e8d56586b (patch) | |
tree | 81de3b39ccdd2f85a0e331d76e1662894e9b1aa8 /org.eclipse.jgit | |
parent | 75b07036928f4ef73e9a217bd7c898457e9c7120 (diff) | |
download | jgit-2661bc081340ae83d2a2ecba11994d3e8d56586b.tar.gz jgit-2661bc081340ae83d2a2ecba11994d3e8d56586b.zip |
Implement protocol v2 with no capabilities in UploadPack
Add initial support for protocol v2 of the fetch-pack/upload-pack
protocol. This protocol is described in the Git project in
"Documentation/technical/protocol-v2.txt".
This patch adds support for protocol v2 (without any capabilities) to
UploadPack. Adaptations of callers to make use of this support will
come in subsequent patches.
[jn: split from a larger patch; tweaked the API to make UploadPack
handle parsing the extra parameters and config instead of requiring
each caller to do such parsing]
Change-Id: I79399fa0dce533fdc8c1dbb6756748818cee45b0
Signed-off-by: Jonathan Tan <jonathantanmy@google.com>
Signed-off-by: Jonathan Nieder <jrn@google.com>
Diffstat (limited to 'org.eclipse.jgit')
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java | 33 | ||||
-rw-r--r-- | org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java | 103 |
2 files changed, 133 insertions, 3 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java index 3b4181d6de..7a7efd6b88 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -62,8 +62,8 @@ import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.SystemReader; /** - * The standard "transfer", "fetch", "receive", and "uploadpack" configuration - * parameters. + * The standard "transfer", "fetch", "protocol", "receive", and "uploadpack" + * configuration parameters. */ public class TransferConfig { private static final String FSCK = "fsck"; //$NON-NLS-1$ @@ -92,6 +92,33 @@ public class TransferConfig { IGNORE; } + /** + * A git configuration variable for which versions of the Git protocol to prefer. + * Used in protocol.version. + */ + enum ProtocolVersion { + V0("0"), + V2("2"); + + final String name; + + ProtocolVersion(String name) { + this.name = name; + } + + static @Nullable ProtocolVersion parse(@Nullable String name) { + if (name == null) { + return null; + } + for (ProtocolVersion v : ProtocolVersion.values()) { + if (v.name.equals(name)) { + return v; + } + } + return null; + } + } + private final boolean fetchFsck; private final boolean receiveFsck; private final String fsckSkipList; @@ -102,6 +129,7 @@ public class TransferConfig { private final boolean allowTipSha1InWant; private final boolean allowReachableSha1InWant; private final boolean allowFilter; + final @Nullable ProtocolVersion protocolVersion; final String[] hideRefs; TransferConfig(final Repository db) { @@ -156,6 +184,7 @@ public class TransferConfig { "uploadpack", "allowreachablesha1inwant", false); //$NON-NLS-1$ //$NON-NLS-2$ allowFilter = rc.getBoolean( "uploadpack", "allowfilter", false); //$NON-NLS-1$ //$NON-NLS-2$ + protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version")); hideRefs = rc.getStringList("uploadpack", null, "hiderefs"); //$NON-NLS-1$ //$NON-NLS-2$ } 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 6ae7b96d43..7f6182bf9b 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -103,6 +103,7 @@ import org.eclipse.jgit.storage.pack.PackConfig; import org.eclipse.jgit.storage.pack.PackStatistics; import org.eclipse.jgit.transport.GitProtocolConstants.MultiAck; import org.eclipse.jgit.transport.RefAdvertiser.PacketLineOutRefAdvertiser; +import org.eclipse.jgit.transport.TransferConfig.ProtocolVersion; import org.eclipse.jgit.util.io.InterruptTimer; import org.eclipse.jgit.util.io.NullOutputStream; import org.eclipse.jgit.util.io.TimeoutInputStream; @@ -112,6 +113,12 @@ import org.eclipse.jgit.util.io.TimeoutOutputStream; * Implements the server side of a fetch connection, transmitting objects. */ public class UploadPack { + // UploadPack sends these lines as the first response to a client that + // supports protocol version 2. + private static final String[] v2CapabilityAdvertisement = { + "version 2", + }; + /** Policy the server uses to validate client requests */ public static enum RequestPolicy { /** Client may only ask for objects the server advertised a reference for. */ @@ -238,6 +245,12 @@ public class UploadPack { /** Timer to manage {@link #timeout}. */ private InterruptTimer timer; + /** + * Whether the client requested to use protocol V2 through a side + * channel (such as the Git-Protocol HTTP header). + */ + private boolean clientRequestedV2; + private InputStream rawIn; private ResponseBufferedOutputStream rawOut; @@ -657,8 +670,34 @@ public class UploadPack { } /** + * Set the Extra Parameters provided by the client. + * + * <p>These are parameters passed by the client through a side channel + * such as the Git-Protocol HTTP header, to allow a client to request + * a newer response format while remaining compatible with older servers + * that do not understand different request formats. + * + * @param params + * parameters supplied by the client, split at colons or NUL + * bytes. + * @since 5.0 + */ + public void setExtraParameters(Collection<String> params) { + this.clientRequestedV2 = params.contains("version=2"); // $NON-NLS-1$ + } + + private boolean useProtocolV2() { + return ProtocolVersion.V2.equals(transferConfig.protocolVersion) + && clientRequestedV2; + } + + /** * Execute the upload task on the socket. * + * <p>If the client passed extra parameters (e.g., "version=2") through a + * side channel, the caller must call setExtraParameters first to supply + * them. + * * @param input * raw input to read client commands from. Caller must ensure the * input is buffered, otherwise read performance may suffer. @@ -699,7 +738,11 @@ public class UploadPack { pckIn = new PacketLineIn(rawIn); pckOut = new PacketLineOut(rawOut); - service(); + if (useProtocolV2()) { + serviceV2(); + } else { + service(); + } } finally { msgOut = NullOutputStream.INSTANCE; walk.close(); @@ -821,6 +864,54 @@ public class UploadPack { sendPack(accumulator); } + /* + * Returns true if this is the last command and we should tear down the + * connection. + */ + private boolean serveOneCommandV2() throws IOException { + String command; + try { + command = pckIn.readString(); + } catch (EOFException eof) { + /* EOF when awaiting command is fine */ + return true; + } + if (command == PacketLineIn.END) { + // A blank request is valid according + // to the protocol; do nothing in this + // case. + return true; + } + throw new PackProtocolException("unknown command " + command); + } + + private void serviceV2() throws IOException { + if (biDirectionalPipe) { + // Just like in service(), the capability advertisement + // is sent only if this is a bidirectional pipe. (If + // not, the client is expected to call + // sendAdvertisedRefs() on its own.) + for (String s : v2CapabilityAdvertisement) { + pckOut.writeString(s + "\n"); + } + pckOut.end(); + + while (!serveOneCommandV2()) { + // Repeat until an empty command or EOF. + } + return; + } + + try { + serveOneCommandV2(); + } finally { + while (0 < rawIn.skip(2048) || 0 <= rawIn.read()) { + // Discard until EOF. + } + rawOut.stopBuffering(); + } + } + private static Set<ObjectId> refIdSet(Collection<Ref> refs) { Set<ObjectId> ids = new HashSet<>(refs.size()); for (Ref ref : refs) { @@ -923,6 +1014,16 @@ public class UploadPack { throw fail; } + if (useProtocolV2()) { + // The equivalent in v2 is only the capabilities + // advertisement. + for (String s : v2CapabilityAdvertisement) { + adv.writeOne(s); + } + adv.end(); + return; + } + adv.init(db); adv.advertiseCapability(OPTION_INCLUDE_TAG); adv.advertiseCapability(OPTION_MULTI_ACK_DETAILED); |