Commit d4df6f48 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: updated bundled http2 copy, enable some tests

Updates bundled copy of x/net/http2 to include
https://golang.org/cl/17823 (catching panics in Handlers)

Fixes #13555

Change-Id: I08e4e38e736a8d93f5ec200e8041c143fc6eafce
Reviewed-on: https://go-review.googlesource.com/17824Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 9b1068ad
...@@ -1473,6 +1473,8 @@ type http2headersEnder interface { ...@@ -1473,6 +1473,8 @@ type http2headersEnder interface {
HeadersEnded() bool HeadersEnded() bool
} }
func http2requestCancel(req *Request) <-chan struct{} { return req.Cancel }
var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1" var http2DebugGoroutines = os.Getenv("DEBUG_HTTP2_GOROUTINES") == "1"
type http2goroutineLock uint64 type http2goroutineLock uint64
...@@ -2545,6 +2547,7 @@ func (sc *http2serverConn) stopShutdownTimer() { ...@@ -2545,6 +2547,7 @@ func (sc *http2serverConn) stopShutdownTimer() {
} }
func (sc *http2serverConn) notePanic() { func (sc *http2serverConn) notePanic() {
if http2testHookOnPanicMu != nil { if http2testHookOnPanicMu != nil {
http2testHookOnPanicMu.Lock() http2testHookOnPanicMu.Lock()
defer http2testHookOnPanicMu.Unlock() defer http2testHookOnPanicMu.Unlock()
...@@ -2752,6 +2755,11 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) { ...@@ -2752,6 +2755,11 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
go sc.writeFrameAsync(wm) 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 // wroteFrame is called on the serve goroutine with the result of
// whatever happened on writeFrameAsync. // whatever happened on writeFrameAsync.
func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) { func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
...@@ -2766,6 +2774,10 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) { ...@@ -2766,6 +2774,10 @@ func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
closeStream := http2endsStream(wm.write) closeStream := http2endsStream(wm.write)
if _, ok := wm.write.(http2handlerPanicRST); ok {
sc.closeStream(st, http2errHandlerPanicked)
}
if ch := wm.done; ch != nil { if ch := wm.done; ch != nil {
select { select {
case ch <- res.err: case ch <- res.err:
...@@ -3360,9 +3372,25 @@ func (sc *http2serverConn) newWriterAndRequest() (*http2responseWriter, *Request ...@@ -3360,9 +3372,25 @@ func (sc *http2serverConn) newWriterAndRequest() (*http2responseWriter, *Request
// Run on its own goroutine. // Run on its own goroutine.
func (sc *http2serverConn) runHandler(rw *http2responseWriter, req *Request, handler func(ResponseWriter, *Request)) { 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) handler(rw, req)
didPanic = false
} }
func http2handleHeaderListTooLong(w ResponseWriter, r *Request) { func http2handleHeaderListTooLong(w ResponseWriter, r *Request) {
...@@ -3743,9 +3771,6 @@ func (w *http2responseWriter) write(lenData int, dataB []byte, dataS string) (n ...@@ -3743,9 +3771,6 @@ func (w *http2responseWriter) write(lenData int, dataB []byte, dataS string) (n
func (w *http2responseWriter) handlerDone() { func (w *http2responseWriter) handlerDone() {
rws := w.rws rws := w.rws
if rws == nil {
panic("handlerDone called twice")
}
rws.handlerDone = true rws.handlerDone = true
w.Flush() w.Flush()
w.rws = nil w.rws = nil
...@@ -4279,9 +4304,11 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) { ...@@ -4279,9 +4304,11 @@ func (cc *http2ClientConn) RoundTrip(req *Request) (*Response, error) {
res.Request = req res.Request = req
res.TLS = cc.tlsState res.TLS = cc.tlsState
return res, nil return res, nil
case <-req.Cancel: case <-http2requestCancel(req):
cs.abortRequestBodyWrite() cs.abortRequestBodyWrite()
return nil, http2errRequestCanceled return nil, http2errRequestCanceled
case <-cs.peerReset:
return nil, cs.resetErr
case err := <-bodyCopyErrc: case err := <-bodyCopyErrc:
if err != nil { if err != nil {
return nil, err return nil, err
...@@ -4630,7 +4657,7 @@ func (rl *http2clientConnReadLoop) processHeaderBlockFragment(frag []byte, strea ...@@ -4630,7 +4657,7 @@ func (rl *http2clientConnReadLoop) processHeaderBlockFragment(frag []byte, strea
cs.bufPipe = http2pipe{b: buf} cs.bufPipe = http2pipe{b: buf}
cs.bytesRemain = res.ContentLength cs.bytesRemain = res.ContentLength
res.Body = http2transportResponseBody{cs} 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" { if cs.requestedGzip && res.Header.Get("Content-Encoding") == "gzip" {
res.Header.Del("Content-Encoding") res.Header.Del("Content-Encoding")
...@@ -5016,6 +5043,16 @@ func (w *http2writeData) writeFrame(ctx http2writeContext) error { ...@@ -5016,6 +5043,16 @@ func (w *http2writeData) writeFrame(ctx http2writeContext) error {
return ctx.Framer().WriteData(w.streamID, w.endStream, w.p) 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 { func (se http2StreamError) writeFrame(ctx http2writeContext) error {
return ctx.Framer().WriteRSTStream(se.StreamID, se.Code) return ctx.Framer().WriteRSTStream(se.StreamID, se.Code)
} }
......
...@@ -1926,16 +1926,12 @@ func testZeroLengthPostAndResponse(t *testing.T, h2 bool) { ...@@ -1926,16 +1926,12 @@ func testZeroLengthPostAndResponse(t *testing.T, h2 bool) {
} }
func TestHandlerPanicNil_h1(t *testing.T) { testHandlerPanic(t, false, h1Mode, nil) } func TestHandlerPanicNil_h1(t *testing.T) { testHandlerPanic(t, false, h1Mode, nil) }
func TestHandlerPanicNil_h2(t *testing.T) { func TestHandlerPanicNil_h2(t *testing.T) { testHandlerPanic(t, false, h2Mode, nil) }
t.Skip("known failure; golang.org/issue/13555")
testHandlerPanic(t, false, h2Mode, nil)
}
func TestHandlerPanic_h1(t *testing.T) { func TestHandlerPanic_h1(t *testing.T) {
testHandlerPanic(t, false, h1Mode, "intentional death for testing") testHandlerPanic(t, false, h1Mode, "intentional death for testing")
} }
func TestHandlerPanic_h2(t *testing.T) { func TestHandlerPanic_h2(t *testing.T) {
t.Skip("known failure; golang.org/issue/13555")
testHandlerPanic(t, false, h2Mode, "intentional death for testing") testHandlerPanic(t, false, h2Mode, "intentional death for testing")
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment