When responding to a HEAD request without streaming the entity and without setting the Content-Length, the Content-Length is incorrectly computed to be zero in org.apache.catalina.connector.OutputBuffer.close(). This is incorrect. The Content-Length header should be unset in this case. RFC 7230 doesn't require the Content-Length to be set on HEAD requests, but if it set it must be the size of the corresponding GET. So "Content-Length: 0" violates the standard. Computing the real size would be excessively expensive in our use case because this would require to transfer data from a backend system.
So a servlet like this will cause Tomcat to return "Content-Length: 0"? public class TestServlet extends HttpServlet { public void doHead(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { response.getWriter().close(); } }
(In reply to Christopher Schultz from comment #1) > So a servlet like this will cause Tomcat to return "Content-Length: 0"? Yes, exactly. The same happens without the close() call.
Would you mind testing quickly with 8.0.33?
Hi, If I have a servlet like this: public class TestServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter(); } protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.getWriter(); } } Both GET and HEAD requests return "Content-Length: 0". So the size of the HEAD corresponds to the size of the GET. Do you observe something else? Regards, Violeta
(In reply to Violeta Georgieva from comment #4) > If I have a servlet like this: My servlet has a different doGet method: Mine returns a response with unknown length. doHead is basically the same - but that results in an incorrect response: the response to the HEAD request includes a generated "Content-Length: 0". AFAIK, there is no reasonable way to make Tomcat not generate this header. (In reply to Christopher Schultz from comment #3) > Would you mind testing quickly with 8.0.33? I've tried with 8.0.33, and the result is the same: > HEAD /test-backend/raw/foo HTTP/1.1 > User-Agent: curl/7.29.0 > Host: localhost:8080 > Accept: */* > < HTTP/1.1 200 OK < Server: Apache-Coyote/1.1 < Content-Length: 0 < Date: Mon, 18 Apr 2016 10:59:20 GMT
You should be able to do the following in your servlet: protected void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentLength(-1); } (BTW, skipping a setContentLength() call in javax.servlet.http.HttpServlet will have the same effect. The contentLength is a numeric field that always has some value, with -1 being the default. The actual header is generated in o.a.coyote.http11.Http11Processor.prepareResponse()).
That won't work. The OutputBuffer will still set the content length to zero. resp.flushBuffer(); sort of works but adds the Transfer-Encoding header. I'm currently experimenting with a unit test to see if I can find a better solution although using flushBuffer() is likely to be the best cross-container solution.
This has been fixed so Tomcat will not send a Content-Length header for a HEAD request unless the application explicitly specifies one. The fix has been made in: - 9.0.x for 9.0.0.M5 - 8.5.x for 8.5.1 - 8.0.x for 8.0.34 - 7.0.x for 7.0.70 - 6.0.x for 6.0.46