From 3b2508b51467c3cb0541c004488eaabae2842f9e Mon Sep 17 00:00:00 2001 From: Masaya Suzuki Date: Fri, 31 Mar 2017 13:54:02 -0700 Subject: [PATCH] Buffer the response until request parsing has done This is a continuation from https://git.eclipse.org/r/#/c/4716/. For a non-bidirectional request, we need to consume the request before writing any response. In UploadPack, we write "shallow"/"unshallow" responses before parsing "have" lines. This has happened not to be a problem most of the time in the smart HTTP protocol because the underlying InputStream has a 32 KiB buffer in SmartOutputStream. Change-Id: I7c61659e7c4e8bd49a8b17e2fe9be67bb32933d3 Signed-off-by: Masaya Suzuki --- .../eclipse/jgit/transport/UploadPack.java | 67 +++++++++++++++++-- 1 file changed, 62 insertions(+), 5 deletions(-) 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 58fdd2574..6b16f550d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -58,6 +58,7 @@ 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; +import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; @@ -71,6 +72,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; import org.eclipse.jgit.errors.MissingObjectException; @@ -235,7 +237,7 @@ public class UploadPack { private InputStream rawIn; - private OutputStream rawOut; + private ResponseBufferedOutputStream rawOut; private PacketLineIn pckIn; @@ -644,11 +646,10 @@ public class UploadPack { * other network connections this should be null. * @throws IOException */ - public void upload(final InputStream input, final OutputStream output, + public void upload(final InputStream input, OutputStream output, final OutputStream messages) throws IOException { try { rawIn = input; - rawOut = output; if (messages != null) msgOut = messages; @@ -656,11 +657,17 @@ public class UploadPack { final Thread caller = Thread.currentThread(); timer = new InterruptTimer(caller.getName() + "-Timer"); //$NON-NLS-1$ TimeoutInputStream i = new TimeoutInputStream(rawIn, timer); - TimeoutOutputStream o = new TimeoutOutputStream(rawOut, timer); + @SuppressWarnings("resource") + TimeoutOutputStream o = new TimeoutOutputStream(output, timer); i.setTimeout(timeout * 1000); o.setTimeout(timeout * 1000); rawIn = i; - rawOut = o; + output = o; + } + + rawOut = new ResponseBufferedOutputStream(output); + if (biDirectionalPipe) { + rawOut.stopBuffering(); } pckIn = new PacketLineIn(rawIn); @@ -714,6 +721,8 @@ public class UploadPack { private void service() throws IOException { boolean sendPack; + // If it's a non-bidi request, we need to read the entire request before + // writing a response. Buffer the response until then. try { if (biDirectionalPipe) sendAdvertisedRefs(new PacketLineOutRefAdvertiser(pckOut)); @@ -769,6 +778,8 @@ public class UploadPack { throw new UploadPackInternalServerErrorException(err); } throw err; + } finally { + rawOut.stopBuffering(); } if (sendPack) @@ -1572,4 +1583,50 @@ public class UploadPack { adv.addSymref(Constants.HEAD, head.getLeaf().getName()); } } + + private static class ResponseBufferedOutputStream extends OutputStream { + private final OutputStream rawOut; + + private OutputStream out; + @Nullable + private ByteArrayOutputStream buffer; + + ResponseBufferedOutputStream(OutputStream rawOut) { + this.rawOut = rawOut; + this.out = this.buffer = new ByteArrayOutputStream(); + } + + @Override + public void write(int b) throws IOException { + out.write(b); + } + + @Override + public void write(byte b[]) throws IOException { + out.write(b); + } + + @Override + public void write(byte b[], int off, int len) throws IOException { + out.write(b, off, len); + } + + @Override + public void flush() throws IOException { + out.flush(); + } + + @Override + public void close() throws IOException { + out.close(); + } + + void stopBuffering() throws IOException { + if (buffer != null) { + buffer.writeTo(rawOut); + buffer = null; + out = rawOut; + } + } + } }