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 a80990d6d..7b952e8cf 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 @@ -594,6 +594,12 @@ public void testV2CapabilitiesRefInWantNotAdvertisedIfUnallowed() throws Excepti checkUnadvertisedIfUnallowed("ref-in-want"); } + @Test + public void testV2CapabilitiesAllowSidebandAll() throws Exception { + checkAdvertisedIfAllowed("uploadpack", "allowsidebandall", "sideband-all"); + checkUnadvertisedIfUnallowed("sideband-all"); + } + @Test public void testV2CapabilitiesRefInWantNotAdvertisedIfAdvertisingForbidden() throws Exception { server.getConfig().setBoolean("uploadpack", null, "allowrefinwant", true); @@ -1874,6 +1880,13 @@ public void testV2FetchWantRefIfNotAllowed() throws Exception { "unexpected want-ref refs/heads/one"); } + @Test + public void testV2FetchSidebandAllIfNotAllowed() throws Exception { + checkV2FetchWhenNotAllowed( + "sideband-all\n", + "unexpected sideband-all"); + } + @Test public void testV2FetchWantRef() throws Exception { RevCommit one = remote.commit().message("1").create(); @@ -2057,6 +2070,51 @@ public void testV2FetchMissingShallow() throws Exception { assertTrue(client.getObjectDatabase().has(three.toObjectId())); } + @Test + public void testV2FetchSidebandAllNoPackfile() throws Exception { + RevCommit fooParent = remote.commit().message("x").create(); + RevCommit fooChild = remote.commit().message("x").parent(fooParent).create(); + RevCommit barParent = remote.commit().message("y").create(); + RevCommit barChild = remote.commit().message("y").parent(barParent).create(); + remote.update("branch1", fooChild); + remote.update("branch2", barChild); + + server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true); + + ByteArrayInputStream recvStream = uploadPackV2( + "command=fetch\n", + PacketLineIn.DELIM, + "sideband-all\n", + "want " + fooChild.toObjectId().getName() + "\n", + "want " + barChild.toObjectId().getName() + "\n", + "have " + fooParent.toObjectId().getName() + "\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + + assertThat(pckIn.readString(), is("\001acknowledgments")); + assertThat(pckIn.readString(), is("\001ACK " + fooParent.getName())); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); + } + + @Test + public void testV2FetchSidebandAllPackfile() throws Exception { + RevCommit commit = remote.commit().message("x").create(); + remote.update("master", commit); + + server.getConfig().setBoolean("uploadpack", null, "allowsidebandall", true); + + ByteArrayInputStream recvStream = uploadPackV2("command=fetch\n", + PacketLineIn.DELIM, + "want " + commit.getName() + "\n", + "sideband-all\n", + "done\n", + PacketLineIn.END); + PacketLineIn pckIn = new PacketLineIn(recvStream); + + assertThat(pckIn.readString(), is("\001packfile")); + parsePack(recvStream); + } + @Test public void testGetPeerAgentProtocolV0() throws Exception { RevCommit one = remote.commit().message("1").create(); 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 6c2426909..86574c14e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/FetchV2Request.java @@ -72,6 +72,8 @@ public final class FetchV2Request extends FetchRequest { @NonNull private final List serverOptions; + private final boolean sidebandAll; + FetchV2Request(@NonNull List peerHas, @NonNull List wantedRefs, @NonNull Set wantIds, @@ -79,7 +81,8 @@ public final class FetchV2Request extends FetchRequest { @NonNull List deepenNotRefs, int depth, @NonNull FilterSpec filterSpec, boolean doneReceived, @NonNull Set clientCapabilities, - @Nullable String agent, @NonNull List serverOptions) { + @Nullable String agent, @NonNull List serverOptions, + boolean sidebandAll) { super(wantIds, depth, clientShallowCommits, filterSpec, clientCapabilities, deepenSince, deepenNotRefs, agent); @@ -87,6 +90,7 @@ public final class FetchV2Request extends FetchRequest { this.wantedRefs = requireNonNull(wantedRefs); this.doneReceived = doneReceived; this.serverOptions = requireNonNull(serverOptions); + this.sidebandAll = sidebandAll; } /** @@ -127,6 +131,13 @@ public List getServerOptions() { return serverOptions; } + /** + * @return true if "sideband-all" was received + */ + boolean getSidebandAll() { + return sidebandAll; + } + /** @return A builder of {@link FetchV2Request}. */ static Builder builder() { return new Builder(); @@ -159,6 +170,8 @@ static final class Builder { final List serverOptions = new ArrayList<>(); + boolean sidebandAll; + private Builder() { } @@ -317,6 +330,15 @@ Builder addServerOption(@NonNull String value) { return this; } + /** + * @param value true if client sent "sideband-all" + * @return this builder + */ + Builder setSidebandAll(boolean value) { + sidebandAll = value; + return this; + } + /** * @return Initialized fetch request */ @@ -324,7 +346,8 @@ FetchV2Request build() { return new FetchV2Request(peerHas, wantedRefs, wantIds, clientShallowCommits, deepenSince, deepenNotRefs, depth, filterSpec, doneReceived, clientCapabilities, - agent, Collections.unmodifiableList(serverOptions)); + agent, Collections.unmodifiableList(serverOptions), + sidebandAll); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java index 096bb678e..e3c0bc629 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/GitProtocolConstants.java @@ -173,6 +173,14 @@ public final class GitProtocolConstants { */ public static final String OPTION_WANT_REF = "want-ref"; //$NON-NLS-1$ + /** + * The client requested that the whole response be multiplexed, with + * each non-flush and non-delim pkt prefixed by a sideband designator. + * + * @since 5.5 + */ + public static final String OPTION_SIDEBAND_ALL = "sideband-all"; //$NON-NLS-1$ + /** * The client supports atomic pushes. If this option is used, the server * will update all refs within one atomic transaction. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java index e9400919a..b07b6f69d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/PacketLineOut.java @@ -74,6 +74,8 @@ public class PacketLineOut { private boolean flushOnEnd; + private boolean useSideband; + /** * Create a new packet line writer. * @@ -97,6 +99,16 @@ public void setFlushOnEnd(boolean flushOnEnd) { this.flushOnEnd = flushOnEnd; } + /** + * When writing packet lines, use the first byte of each non-flush and + * non-delim packet as a sideband designator. + * + * @since 5.5 + */ + public void useSidebandFormat() { + this.useSideband = true; + } + /** * Write a UTF-8 encoded string as a single length-delimited packet. * @@ -139,8 +151,14 @@ public void writePacket(byte[] packet) throws IOException { * @since 4.5 */ public void writePacket(byte[] buf, int pos, int len) throws IOException { - formatLength(len + 4); - out.write(lenbuffer, 0, 4); + if (useSideband) { + formatLength(len + 5); + out.write(lenbuffer, 0, 4); + out.write(1); + } else { + formatLength(len + 4); + out.write(lenbuffer, 0, 4); + } out.write(buf, pos, len); if (log.isDebugEnabled()) { String s = RawParseUtils.decode(UTF_8, buf, pos, len); 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 caba15fc5..453be7f8c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/ProtocolV2Parser.java @@ -49,6 +49,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SERVER_OPTION; +import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_SIDEBAND_ALL; 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_WANT_REF; @@ -210,6 +211,9 @@ FetchV2Request parseFetchRequest(PacketLineIn pckIn) filterReceived = true; reqBuilder.setFilterSpec(FilterSpec.fromFilterLine( line2.substring(OPTION_FILTER.length() + 1))); + } else if (transferConfig.isAllowSidebandAll() + && line2.equals(OPTION_SIDEBAND_ALL)) { + reqBuilder.setSidebandAll(true); } else { throw new PackProtocolException(MessageFormat .format(JGitText.get().unexpectedPacketLine, line2)); 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 a3e655cd9..758d74c3f 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/TransferConfig.java @@ -131,6 +131,7 @@ static ProtocolVersion parse(@Nullable String name) { private final boolean allowTipSha1InWant; private final boolean allowReachableSha1InWant; private final boolean allowFilter; + private final boolean allowSidebandAll; final @Nullable ProtocolVersion protocolVersion; final String[] hideRefs; @@ -210,6 +211,8 @@ public TransferConfig(Config rc) { "uploadpack", "allowfilter", false); protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version")); hideRefs = rc.getStringList("uploadpack", null, "hiderefs"); + allowSidebandAll = rc.getBoolean( + "uploadpack", "allowsidebandall", false); } /** @@ -291,6 +294,14 @@ public boolean isAllowRefInWant() { return allowRefInWant; } + /** + * @return true if clients are allowed to specify a "sideband-all" line + * @since 5.5 + */ + public boolean isAllowSidebandAll() { + return allowSidebandAll; + } + /** * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured * hidden refs. 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 37ea8698c..443c98924 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -61,6 +61,7 @@ import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_NO_PROGRESS; import static org.eclipse.jgit.transport.GitProtocolConstants.OPTION_OFS_DELTA; 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_THIN_PACK; @@ -1122,6 +1123,10 @@ private void fetchV2(PacketLineOut pckOut) throws IOException { protocolV2Hook.onFetch(req); + if (req.getSidebandAll()) { + pckOut.useSidebandFormat(); + } + // TODO(ifrade): Refactor to pass around the Request object, instead of // copying data back to class fields List deepenNots = new ArrayList<>(); @@ -1263,6 +1268,7 @@ private List getV2CapabilityAdvertisement() { COMMAND_FETCH + '=' + (transferConfig.isAllowFilter() ? OPTION_FILTER + ' ' : "") + //$NON-NLS-1$ (advertiseRefInWant ? CAPABILITY_REF_IN_WANT + ' ' : "") + //$NON-NLS-1$ + (transferConfig.isAllowSidebandAll() ? OPTION_SIDEBAND_ALL + ' ' : "") + //$NON-NLS-1$ OPTION_SHALLOW); caps.add(CAPABILITY_SERVER_OPTION); return caps;