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 20d6934069b2499c9b8c73f97cb6ea0adfcd1d9a..fe2afbe9b752b0c51ab085f41c7fdea640ebff52 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,19 @@ public class UploadPack { /** Timeout in seconds to wait for client interaction. */ private int timeout; + /** + * Is the client connection a bi-directional socket or pipe? + * <p> + * If true, this class assumes it can perform multiple read and write cycles + * with the client over the input and output streams. This matches the + * functionality available with a standard TCP/IP connection, or a local + * operating system or in-memory pipe. + * <p> + * If false, this class runs in a read everything then output results mode, + * making it suitable for single round-trip systems RPCs such as HTTP. + */ + private boolean biDirectionalPipe = true; + /** Timer to manage {@link #timeout}. */ private InterruptTimer timer; @@ -198,6 +211,27 @@ public void setTimeout(final int seconds) { timeout = seconds; } + /** + * @return true if this class expects a bi-directional pipe opened between + * the client and itself. The default is true. + */ + public boolean isBiDirectionalPipe() { + return biDirectionalPipe; + } + + /** + * @param twoWay + * if true, this class will assume the socket is a fully + * bidirectional pipe between the two peers and takes advantage + * of that by first transmitting the known refs, then waiting to + * read commands. If false, this class assumes it must read the + * commands before writing output and does not perform the + * initial advertising. + */ + public void setBiDirectionalPipe(final boolean twoWay) { + biDirectionalPipe = twoWay; + } + /** * Execute the upload task on the socket. * @@ -247,7 +281,19 @@ public void upload(final InputStream input, final OutputStream output, } private void service() throws IOException { - sendAdvertisedRefs(); + if (biDirectionalPipe) + sendAdvertisedRefs(); + else { + refs = db.getAllRefs(); + for (Ref r : refs.values()) { + try { + walk.parseAny(r.getObjectId()).add(ADVERTISED); + } catch (IOException e) { + // Skip missing/corrupt objects + } + } + } + recvWants(); if (wantAll.isEmpty()) return; @@ -259,8 +305,8 @@ else if (options.contains(OPTION_MULTI_ACK)) else multiAck = MultiAck.OFF; - negotiate(); - sendPack(); + if (negotiate()) + sendPack(); } private void sendAdvertisedRefs() throws IOException { @@ -336,7 +382,7 @@ else if (o instanceof RevTag) { } } - private void negotiate() throws IOException { + private boolean negotiate() throws IOException { ObjectId last = ObjectId.zeroId(); for (;;) { String line; @@ -350,6 +396,9 @@ private void negotiate() throws IOException { if (commonBase.isEmpty() || multiAck != MultiAck.OFF) pckOut.writeString("NAK\n"); pckOut.flush(); + if (!biDirectionalPipe) + return false; + } else if (line.startsWith("have ") && line.length() == 45) { final ObjectId id = ObjectId.fromString(line.substring(5)); if (matchHave(id)) { @@ -389,7 +438,8 @@ private void negotiate() throws IOException { else if (multiAck != MultiAck.OFF) pckOut.writeString("ACK " + last.name() + "\n"); - break; + + return true; } else { throw new PackProtocolException("expected have; got " + line);