Join GitHub today
net/http: half-closed connection triggers request cancellation #18527
Please answer these questions before submitting your issue. Thanks!
What did you do?
What did you expect to see?
A client that sends a request followed immediately by a FIN can read a response from the server.
What did you see instead?
The half-close is detected and the request is cancelled immediately.
Does this issue reproduce with the latest release (go1.7.4)?
We have an internal client that fires-and-forget's requests to a service. Those requests may take some time to process, and the server uses the request's contexts for timeouts unrelated to the client connection. AFAIK there is no way to prevent the "client gone" detection from canceling the context.
Other web servers do support this behavior, but it's not covered by any RFC: http://mailman.nginx.org/pipermail/nginx/2008-September/007388.html
Supporting clients that want to fire-and-forget requests could be added by ignoring the read
What do you mean by "fire-and-forget"? It goes out of its way to do a
Thanks for the nginx link.
@mnot, is there any HTTP RFC which says how clients and/or servers should behave here with respect to half-closed (closed by client) requests?
The relevant part of the specs is:
... but that doesn't address this situation. If you think it should, log an issue in https://github.com/httpwg/http11bis
referenced this issue
Jan 6, 2017
I described the issue as a problem with a client that depends on unspecified behavior hoping to clarify the issues but it seems to have had the opposite effect. Clients should not depend on unspecified behavior and we will fix our client to not half-close connections.
However, from a server's perspective normal browsers behavior can look identical to the fire-and-forget client I described above: a user closes their browser soon after initiating request for an asset. The server sees a connection, a valid request, and a FIN packet in very short succession.
Deciding to abort the request/response isn't so straight forward. A pageview endpoint would likely want to proceed without cancellation so that the page load is recorded. A large asset endpoint probably wants to drop the response as soon as the FIN is detected.
Because the HTTP spec does not cover handling a client that terminates the TCP connection unexpectedly, it seems that this it is left up to the library and application developers. I'm in favor of net/http deciding that an early EOF from the client triggers a cancellation of the request's context since it is ultimately up to the author of the handler to use the context or not.
But a handler that uses the request's context can behave differently between 1.7 and 1.8 due to EOF triggered cancellations. This probably won't be noticed by most but pageview style handlers will be affected, along with proxying handlers that want to let the backend decide how to handle an early client EOF.
I'm in favor of closing this issue because, even if there were a non-hacky way to support the 1.7 behavior, as @bradfitz said it's too late in the release cycle to do anything about it. The few handlers that are effected by this will have to be update to be more explicit about how they handle context cancellation, which seems fine.
one last thought: it may be benifitial to use a custom
I went to add support for not canceling contexts for HTTP/1.0 POST/PUT requests, wrote a test I expected to fail but then I discovered Go didn't read the request body which confused me for a second until I also discovered that HTTP/1.0 always required a Content-Length (httpwg/http-core#22 (comment)).
So I'm inclined to do nothing here, at least for now.
@benburkert, I agree that some Handlers may want to continue processing after the client has gone away. But like you said, they can use a different Context instead.
We can do the release candidate and see how things go. We can revisit this if there are problems.
I encountered a variation on this bug today. It turns out a big-name CDN actually does do half-close on http/1.1.
I'm not sure how you'd detect a half-closed TCP connection. We could assume that the request end being closed when