Created attachment 34992[details]
Exemple standalone servlet to give out HTTP 205 response
When a servlet running on Tomcat sends a response over HTTP with status 205 Reset Content, some clients hang with this response and just wait for it to "complete" after Tomcat considers it fully done.
So far I've identified two clients:
- command line program curl, version 7.52.1,
- Jersey client, version 1.19.1.
Using Tomcat 8.5.15 (latest release), but the issue was here for as long as I went back and it seems still here in Tomcat 9.
Debugging the HTTP communication shows it has to do with the fact that the response has no body (which is correct, as mandated by RFC for status 205), and no indication of content length to explicitly say that there is no body. That last part is incorrect behavior according to RFC 7231 section 6.3.6:
" Since the 205 status code implies that no additional content will be provided, a server MUST NOT generate a payload in a 205 response. In other words, a server MUST do one of the following for a 205 response: a) indicate a zero-length body for the response by including a Content-Length header field with a value of 0; b) indicate a zero-length payload for the response by including a Transfer-Encoding header field with a value of chunked and a message body consisting of a single chunk of zero-length; or, c) close the connection immediately after sending the blank line terminating the header section. "
It seems the HTTP clients I've identified, do rely on this requirement stated by RFC. Testing with servers that do add a Content-Length: 0 header or a Transfer-encoding chunked with a zero-length chunk with a status 205, these clients behave as expected. Also note, that Tomcat will typically eventually reach its keep-alive timeout and close the connection. Which is actually a valid way to end the response, and these clients do accept it when they don't reach their own timeouts. It's just the response takes by default 20 seconds to be finished, and is done with closing a perfectly re-usable connection.
Steps to reproduce:
(1) Have a clean Tomcat install version 8.5.15
(2) Deploy on it a root webapp that responds to requests with
HTTP status 205.
You can use the standalone servlet class I put in attachment.
As can be seen, it responds to all requests with status 205,
and it adds a custom header just to be sure the response comes
from this servlet.
(3) Make an HTTP request to it with curl.
Response looks like:
$ curl -v http://localhost:8080
* STATE: INIT => CONNECT handle 0x6000578f0; line 1413 (connection #-5000)
* Rebuilt URL to: http://localhost:8080/
* Added connection 0. The cache now contains 1 members
* Trying 127.0.0.1...
* TCP_NODELAY set
* STATE: CONNECT => WAITCONNECT handle 0x6000578f0; line 1466 (connection #0)
* Connected to localhost (127.0.0.1) port 8080 (#0)
* STATE: WAITCONNECT => SENDPROTOCONNECT handle 0x6000578f0; line 1583 (connection #0)
* Marked for [keep alive]: HTTP default
* STATE: SENDPROTOCONNECT => DO handle 0x6000578f0; line 1601 (connection #0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
* STATE: DO => DO_DONE handle 0x6000578f0; line 1680 (connection #0)
* STATE: DO_DONE => WAITPERFORM handle 0x6000578f0; line 1807 (connection #0)
* STATE: WAITPERFORM => PERFORM handle 0x6000578f0; line 1817 (connection #0)
* HTTP 1.1 or later with persistent connection, pipelining supported
< HTTP/1.1 205
< x-mmar-servletname: return205
< Date: Thu, 11 May 2017 15:43:26 GMT
* no chunk, no close, no size. Assume close to signal end
* Marked for [closure]: HTTP: No end-of-message indicator
<
* STATE: PERFORM => DONE handle 0x6000578f0; line 1981 (connection #0)
* multi_done
* Curl_http_done: called premature == 0
* Closing connection 0
* The cache now contains 0 members
curl hangs for a while after "Marked for [closure]: HTTP: No end-of-message indicator".
Then after 20 seconds Tomcat reaches connection
keep-alive timeout, closes the connection and curl
accepts it as a valid way to finish the response.
Proposed (naive) patch:
I have located the cause for this behavior, in class
org.apache.coyote.http11.Http11Processor
in line 1144.
Status 205 is treated the same way as 204 and 304,
that is to say no body as mandated by RFC,
but also no content length information.
The naive patch attached just removes 205 from those,
which solves the issue with the problematic clients.
However it makes it possible to add a body to a
205 response, and it becomes the webapp's author's
responsibility to not do that.
Another, possibly better, approach, could be to
have a special case for 205 only, where it
would ignore any attempt to put a content,
but it would add the header Content-Length: 0.

Thanks for the report.
This has been fixed by explicitly setting content length to zero for 205 responses.
This has been fixed in:
- 9.0.x for 9.0.0.M22 onwards
- 8.5.x for 8.5.16 onwards
- 8.0.x for 8.0.45 onwards
- 7.0.x for 7.0.79 onwards

Hi,
Thanks for the patch and the test - see r1803616.
This has been fixed in:
- 9.0.x for 9.0.0.M26 onwards
- 8.5.x for 8.5.20 onwards
- 8.0.x for 8.0.46 onwards
- 7.0.x for 7.0.80 onwards
Regards,
Violeta

This is ASF Bugzilla: the Apache Software Foundation bug system. In case
of problems with the functioning of ASF Bugzilla, please contact
bugzilla-admin@apache.org.
Please Note: this e-mail address is only for reporting problems
with ASF Bugzilla. Mail about any other subject will be silently
ignored.