Skip to content

Commit 74bd44b

Browse files
committed
http2: catch panics server-side, respect RST_STREAM on the Transport side
Tests in https://go-review.googlesource.com/#/c/17683/5/src/net/http/serve_test.go: TestHandlerPanicNil_h2 and TestHandlerPanic_h2, to be re-enabled after the copy to the main repo. Updates golang/go#13555 (fixes the bug after the copy to the main repo) Change-Id: I7aa3e55fb21b576ea4f94c7ed41d1ebd750ef951 Reviewed-on: https://go-review.googlesource.com/17823 Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 0d2c2e1 commit 74bd44b

File tree

3 files changed

+41
-5
lines changed

3 files changed

+41
-5
lines changed

http2/server.go

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import (
4747
"net"
4848
"net/http"
4949
"net/url"
50+
"runtime"
5051
"strconv"
5152
"strings"
5253
"sync"
@@ -615,6 +616,7 @@ func (sc *serverConn) stopShutdownTimer() {
615616
}
616617

617618
func (sc *serverConn) notePanic() {
619+
// Note: this is for serverConn.serve panicking, not http.Handler code.
618620
if testHookOnPanicMu != nil {
619621
testHookOnPanicMu.Lock()
620622
defer testHookOnPanicMu.Unlock()
@@ -837,6 +839,11 @@ func (sc *serverConn) startFrameWrite(wm frameWriteMsg) {
837839
go sc.writeFrameAsync(wm)
838840
}
839841

842+
// errHandlerPanicked is the error given to any callers blocked in a read from
843+
// Request.Body when the main goroutine panics. Since most handlers read in the
844+
// the main ServeHTTP goroutine, this will show up rarely.
845+
var errHandlerPanicked = errors.New("http2: handler panicked")
846+
840847
// wroteFrame is called on the serve goroutine with the result of
841848
// whatever happened on writeFrameAsync.
842849
func (sc *serverConn) wroteFrame(res frameWriteResult) {
@@ -851,6 +858,10 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) {
851858

852859
closeStream := endsStream(wm.write)
853860

861+
if _, ok := wm.write.(handlerPanicRST); ok {
862+
sc.closeStream(st, errHandlerPanicked)
863+
}
864+
854865
// Reply (if requested) to the blocked ServeHTTP goroutine.
855866
if ch := wm.done; ch != nil {
856867
select {
@@ -1530,9 +1541,25 @@ func (sc *serverConn) newWriterAndRequest() (*responseWriter, *http.Request, err
15301541

15311542
// Run on its own goroutine.
15321543
func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler func(http.ResponseWriter, *http.Request)) {
1533-
defer rw.handlerDone()
1534-
// TODO: catch panics like net/http.Server
1544+
didPanic := true
1545+
defer func() {
1546+
if didPanic {
1547+
e := recover()
1548+
// Same as net/http:
1549+
const size = 64 << 10
1550+
buf := make([]byte, size)
1551+
buf = buf[:runtime.Stack(buf, false)]
1552+
sc.writeFrameFromHandler(frameWriteMsg{
1553+
write: handlerPanicRST{rw.rws.stream.id},
1554+
stream: rw.rws.stream,
1555+
})
1556+
sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
1557+
return
1558+
}
1559+
rw.handlerDone()
1560+
}()
15351561
handler(rw, req)
1562+
didPanic = false
15361563
}
15371564

15381565
func handleHeaderListTooLong(w http.ResponseWriter, r *http.Request) {
@@ -1920,9 +1947,6 @@ func (w *responseWriter) write(lenData int, dataB []byte, dataS string) (n int,
19201947

19211948
func (w *responseWriter) handlerDone() {
19221949
rws := w.rws
1923-
if rws == nil {
1924-
panic("handlerDone called twice")
1925-
}
19261950
rws.handlerDone = true
19271951
w.Flush()
19281952
w.rws = nil

http2/transport.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,8 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
584584
case <-requestCancel(req):
585585
cs.abortRequestBodyWrite()
586586
return nil, errRequestCanceled
587+
case <-cs.peerReset:
588+
return nil, cs.resetErr
587589
case err := <-bodyCopyErrc:
588590
if err != nil {
589591
return nil, err

http2/write.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,16 @@ func (w *writeData) writeFrame(ctx writeContext) error {
9595
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p)
9696
}
9797

98+
// handlerPanicRST is the message sent from handler goroutines when
99+
// the handler panics.
100+
type handlerPanicRST struct {
101+
StreamID uint32
102+
}
103+
104+
func (hp handlerPanicRST) writeFrame(ctx writeContext) error {
105+
return ctx.Framer().WriteRSTStream(hp.StreamID, ErrCodeInternal)
106+
}
107+
98108
func (se StreamError) writeFrame(ctx writeContext) error {
99109
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
100110
}

0 commit comments

Comments
 (0)