summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java14
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java22
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java2
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java130
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java9
-rw-r--r--org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java5
-rw-r--r--org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java4
7 files changed, 150 insertions, 36 deletions
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
index b766196fdc..f667ce95a7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/InfoRefsServlet.java
@@ -44,9 +44,9 @@
package org.eclipse.jgit.http.server;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
-import static org.eclipse.jgit.http.server.ServletUtils.send;
import java.io.IOException;
+import java.io.OutputStreamWriter;
import java.util.Map;
import javax.servlet.http.HttpServlet;
@@ -69,20 +69,18 @@ class InfoRefsServlet extends HttpServlet {
final HttpServletResponse rsp) throws IOException {
// Assume a dumb client and send back the dumb client
// version of the info/refs file.
- final byte[] raw = dumbHttp(req);
rsp.setContentType(HttpSupport.TEXT_PLAIN);
rsp.setCharacterEncoding(Constants.CHARACTER_ENCODING);
- send(raw, req, rsp);
- }
- private byte[] dumbHttp(final HttpServletRequest req) throws IOException {
final Repository db = getRepository(req);
final RevWalk walk = new RevWalk(db);
final RevFlag ADVERTISED = walk.newFlag("ADVERTISED");
- final StringBuilder out = new StringBuilder();
+
+ final OutputStreamWriter out = new OutputStreamWriter(
+ new SmartOutputStream(req, rsp), Constants.CHARSET);
final RefAdvertiser adv = new RefAdvertiser() {
@Override
- protected void writeOne(final CharSequence line) {
+ protected void writeOne(final CharSequence line) throws IOException {
// Whoever decided that info/refs should use a different
// delimiter than the native git:// protocol shouldn't
// be allowed to design this sort of stuff. :-(
@@ -100,6 +98,6 @@ class InfoRefsServlet extends HttpServlet {
Map<String, Ref> refs = db.getAllRefs();
refs.remove(Constants.HEAD);
adv.send(refs);
- return out.toString().getBytes(Constants.CHARACTER_ENCODING);
+ out.close();
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
index 1a426af96d..ba8b8ab669 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
@@ -50,9 +50,7 @@ import static javax.servlet.http.HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE;
import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.io.OutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -104,11 +102,14 @@ class ReceivePackServlet extends HttpServlet {
}
final Repository db = getRepository(req);
- final ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
final ReceivePack rp = receivePackFactory.create(req, db);
rp.setBiDirectionalPipe(false);
+ rsp.setContentType(RSP_TYPE);
+
+ final SmartOutputStream out = new SmartOutputStream(req, rsp);
rp.receive(getInputStream(req), out, null);
+ out.close();
} catch (ServiceNotAuthorizedException e) {
rsp.sendError(SC_UNAUTHORIZED);
@@ -123,20 +124,5 @@ class ReceivePackServlet extends HttpServlet {
rsp.sendError(SC_INTERNAL_SERVER_ERROR);
return;
}
-
- reply(rsp, out.toByteArray());
- }
-
- private void reply(final HttpServletResponse rsp, final byte[] result)
- throws IOException {
- rsp.setContentType(RSP_TYPE);
- rsp.setContentLength(result.length);
- final OutputStream os = rsp.getOutputStream();
- try {
- os.write(result);
- os.flush();
- } finally {
- os.close();
- }
}
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
index d6b039246f..7514d7c7e7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
@@ -189,7 +189,7 @@ public final class ServletUtils {
return content;
}
- private static boolean acceptsGzipEncoding(final HttpServletRequest req) {
+ static boolean acceptsGzipEncoding(final HttpServletRequest req) {
final String accepts = req.getHeader(HDR_ACCEPT_ENCODING);
return accepts != null && 0 <= accepts.indexOf(ENCODING_GZIP);
}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
new file mode 100644
index 0000000000..00cb67ca95
--- /dev/null
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartOutputStream.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.eclipse.jgit.http.server;
+
+import static org.eclipse.jgit.http.server.ServletUtils.acceptsGzipEncoding;
+import static org.eclipse.jgit.util.HttpSupport.ENCODING_GZIP;
+import static org.eclipse.jgit.util.HttpSupport.HDR_CONTENT_ENCODING;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.GZIPOutputStream;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jgit.util.TemporaryBuffer;
+
+/**
+ * Buffers a response, trying to gzip it if the user agent supports that.
+ * <p>
+ * If the response overflows the buffer, gzip is skipped and the response is
+ * streamed to the client as its produced, most likely using HTTP/1.1 chunked
+ * encoding. This is useful for servlets that produce mixed-mode content, where
+ * smaller payloads are primarily pure text that compresses well, while much
+ * larger payloads are heavily compressed binary data. {@link UploadPackServlet}
+ * is one such servlet.
+ */
+class SmartOutputStream extends TemporaryBuffer {
+ private static final int LIMIT = 32 * 1024;
+
+ private final HttpServletRequest req;
+
+ private final HttpServletResponse rsp;
+
+ private boolean startedOutput;
+
+ SmartOutputStream(final HttpServletRequest req,
+ final HttpServletResponse rsp) {
+ super(LIMIT);
+ this.req = req;
+ this.rsp = rsp;
+ }
+
+ @Override
+ protected OutputStream overflow() throws IOException {
+ startedOutput = true;
+ return rsp.getOutputStream();
+ }
+
+ public void close() throws IOException {
+ super.close();
+
+ if (!startedOutput) {
+ // If output hasn't started yet, the entire thing fit into our
+ // buffer. Try to use a proper Content-Length header, and also
+ // deflate the response with gzip if it will be smaller.
+ TemporaryBuffer out = this;
+
+ if (256 < out.length() && acceptsGzipEncoding(req)) {
+ TemporaryBuffer gzbuf = new TemporaryBuffer.Heap(LIMIT);
+ try {
+ GZIPOutputStream gzip = new GZIPOutputStream(gzbuf);
+ out.writeTo(gzip, null);
+ gzip.close();
+ if (gzbuf.length() < out.length()) {
+ out = gzbuf;
+ rsp.setHeader(HDR_CONTENT_ENCODING, ENCODING_GZIP);
+ }
+ } catch (IOException err) {
+ // Most likely caused by overflowing the buffer, meaning
+ // its larger if it were compressed. Discard compressed
+ // copy and use the original.
+ }
+ }
+
+ // The Content-Length cannot overflow when cast to an int, our
+ // hardcoded LIMIT constant above assures us we wouldn't store
+ // more than 2 GiB of content in memory.
+ rsp.setContentLength((int) out.length());
+ final OutputStream os = rsp.getOutputStream();
+ try {
+ out.writeTo(os, null);
+ os.flush();
+ } finally {
+ os.close();
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
index 96716d0703..94e2e7f6f7 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/SmartServiceInfoRefs.java
@@ -46,9 +46,7 @@ package org.eclipse.jgit.http.server;
import static javax.servlet.http.HttpServletResponse.SC_FORBIDDEN;
import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
-import static org.eclipse.jgit.http.server.ServletUtils.send;
-import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.Filter;
@@ -90,13 +88,14 @@ abstract class SmartServiceInfoRefs implements Filter {
final HttpServletResponse rsp = (HttpServletResponse) response;
try {
final Repository db = getRepository(req);
- final ByteArrayOutputStream buf = new ByteArrayOutputStream();
+ rsp.setContentType("application/x-" + svc + "-advertisement");
+
+ final SmartOutputStream buf = new SmartOutputStream(req, rsp);
final PacketLineOut out = new PacketLineOut(buf);
out.writeString("# service=" + svc + "\n");
out.end();
advertise(req, db, new PacketLineOutRefAdvertiser(out));
- rsp.setContentType("application/x-" + svc + "-advertisement");
- send(buf.toByteArray(), req, rsp);
+ buf.close();
} catch (ServiceNotAuthorizedException e) {
rsp.sendError(SC_UNAUTHORIZED);
diff --git a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
index dc874fa5f2..8de5f06781 100644
--- a/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
+++ b/org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java
@@ -106,7 +106,10 @@ class UploadPackServlet extends HttpServlet {
final UploadPack up = uploadPackFactory.create(req, db);
up.setBiDirectionalPipe(false);
rsp.setContentType(RSP_TYPE);
- up.upload(getInputStream(req), rsp.getOutputStream(), null);
+
+ final SmartOutputStream out = new SmartOutputStream(req, rsp);
+ up.upload(getInputStream(req), out, null);
+ out.close();
} catch (ServiceNotAuthorizedException e) {
rsp.reset();
diff --git a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
index fd78bb4515..f7b3bdb203 100644
--- a/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
+++ b/org.eclipse.jgit.http.test/tst/org/eclipse/jgit/http/test/SmartClientSmartServerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2009-2010, Google Inc.
+ * Copyright (C) 2010, Google Inc.
* and other copyright owners as documented in the project's IP log.
*
* This program and the accompanying materials are made available
@@ -253,8 +253,6 @@ public class SmartClientSmartServerTest extends HttpTestCase {
assertEquals(200, service.getStatus());
assertEquals("application/x-git-upload-pack-result", service
.getResponseHeader(HDR_CONTENT_TYPE));
- assertNull("no compression (never compressed)", service
- .getResponseHeader(HDR_CONTENT_ENCODING));
}
public void testFetchUpdateExisting() throws Exception {