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: TimeoutHandler header race on timeout #9162
Comments
Could this be solved by adding a mutex? diff -r 59dbd878547f src/net/http/server.go --- a/src/net/http/server.go Tue Nov 25 08:42:00 2014 +0100 +++ b/src/net/http/server.go Wed Nov 26 10:44:33 2014 +0100 @@ -306,6 +306,7 @@ cw chunkWriter sw *switchWriter // of the bufio.Writer, for return to putBufioWriter + hmu sync.Mutex // handlerHeader is the Header that Handlers get access to, // which may be retained and mutated even after WriteHeader. // handlerHeader is copied into cw.header at WriteHeader @@ -606,6 +607,8 @@ } func (w *response) Header() Header { + w.hmu.Lock() + defer w.hmu.Unlock() if w.cw.header == nil && w.wroteHeader && !w.cw.wroteHeader { // Accessing the header between logically writing it // and physically writing it means we need to allocate @@ -640,7 +643,9 @@ w.status = code if w.calledHeader && w.cw.header == nil { + w.hmu.Lock() w.cw.header = w.handlerHeader.clone() + w.hmu.Unlock() } if cl := w.handlerHeader.get("Content-Length"); cl != "" { |
A mutex doesn't help here. The problem is that the TimeoutHandler executes the handler in a separate goroutine, which may obtain the headers via I think the solution is to change the design of the Timeout Handler to instead Hijack the connection if possible and write the timeout response headers & body by hand, if possible. There are still problems with that, though: requires the ResponseWriter to be a Hijacker, and assumes HTTP/1.n (but this isn't the first such problem), and prevents wrapper Handlers from intercepting the error and writing a prettier failure HTML body. So I think I'll change things to avoid this in the common case but I think the bigger problem is that TimeoutHandler isn't compatible with the ResponseWriter & Header design. We probably should've propagated a Context type (like https://godoc.org/golang.org/x/net/context#Context) through ServeHTTP instead, to which we could attach deadlines and do cancelation. Alas. |
Too late for Go 1.5. |
Ping @bradfitz . Is anything here going to happen for 1.6? |
Can timeoutWriter store a separate "inner" header map specifically for use by the wrapped Handler? When WriteHeader (or the first Write) occurs, if timedOut is false, the inner map can be key-wise copied (with mutex protection) into the original ResponseWriter's map. In any case, the two maps remain isolated. Some special handling would need to occur for trailers to function properly. |
CL https://golang.org/cl/17752 mentions this issue. |
The text was updated successfully, but these errors were encountered: