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 hides panic locations #27375
Comments
/cc @bradfitz |
Hello, I would like to contribute fix for this issue. I intend to change role of the two goroutines in @bradfitz is this fix OK? |
Running the inner handler in the main goroutine would also solve more serious problems like #34608. On the other hand, the current situation is quite messy: wrong panic location, panic disappearing after timeout, and broken (racy) To be clear, I'm thinking about a change of this kind in go func() {
<-ctx.Done()
tw.mu.Lock()
defer tw.mu.Unlock()
tw.timedOut = true
}
h.handler.ServeHTTP(tw, r)
tw.mu.Lock()
defer tw.mu.Unlock()
if tw.timedOut {
w.WriteHeader(StatusServiceUnavailable)
io.WriteString(w, h.errorBody())
return
}
dst := w.Header()
for k, vv := range tw.h {
dst[k] = vv
}
if !tw.wroteHeader {
tw.code = StatusOK
}
w.WriteHeader(tw.code)
w.Write(tw.wbuf.Bytes()) Writing the "timed out" response earlier (in the spawned goroutine) won't make any difference, because of buffering, and because there's no way (that I know of) to tell the server "I'm done" without returning from |
@pam4 But delaying the response is not an acceptable solution for most cases, it would be fatal if the inner handler doesn't return, and will cause big delay on response those missed the intent of a timeout handler. Also your solution could be implemented simply by checking the amount of time that took for the inner handler to process the request. I think that the approach should be printing\saving the stack trace from the go routine. |
It could be implemented without a separate goroutine, but we still want writes from the inner handler to fail on time (returning error), so that the inner handler can detect the timeout on its own, otherwise it would be completely useless.
Yes, the separate goroutine here could be changed to log the stack trace directly to the server logger in case of panic (same as here). And the main goroutine would have to run |
Yes that exactly what I meant, but this requires changes in the golang net/http package so I have created an handler that intermediate between the TimeoutHandler and the inner handler that log the panic in edition to the default logging of the panic that is called by the TimeoutHandler.
UPDATE: But in the end it's kind of does the job:
|
@CfirTsabari Thanks for the workaround. It does the job but as you noticed there are many drawbacks of this solution. The bottom line is - Lines 1769 to 1774 in 753d56d
I believe that the only way to solve it nicely (and in backward compatible way) would be to wrap errors recovered in I know there was a proposal for extending errors with stack frames and wrapping. I also know that the wrapping/unwrapping part got accepted and we have them since Go 1.13, but I don't know what happened to |
The error handling in go is a big issue #29934, yes adding a mechanism of error handling will most likely solve the hidden panic issue, but this is behind the scope of this issue IMHO. |
As far as I know,
You can get some via Context, see
Is there a specific problem you have in mind?
You should probably just call |
Yea but copying the timeout handler is also a bad practice, for example lets say someone found a bug in the net/http timeout handler and fixed, you won't be getting this bug fix.
great idea 👍
this hide the panic location, so as long as we use it only as addition to the timeout handler that kind of fine, but without this timeout handler the new handler will cause the same problem it was supposed to solve.
great idea 👍 so i have created a gist updated with your suggestions |
Of course. I just wanted to point out that it does not use any internals so you can do about the same things from outside. I certainly hope it *will* get fixed.
If you re-throw a panic from the recovering function itself, you get one more stack frame for the rethrow, but the stack trace up to the original panic location should remain unchanged. Not exactly the same thing, but it doesn't really "hide" the panic location as
Looks good 👍 |
Please answer these questions before submitting your issue. Thanks!
What version of Go are you using (
go version
)?v1.10.3
Does this issue reproduce with the latest release?
Not sure, I am finding a solution with my current version
What operating system and processor architecture are you using (
go env
)?OS: linux, arch: amd64
What did you do?
This is a demo
What did you expect to see?
I expect to see stack trace point to where cause panic (
log.Print(mm.(string))
) so that I can debug app easilyWhat did you see instead?
Stack trace point me
panic(p)
statement innet/http.timeoutHandler
(go/src/net/http/server.go:3144
). I know it make sense because that is the place that callpanic
. Are there any good solution to passing stack trace from handler insideTimeoutHandler
?The text was updated successfully, but these errors were encountered: