New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
net/http: ResponseWriter.Write does not error after WriteTimeout nor is ErrorLog used #21389
Comments
I believe this happens because of buffering inside net/http.Server. Replace "hello" with Would you consider this a dup of #18997, or perhaps blocked on #18997? We cannot easily make Write fail without removing the buffer. I also think this would be too spammy for w.conn.server.logf. However, this looks like something we could expose easily with httptrace. |
It seems your assumption about buffering is right. Also the 132 bytes map exactly to the chunked encoded header length. I can also see the error I think being able to inspect this scenario via a server httptrace #18997 could help debugging this, but eventually having buffered |
|
I understand. Can’t I force to flush? Me as the handler implementor know when I am done writing my response, this handler could then call flush on the underlying buffer or TCPConn. However I believe I can’t get a handle on neither. And even if I could flush, shouldn’t flush then force a write out and that should cause an error which in turn should then be reported somewhere? |
You can flush: the default http.ResponseWriter implements http.Flusher: You would call Flush() after Write(), and the error would happen during your call to Flush(). However, Flush() does not return an error, so you cannot tell if the flush succeeded. This makes me sad and was likely an oversight when Flush() was added.
The flush error is not reported because it happens asynchronously to any user calls so there's no place to report it (at least not until there is a server httptrace). What did you mean by "handled"? net/http does detect the error and stops using the connection, so in that sense the error is "handled". I think the best solution I can offer right now is #18997. I would like to add an error to Flush(), but we cannot do that without breaking backwards compatibility. Happy to hear other ideas though if someone has them. |
We could start a timer at the beginning of the request to fire at WriteTimeout and cause the ResponseWriter to return Write errors, regardless of buffering status. That work, @tombergan? Too late for Go 1.10, though. Just found this while cleaning up bugs. It had arrived while I was out on leave. |
@tombergan - Did you get a chance to look at this ? |
(Comment started on #47229, moved here because I realized that was a duplicate of this one.) The Server behavior when For HTTP/1, For HTTP/2, Responding to the client immediately upon passing the write timeout (even if only by closing the connection or stream) seems reasonable. The fact that we do this for HTTP/2 seems to indicate that doing the same for HTTP/1 is acceptable. |
So while we wait for "perfect" solution, could we in meantime do the point 2) above: logging using So I would request that timeout is simply logged to the error log for now. How to surface that to the caller seems to be a much more complicated issue (and frankly, I have seen too much code doing |
I have also lost several days while trying to debug this on code I have not wrote and had no idea, that the http server had a WriteTimout set. The main problem was that it also looked, like everything is ok from the logs. |
Calling Write on a response now returns an error if the call happened after the configured server WriteTimeout. Fixes golang#21389
response.Write now returns an error if the called happened after the configured server WriteTimeout. Fixes golang#21389
Change https://go.dev/cl/406554 mentions this issue: |
Hello @neild , I was about to open an issue about the fact that a reverse-proxy with a WriteTimeout behave differently on HTTP/1 and on HTTP/2 (because the RoundTrip is eventually cancelled only in the case of HTTP/2, for the reasons you state above). This is why we have traefik/traefik#9191
Can I consider that this here issue #21389 is indeed the same problem and just track it, or should I open a new issue? |
@mpl Yeah, that sounds like the same problem to me. No need for a new issue. |
response.Write now returns an error if the called happened after the configured server WriteTimeout. Fixes golang#21389
response.Write now returns an error if the called happened after the configured server WriteTimeout. Fixes golang#21389
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?What operating system and processor architecture are you using (
go env
)?What did you do?
https://play.golang.org/p/pwjZmMKleR
Create a HTTP server with a
WriteTimeout = 1 * time.Second
and aHTTPHandler
with an artificialtime.Sleep(2 * time.Second)
and then writing out some bytes. Then start the server and run a request against this handler.What did you expect to see?
Writing a response after hitting
WriteTimeout
should at least do one out of the two options:n, err := w.Write()
http.Server.ErrorLog
(1) would indicate that no more bytes can be written, e.g. because the connection was closed.
(2) would be similar to
w.conn.server.logf("http: multiple response.WriteHeader calls")
, e.g.w.conn.server.logf("http: write attempt after write timeout")
.What did you see instead?
Neither (1) nor (2) happened.
This was confusing as I saw in my http logging middleware that response bytes have been written, but I did not understand to where/which connection; which was hard to debug.
While trying to understand how
WriteTimeout
works it seems that the http server'sResponseWriter
is not aware of the consequences. But I would like to discuss if there are options to improve the situation/inform the user.An example of the observed consequences with
WriteTimeout
set to3 * time.Second
on e.g. Heroku:The text was updated successfully, but these errors were encountered: