Browse Source

Discard request HTTP bodies for status code <400

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
stable-1.3
Shawn O. Pearce 13 years ago
parent
commit
db00632db7
  1. 17
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java
  2. 2
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java
  3. 44
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java
  4. 3
      org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

17
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/GitSmartHttpTools.java

@ -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 {

2
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ReceivePackServlet.java

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

44
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/ServletUtils.java

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

3
org.eclipse.jgit.http.server/src/org/eclipse/jgit/http/server/UploadPackServlet.java

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

Loading…
Cancel
Save