From 8271f4588c909e66746985cd621c7db3755c301d Mon Sep 17 00:00:00 2001 From: Masaya Suzuki Date: Mon, 2 Apr 2018 15:40:40 -0700 Subject: [PATCH] Detect RawInput correctly Some endpoints allow both JSON and raw input. parseRequest selects whether to parse to JSON using a reader or to provide the raw input as an InputStream based on the request's content-type. Since v2.15-rc0~1847^2 (Discard request HTTP bodies before writing response, 2017-03-16), on endpoints that permit raw input, we call getInputStream to obtain the rest of the response body and discard it before writing the response. When the request was JSON, this produces errors from Jetty, since calling getInputStream after getReader violates the servlet API: [HTTP-66] ERROR com.google.gerrit.httpd.restapi.RestApiServlet : Error in PUT /a/plugins/reviewers.jar java.lang.IllegalStateException: READER at org.eclipse.jetty.server.Request.getInputStream(Request.java:844) at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:138) at javax.servlet.ServletRequestWrapper.getInputStream(ServletRequestWrapper.java:138) To fix it, instead of guessing whether this was a raw request based on whether the endpoint supports raw requests, use the parseRequest result to decide whether this is a raw request for which we need to discard any unconsumed content. Bug: Issue 8677 Change-Id: I1db69104f31e1c04b137d994523422a07ca5cf43 (cherry picked from commit 91136bb28ec45cbbd66e7d8aabe209a6faa7eb2a) --- .../gerrit/httpd/restapi/RestApiServlet.java | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java index d1e4e88915..4d4ef8e92c 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/restapi/RestApiServlet.java @@ -403,7 +403,11 @@ public class RestApiServlet extends HttpServlet { Type type = inputType(m); inputRequestBody = parseRequest(req, type); result = m.apply(rsrc, inputRequestBody); - consumeRawInputRequestBody(req, type); + if (inputRequestBody instanceof RawInput) { + try (InputStream is = req.getInputStream()) { + ServletUtils.consumeRequestBody(is); + } + } } else { throw new ResourceNotFoundException(); } @@ -750,7 +754,9 @@ public class RestApiServlet extends HttpServlet { br.skip(Long.MAX_VALUE); } } - } else if (rawInputRequest(req, type)) { + } + String method = req.getMethod(); + if (("PUT".equals(method) || "POST".equals(method)) && acceptsRawInput(type)) { return parseRawInput(req, type); } else if ("DELETE".equals(req.getMethod()) && hasNoBody(req)) { return null; @@ -773,19 +779,6 @@ public class RestApiServlet extends HttpServlet { } } - private void consumeRawInputRequestBody(HttpServletRequest req, Type type) throws IOException { - if (rawInputRequest(req, type)) { - try (InputStream is = req.getInputStream()) { - ServletUtils.consumeRequestBody(is); - } - } - } - - private static boolean rawInputRequest(HttpServletRequest req, Type type) { - String method = req.getMethod(); - return ("PUT".equals(method) || "POST".equals(method)) && acceptsRawInput(type); - } - private static boolean hasNoBody(HttpServletRequest req) { int len = req.getContentLength(); String type = req.getContentType();