]> source.dussan.org Git - jgit.git/commitdiff
Discard request HTTP bodies for status code <400 16/4716/1
authorShawn O. Pearce <spearce@spearce.org>
Thu, 1 Dec 2011 01:36:32 +0000 (17:36 -0800)
committerShawn O. Pearce <spearce@spearce.org>
Fri, 2 Dec 2011 00:01:13 +0000 (16:01 -0800)
The HTTP RFCs require a server to fully consume the request body before
it can return a non-error status code, which is any code below 400.

JGit returns most Git level errors inside of an HTTP 200 OK response,
and sometimes this happens before the entire request was consumed from
the servlet container. In such cases the body must be skipped or read
until EOF is reached, ensuring the HTTP keep-alive semantics will work
for the next request on the same TCP connection.

HTTP status codes >= 400 may be returned without consuming the body,
and a servlet container must set "Connection: close" in the response
headers when this happens, since the state of the request body is not
well defined with an early abort.

With the introduction of sendError() in GitSmartHttpTools there are
only a handful of locations that need to worry about the request body
being consumed, so sprinkle the call in as necessary.

Change-Id: I5381e110585f780c01a764df8e27c80aacf5146e

org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

index 3d2aff174dcb63c98c2b0be56f2d25b655605af3..8bd1704bc472c90249b90cf62f46a1c982fe19a0 100644 (file)
@@ -49,11 +49,11 @@ import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import javax.servlet.ServletOutputStream;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
@@ -184,24 +184,27 @@ public class GitSmartHttpTools {
                        pck.writeString("# service=" + svc + "\n");
                        pck.end();
                        pck.writeString("ERR " + textForGit);
-                       send(res, infoRefsResultType(svc), buf.toByteArray());
+                       send(req, res, infoRefsResultType(svc), buf.toByteArray());
                } else if (isUploadPack(req)) {
                        pck.writeString("ERR " + textForGit);
-                       send(res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
+                       send(req, res, UPLOAD_PACK_RESULT_TYPE, buf.toByteArray());
                } else if (isReceivePack(req)) {
                        pck.writeString("ERR " + textForGit);
-                       send(res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
+                       send(req, res, RECEIVE_PACK_RESULT_TYPE, buf.toByteArray());
                } else {
+                       if (httpStatus < 400)
+                               ServletUtils.consumeRequestBody(req);
                        res.sendError(httpStatus);
                }
        }
 
-       private static void send(HttpServletResponse res, String type, byte[] buf)
-                       throws IOException {
+       private static void send(HttpServletRequest req, HttpServletResponse res,
+                       String type, byte[] buf) throws IOException {
+               ServletUtils.consumeRequestBody(req);
                res.setStatus(HttpServletResponse.SC_OK);
                res.setContentType(type);
                res.setContentLength(buf.length);
-               ServletOutputStream os = res.getOutputStream();
+               OutputStream os = res.getOutputStream();
                try {
                        os.write(buf);
                } finally {
index 27bee85d825d1fcb5485bbe1db1b631bfd4e08f7..c84d52b695e0032f1153501b0a92023208d84a0a 100644 (file)
@@ -52,6 +52,7 @@ import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_REQUES
 import static org.eclipse.jgit.http.server.GitSmartHttpTools.RECEIVE_PACK_RESULT_TYPE;
 import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
+import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
 import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
 import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
 
@@ -177,6 +178,7 @@ class ReceivePackServlet extends HttpServlet {
                        getServletContext().log(
                                        HttpServerText.get().internalErrorDuringReceivePack,
                                        e.getCause());
+                       consumeRequestBody(req);
                        out.close();
 
                } catch (Throwable e) {
index 21146558757177b5c75d35ddda30559c2aac4980..91fb8cce9a92a0678a87402ea663eabe24b423f6 100644 (file)
@@ -118,6 +118,50 @@ public final class ServletUtils {
                return in;
        }
 
+       /**
+        * Consume the entire request body, if one was supplied.
+        *
+        * @param req
+        *            the request whose body must be consumed.
+        */
+       public static void consumeRequestBody(HttpServletRequest req) {
+               if (0 < req.getContentLength() || isChunked(req)) {
+                       try {
+                               consumeRequestBody(req.getInputStream());
+                       } catch (IOException e) {
+                               // Ignore any errors obtaining the input stream.
+                       }
+               }
+       }
+
+       private static boolean isChunked(HttpServletRequest req) {
+               return "chunked".equals(req.getHeader("Transfer-Encoding"));
+       }
+
+       /**
+        * Consume the rest of the input stream and discard it.
+        *
+        * @param in
+        *            the stream to discard, closed if not null.
+        */
+       public static void consumeRequestBody(InputStream in) {
+               if (in == null)
+                       return;
+               try {
+                       while (0 < in.skip(2048) || 0 <= in.read()) {
+                               // Discard until EOF.
+                       }
+               } catch (IOException err) {
+                       // Discard IOException during read or skip.
+               } finally {
+                       try {
+                               in.close();
+                       } catch (IOException err) {
+                               // Discard IOException during close of input stream.
+                       }
+               }
+       }
+
        /**
         * Send a plain text response to a {@code GET} or {@code HEAD} HTTP request.
         * <p>
index 33bfff6d47a8d53741b4398e240d012ac3d7541b..15ef2c7eac6af58a11db65ffc241055653088e77 100644 (file)
@@ -52,6 +52,7 @@ import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_REQUEST
 import static org.eclipse.jgit.http.server.GitSmartHttpTools.UPLOAD_PACK_RESULT_TYPE;
 import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
 import static org.eclipse.jgit.http.server.ServletUtils.ATTRIBUTE_HANDLER;
+import static org.eclipse.jgit.http.server.ServletUtils.consumeRequestBody;
 import static org.eclipse.jgit.http.server.ServletUtils.getInputStream;
 import static org.eclipse.jgit.http.server.ServletUtils.getRepository;
 
@@ -177,6 +178,7 @@ class UploadPackServlet extends HttpServlet {
 
                } catch (UploadPackMayNotContinueException e) {
                        if (e.isOutput()) {
+                               consumeRequestBody(req);
                                out.close();
                        } else if (!rsp.isCommitted()) {
                                rsp.reset();
@@ -189,6 +191,7 @@ class UploadPackServlet extends HttpServlet {
                        getServletContext().log(
                                        HttpServerText.get().internalErrorDuringUploadPack,
                                        e.getCause());
+                       consumeRequestBody(req);
                        out.close();
 
                } catch (Throwable e) {