Bug 59310 - Content-Length of HEAD requests incorrectly computed as 0
Summary: Content-Length of HEAD requests incorrectly computed as 0
Status: RESOLVED FIXED
Alias: None
Product: Tomcat 7
Classification: Unclassified
Component: Connectors (show other bugs)
Version: 7.0.62
Hardware: All All
: P2 normal (vote)
Target Milestone: ---
Assignee: Tomcat Developers Mailing List
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2016-04-12 12:53 UTC by Tobias Oberlies
Modified: 2016-04-18 15:54 UTC (History)
0 users



Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tobias Oberlies 2016-04-12 12:53:49 UTC
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.
Comment 1 Christopher Schultz 2016-04-12 13:54:57 UTC
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();
  }
}
Comment 2 Tobias Oberlies 2016-04-12 14:01:19 UTC
(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.
Comment 3 Christopher Schultz 2016-04-12 16:01:07 UTC
Would you mind testing quickly with 8.0.33?
Comment 4 Violeta Georgieva 2016-04-13 13:40:54 UTC
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
Comment 5 Tobias Oberlies 2016-04-18 11:12:07 UTC
(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
Comment 6 Konstantin Kolinko 2016-04-18 12:07:25 UTC
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()).
Comment 7 Mark Thomas 2016-04-18 12:32:22 UTC
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.
Comment 8 Mark Thomas 2016-04-18 15:54:22 UTC
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