Created attachment 28985 [details] Sampe app with sources If a client extends HttpServlet and desides to override doGet() method, resulting servlet can fail to correctly handle HEAD requests. This will happen, if client chooses to set Content-Length manually (e.g., to allow content bigger than 2Gb): resp.setHeader("Content-Length", String.valueOf(12345678900L)); and only writes actual content if it is a GET request (e.g., because it is costly operation). In such conditions, GET request will have correct "Content-Length" header, but HEAD requset will have "Content-Length" header with value 0. Sample project with sources is attached
This is actually caused by the servlet API classes which are not a part of Tomcat. Looking at servlet-api-2.5.jar, you can see this implementation of HttpServlet.doHead: protected void doHead(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException, java.io.IOException; Code: 0: new #11; //class javax/servlet/http/NoBodyResponse 3: dup 4: aload_2 5: invokespecial #12; //Method javax/servlet/http/NoBodyResponse."<init>":(Ljavax/servlet/http/HttpServletResponse;)V 8: astore_3 9: aload_0 10: aload_1 11: aload_3 12: invokevirtual #13; //Method doGet:(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)V 15: aload_3 16: invokevirtual #14; //Method javax/servlet/http/NoBodyResponse.setContentLength:()V 19: return That's roughly this Java code: protected void doHead(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { NoBodyResponse response = new NoBodyResponse(resp); doGet(req, response); response.setContentLength(); } The NoBodyResponse class is a package-protected class whose setContentLength method looks like this: void setContentLength(); Code: 0: aload_0 1: getfield #5; //Field didSetContentLength:Z 4: ifne 18 7: aload_0 8: aload_0 9: getfield #4; //Field noBody:Ljavax/servlet/http/NoBodyOutputStream; 12: invokevirtual #6; //Method javax/servlet/http/NoBodyOutputStream.getContentLength:()I 15: invokespecial #7; //Method javax/servlet/http/HttpServletResponseWrapper.setContentLength:(I)V 18: return That's roughly this Java code: void setContentLength() { if(!didSetContentLength) super.setContentLength(noBody.getContentLength()); } The field didSetContentLength is only set here: public void setContentLength(int); Code: 0: aload_0 1: iload_1 2: invokespecial #7; //Method javax/servlet/http/HttpServletResponseWrapp er.setContentLength:(I)V 5: aload_0 6: iconst_1 7: putfield #5; //Field didSetContentLength:Z 10: return Which is this: public void setContentLength(int len) { super.setContentLength(len); didSetContentLength = true; } So, since you are not calling setContentLength(int), your Content-Length header is being clobbered by the servlet API itself. Try this instead: resp.setContentLength(0); resp.setHeader("Content-Length", String.valueOf(12345678900L)); I think that will get you around this particular oversight in the API classes.
It is Tomcat's implementation of the Servlet API so this is a Tomcat bug.
Christopher, Thank you for provided workaround. I will use it. Still, I agree with Mark, that it is Tomcat's implementation of API, and it violates API specification in part of doGet() override http://docs.oracle.com/javaee/5/api/javax/servlet/http/HttpServlet.html#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
(In reply to comment #2) > It is Tomcat's implementation of the Servlet API so this is a Tomcat bug. Oh, I didn't realize that the servlet-api.jar that ships with Tomcat wasn't directly from Oracle. Obviously, Content-Length can be set in ways other than calling setContentLength (and, in fact, must be when the Content-Length exceeds 2^31-1).
Fixed in trunk and 7.0.x and will be included in 7.0.29 onwards. Proposed for 6.0.x.
Fixed in 6.0.x and will be included in 6.0.36 onwards.