HeadersEnded() bool
}
+func http2requestCancel(req *Request) <-chan struct{} { return req.Cancel }
+
var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
type http2goroutineLock uint64
}
func (sc *http2serverConn) notePanic() {
+
if http2testHookOnPanicMu != nil {
http2testHookOnPanicMu.Lock()
defer http2testHookOnPanicMu.Unlock()
go sc.writeFrameAsync(wm)
}
+// errHandlerPanicked is the error given to any callers blocked in a read from
+// Request.Body when the main goroutine panics. Since most handlers read in the
+// the main ServeHTTP goroutine, this will show up rarely.
+var http2errHandlerPanicked = errors.New("http2: handler panicked")
+
// wroteFrame is called on the serve goroutine with the result of
// whatever happened on writeFrameAsync.
func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
closeStream := http2endsStream(wm.write)
+ if _, ok := wm.write.(http2handlerPanicRST); ok {
+ sc.closeStream(st, http2errHandlerPanicked)
+ }
+
if ch := wm.done; ch != nil {
select {
case ch <- res.err:
// Run on its own goroutine.
func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) {
- defer rw.handlerDone()
-
+ didPanic := true
+ defer func() {
+ if didPanic {
+ e := recover()
+ // Same as net/http:
+ const size = 64 << 10
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ sc.writeFrameFromHandler(http2frameWriteMsg{
+ write: http2handlerPanicRST{rw.rws.stream.id},
+ stream: rw.rws.stream,
+ })
+ sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
+ return
+ }
+ rw.handlerDone()
+ }()
handler(rw, req)
+ didPanic = false
}
func http2handleHeaderListTooLong(w ResponseWriter, r *Request) {
func (w *http2responseWriter) handlerDone() {
rws := w.rws
- if rws == nil {
- panic("handlerDone called twice")
- }
rws.handlerDone = true
w.Flush()
w.rws = nil
res.Request = req
res.TLS = cc.tlsState
return res, nil
- case <-req.Cancel:
+ case <-http2requestCancel(req):
cs.abortRequestBodyWrite()
return nil, http2errRequestCanceled
+ case <-cs.peerReset:
+ return nil, cs.resetErr
case err := <-bodyCopyErrc:
if err != nil {
return nil, err
cs.bufPipe = http2pipe{b: buf}
cs.bytesRemain = res.ContentLength
res.Body = http2transportResponseBody{cs}
- go cs.awaitRequestCancel(cs.req.Cancel)
+ go cs.awaitRequestCancel(http2requestCancel(cs.req))
if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
res.Header.Del("Content-Encoding")
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
}
+// handlerPanicRST is the message sent from handler goroutines when
+// the handler panics.
+type http2handlerPanicRST struct {
+ StreamID uint32
+}
+
+func (hp http2handlerPanicRST) writeFrame(ctx http2writeContext) error {
+ return ctx.Framer().WriteRSTStream(hp.StreamID, http2ErrCodeInternal)
+}
+
func (se http2StreamError) writeFrame(ctx http2writeContext) error {
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
}
}
func TestHandlerPanicNil_h1(t *testing.T) { testHandlerPanic(t, false, h1Mode, nil) }
-func TestHandlerPanicNil_h2(t *testing.T) {
- t.Skip("known failure; golang.org/issue/13555")
- testHandlerPanic(t, false, h2Mode, nil)
-}
+func TestHandlerPanicNil_h2(t *testing.T) { testHandlerPanic(t, false, h2Mode, nil) }
func TestHandlerPanic_h1(t *testing.T) {
testHandlerPanic(t, false, h1Mode, "intentional death for testing")
}
func TestHandlerPanic_h2(t *testing.T) {
- t.Skip("known failure; golang.org/issue/13555")
testHandlerPanic(t, false, h2Mode, "intentional death for testing")
}