Teach UploadPack how to use an RPC style interface

If biDirectionalPipe is false UploadPack does not start out with
the advertisement but instead assumes it should read one block of
want/have lines, process that, and write the ACK/NAKs out.

This means it only is doing one read through the input followed by
one write to the output, which fits with the HTTP request processing
model, and any other type of RPC system.

Change-Id: Ia9f7c46ee556f996367180f15d2caa8572cdd59f
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
This commit is contained in:
Shawn O. Pearce 2009-10-07 00:10:51 -07:00
parent 2a5c8cb46c
commit e187618b6b
1 changed files with 55 additions and 5 deletions

View File

@ -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);