aboutsummaryrefslogtreecommitdiff
path: root/vendor/golang.org/x/net/http2/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/net/http2/server.go')
-rw-r--r--vendor/golang.org/x/net/http2/server.go271
1 files changed, 183 insertions, 88 deletions
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index 370e42e..0431ab0 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -129,10 +129,6 @@ func (s *Server) maxConcurrentStreams() uint32 {
return defaultMaxStreams
}
-// List of funcs for ConfigureServer to run. Both h1 and h2 are guaranteed
-// to be non-nil.
-var configServerFuncs []func(h1 *http.Server, h2 *Server) error
-
// ConfigureServer adds HTTP/2 support to a net/http Server.
//
// The configuration conf may be nil.
@@ -192,9 +188,6 @@ func ConfigureServer(s *http.Server, conf *Server) error {
if !haveNPN {
s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, NextProtoTLS)
}
- // h2-14 is temporary (as of 2015-03-05) while we wait for all browsers
- // to switch to "h2".
- s.TLSConfig.NextProtos = append(s.TLSConfig.NextProtos, "h2-14")
if s.TLSNextProto == nil {
s.TLSNextProto = map[string]func(*http.Server, *tls.Conn, http.Handler){}
@@ -209,17 +202,9 @@ func ConfigureServer(s *http.Server, conf *Server) error {
})
}
s.TLSNextProto[NextProtoTLS] = protoHandler
- s.TLSNextProto["h2-14"] = protoHandler // temporary; see above.
return nil
}
-// h1ServerShutdownChan if non-nil provides a func to return a channel
-// that will be closed when the provided *http.Server wants to shut
-// down. This is initialized via an init func in net/http (via its
-// mangled name from x/tools/cmd/bundle). This is only used when http2
-// is bundled into std for now.
-var h1ServerShutdownChan func(*http.Server) <-chan struct{}
-
// ServeConnOpts are options for the Server.ServeConn method.
type ServeConnOpts struct {
// BaseConfig optionally sets the base configuration
@@ -401,8 +386,8 @@ type serverConn struct {
advMaxStreams uint32 // our SETTINGS_MAX_CONCURRENT_STREAMS advertised the client
curClientStreams uint32 // number of open streams initiated by the client
curPushedStreams uint32 // number of open streams initiated by server push
- maxStreamID uint32 // max ever seen from client
- maxPushPromiseID uint32 // ID of the last push promise, or 0 if there have been no pushes
+ maxClientStreamID uint32 // max ever seen from client (odd), or 0 if there have been no client requests
+ maxPushPromiseID uint32 // ID of the last push promise (even), or 0 if there have been no pushes
streams map[uint32]*stream
initialWindowSize int32
maxFrameSize int32
@@ -438,6 +423,11 @@ func (sc *serverConn) maxHeaderListSize() uint32 {
return uint32(n + typicalHeaders*perFieldOverhead)
}
+func (sc *serverConn) curOpenStreams() uint32 {
+ sc.serveG.check()
+ return sc.curClientStreams + sc.curPushedStreams
+}
+
// stream represents a stream. This is the minimal metadata needed by
// the serve goroutine. Most of the actual stream state is owned by
// the http.Handler's goroutine in the responseWriter. Because the
@@ -463,8 +453,7 @@ type stream struct {
numTrailerValues int64
weight uint8
state streamState
- sentReset bool // only true once detached from streams map
- gotReset bool // only true once detacted from streams map
+ resetQueued bool // RST_STREAM queued for write; set by sc.resetStream
gotTrailerHeader bool // HEADER frame for trailers was seen
wroteHeaders bool // whether we wrote headers (not status 100)
reqBuf []byte // if non-nil, body pipe buffer to return later at EOF
@@ -492,8 +481,14 @@ func (sc *serverConn) state(streamID uint32) (streamState, *stream) {
// a client sends a HEADERS frame on stream 7 without ever sending a
// frame on stream 5, then stream 5 transitions to the "closed"
// state when the first frame for stream 7 is sent or received."
- if streamID <= sc.maxStreamID {
- return stateClosed, nil
+ if streamID%2 == 1 {
+ if streamID <= sc.maxClientStreamID {
+ return stateClosed, nil
+ }
+ } else {
+ if streamID <= sc.maxPushPromiseID {
+ return stateClosed, nil
+ }
}
return stateIdle, nil
}
@@ -718,7 +713,7 @@ func (sc *serverConn) serve() {
}
var gracefulShutdownCh <-chan struct{}
- if sc.hs != nil && h1ServerShutdownChan != nil {
+ if sc.hs != nil {
gracefulShutdownCh = h1ServerShutdownChan(sc.hs)
}
@@ -751,7 +746,7 @@ func (sc *serverConn) serve() {
return
case <-gracefulShutdownCh:
gracefulShutdownCh = nil
- sc.goAwayIn(ErrCodeNo, 0)
+ sc.startGracefulShutdown()
case <-sc.shutdownTimerCh:
sc.vlogf("GOAWAY close timer fired; closing conn from %v", sc.conn.RemoteAddr())
return
@@ -762,7 +757,7 @@ func (sc *serverConn) serve() {
fn(loopNum)
}
- if sc.inGoAway && sc.curClientStreams == 0 && !sc.needToSendGoAway && !sc.writingFrame {
+ if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
return
}
}
@@ -878,8 +873,34 @@ func (sc *serverConn) writeFrameFromHandler(wr FrameWriteRequest) error {
func (sc *serverConn) writeFrame(wr FrameWriteRequest) {
sc.serveG.check()
+ // If true, wr will not be written and wr.done will not be signaled.
var ignoreWrite bool
+ // We are not allowed to write frames on closed streams. RFC 7540 Section
+ // 5.1.1 says: "An endpoint MUST NOT send frames other than PRIORITY on
+ // a closed stream." Our server never sends PRIORITY, so that exception
+ // does not apply.
+ //
+ // The serverConn might close an open stream while the stream's handler
+ // is still running. For example, the server might close a stream when it
+ // receives bad data from the client. If this happens, the handler might
+ // attempt to write a frame after the stream has been closed (since the
+ // handler hasn't yet been notified of the close). In this case, we simply
+ // ignore the frame. The handler will notice that the stream is closed when
+ // it waits for the frame to be written.
+ //
+ // As an exception to this rule, we allow sending RST_STREAM after close.
+ // This allows us to immediately reject new streams without tracking any
+ // state for those streams (except for the queued RST_STREAM frame). This
+ // may result in duplicate RST_STREAMs in some cases, but the client should
+ // ignore those.
+ if wr.StreamID() != 0 {
+ _, isReset := wr.write.(StreamError)
+ if state, _ := sc.state(wr.StreamID()); state == stateClosed && !isReset {
+ ignoreWrite = true
+ }
+ }
+
// Don't send a 100-continue response if we've already sent headers.
// See golang.org/issue/14030.
switch wr.write.(type) {
@@ -887,6 +908,11 @@ func (sc *serverConn) writeFrame(wr FrameWriteRequest) {
wr.stream.wroteHeaders = true
case write100ContinueHeadersFrame:
if wr.stream.wroteHeaders {
+ // We do not need to notify wr.done because this frame is
+ // never written with wr.done != nil.
+ if wr.done != nil {
+ panic("wr.done != nil for write100ContinueHeadersFrame")
+ }
ignoreWrite = true
}
}
@@ -910,14 +936,15 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
if st != nil {
switch st.state {
case stateHalfClosedLocal:
- panic("internal error: attempt to send frame on half-closed-local stream")
- case stateClosed:
- if st.sentReset || st.gotReset {
- // Skip this frame.
- sc.scheduleFrameWrite()
- return
+ switch wr.write.(type) {
+ case StreamError, handlerPanicRST, writeWindowUpdate:
+ // RFC 7540 Section 5.1 allows sending RST_STREAM, PRIORITY, and WINDOW_UPDATE
+ // in this state. (We never send PRIORITY from the server, so that is not checked.)
+ default:
+ panic(fmt.Sprintf("internal error: attempt to send frame on a half-closed-local stream: %v", wr))
}
- panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wr))
+ case stateClosed:
+ panic(fmt.Sprintf("internal error: attempt to send frame on a closed stream: %v", wr))
}
}
if wpp, ok := wr.write.(*writePushPromise); ok {
@@ -925,9 +952,7 @@ func (sc *serverConn) startFrameWrite(wr FrameWriteRequest) {
wpp.promisedID, err = wpp.allocatePromisedID()
if err != nil {
sc.writingFrameAsync = false
- if wr.done != nil {
- wr.done <- err
- }
+ wr.replyToWriter(err)
return
}
}
@@ -960,25 +985,9 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) {
sc.writingFrameAsync = false
wr := res.wr
- st := wr.stream
-
- closeStream := endsStream(wr.write)
-
- if _, ok := wr.write.(handlerPanicRST); ok {
- sc.closeStream(st, errHandlerPanicked)
- }
-
- // Reply (if requested) to the blocked ServeHTTP goroutine.
- if ch := wr.done; ch != nil {
- select {
- case ch <- res.err:
- default:
- panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wr.write))
- }
- }
- wr.write = nil // prevent use (assume it's tainted after wr.done send)
- if closeStream {
+ if writeEndsStream(wr.write) {
+ st := wr.stream
if st == nil {
panic("internal error: expecting non-nil stream")
}
@@ -991,15 +1000,29 @@ func (sc *serverConn) wroteFrame(res frameWriteResult) {
// reading data (see possible TODO at top of
// this file), we go into closed state here
// anyway, after telling the peer we're
- // hanging up on them.
- st.state = stateHalfClosedLocal // won't last long, but necessary for closeStream via resetStream
- errCancel := streamError(st.id, ErrCodeCancel)
- sc.resetStream(errCancel)
+ // hanging up on them. We'll transition to
+ // stateClosed after the RST_STREAM frame is
+ // written.
+ st.state = stateHalfClosedLocal
+ sc.resetStream(streamError(st.id, ErrCodeCancel))
case stateHalfClosedRemote:
sc.closeStream(st, errHandlerComplete)
}
+ } else {
+ switch v := wr.write.(type) {
+ case StreamError:
+ // st may be unknown if the RST_STREAM was generated to reject bad input.
+ if st, ok := sc.streams[v.StreamID]; ok {
+ sc.closeStream(st, v)
+ }
+ case handlerPanicRST:
+ sc.closeStream(wr.stream, errHandlerPanicked)
+ }
}
+ // Reply (if requested) to unblock the ServeHTTP goroutine.
+ wr.replyToWriter(res.err)
+
sc.scheduleFrameWrite()
}
@@ -1026,7 +1049,7 @@ func (sc *serverConn) scheduleFrameWrite() {
sc.needToSendGoAway = false
sc.startFrameWrite(FrameWriteRequest{
write: &writeGoAway{
- maxStreamID: sc.maxStreamID,
+ maxStreamID: sc.maxClientStreamID,
code: sc.goAwayCode,
},
})
@@ -1053,6 +1076,13 @@ func (sc *serverConn) scheduleFrameWrite() {
sc.inFrameScheduleLoop = false
}
+// startGracefulShutdown sends a GOAWAY with ErrCodeNo to tell the
+// client we're gracefully shutting down. The connection isn't closed
+// until all current streams are done.
+func (sc *serverConn) startGracefulShutdown() {
+ sc.goAwayIn(ErrCodeNo, 0)
+}
+
func (sc *serverConn) goAway(code ErrCode) {
sc.serveG.check()
var forceCloseIn time.Duration
@@ -1089,8 +1119,7 @@ func (sc *serverConn) resetStream(se StreamError) {
sc.serveG.check()
sc.writeFrame(FrameWriteRequest{write: se})
if st, ok := sc.streams[se.StreamID]; ok {
- st.sentReset = true
- sc.closeStream(st, se)
+ st.resetQueued = true
}
}
@@ -1175,6 +1204,8 @@ func (sc *serverConn) processFrame(f Frame) error {
return sc.processResetStream(f)
case *PriorityFrame:
return sc.processPriority(f)
+ case *GoAwayFrame:
+ return sc.processGoAway(f)
case *PushPromiseFrame:
// A client cannot push. Thus, servers MUST treat the receipt of a PUSH_PROMISE
// frame as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
@@ -1200,7 +1231,7 @@ func (sc *serverConn) processPing(f *PingFrame) error {
// PROTOCOL_ERROR."
return ConnectionError(ErrCodeProtocol)
}
- if sc.inGoAway {
+ if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
return nil
}
sc.writeFrame(FrameWriteRequest{write: writePingAck{f}})
@@ -1209,9 +1240,6 @@ func (sc *serverConn) processPing(f *PingFrame) error {
func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
sc.serveG.check()
- if sc.inGoAway {
- return nil
- }
switch {
case f.StreamID != 0: // stream-level flow control
state, st := sc.state(f.StreamID)
@@ -1244,9 +1272,6 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
sc.serveG.check()
- if sc.inGoAway {
- return nil
- }
state, st := sc.state(f.StreamID)
if state == stateIdle {
@@ -1258,7 +1283,6 @@ func (sc *serverConn) processResetStream(f *RSTStreamFrame) error {
return ConnectionError(ErrCodeProtocol)
}
if st != nil {
- st.gotReset = true
st.cancelCtx()
sc.closeStream(st, streamError(f.StreamID, f.ErrCode))
}
@@ -1276,12 +1300,15 @@ func (sc *serverConn) closeStream(st *stream, err error) {
} else {
sc.curClientStreams--
}
- if sc.curClientStreams+sc.curPushedStreams == 0 {
- sc.setConnState(http.StateIdle)
- }
delete(sc.streams, st.id)
- if len(sc.streams) == 0 && sc.srv.IdleTimeout != 0 {
- sc.idleTimer.Reset(sc.srv.IdleTimeout)
+ if len(sc.streams) == 0 {
+ sc.setConnState(http.StateIdle)
+ if sc.srv.IdleTimeout != 0 {
+ sc.idleTimer.Reset(sc.srv.IdleTimeout)
+ }
+ if h1ServerKeepAlivesDisabled(sc.hs) {
+ sc.startGracefulShutdown()
+ }
}
if p := st.body; p != nil {
// Return any buffered unread bytes worth of conn-level flow control.
@@ -1306,9 +1333,6 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
}
return nil
}
- if sc.inGoAway {
- return nil
- }
if err := f.ForeachSetting(sc.processSetting); err != nil {
return err
}
@@ -1380,7 +1404,7 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
func (sc *serverConn) processData(f *DataFrame) error {
sc.serveG.check()
- if sc.inGoAway {
+ if sc.inGoAway && sc.goAwayCode != ErrCodeNo {
return nil
}
data := f.Data()
@@ -1397,7 +1421,7 @@ func (sc *serverConn) processData(f *DataFrame) error {
// type PROTOCOL_ERROR."
return ConnectionError(ErrCodeProtocol)
}
- if st == nil || state != stateOpen || st.gotTrailerHeader {
+ if st == nil || state != stateOpen || st.gotTrailerHeader || st.resetQueued {
// This includes sending a RST_STREAM if the stream is
// in stateHalfClosedLocal (which currently means that
// the http.Handler returned, so it's done reading &
@@ -1417,6 +1441,10 @@ func (sc *serverConn) processData(f *DataFrame) error {
sc.inflow.take(int32(f.Length))
sc.sendWindowUpdate(nil, int(f.Length)) // conn-level
+ if st != nil && st.resetQueued {
+ // Already have a stream error in flight. Don't send another.
+ return nil
+ }
return streamError(id, ErrCodeStreamClosed)
}
if st.body == nil {
@@ -1459,6 +1487,20 @@ func (sc *serverConn) processData(f *DataFrame) error {
return nil
}
+func (sc *serverConn) processGoAway(f *GoAwayFrame) error {
+ sc.serveG.check()
+ if f.ErrCode != ErrCodeNo {
+ sc.logf("http2: received GOAWAY %+v, starting graceful shutdown", f)
+ } else {
+ sc.vlogf("http2: received GOAWAY %+v, starting graceful shutdown", f)
+ }
+ sc.startGracefulShutdown()
+ // http://tools.ietf.org/html/rfc7540#section-6.8
+ // We should not create any new streams, which means we should disable push.
+ sc.pushEnabled = false
+ return nil
+}
+
// isPushed reports whether the stream is server-initiated.
func (st *stream) isPushed() bool {
return st.id%2 == 0
@@ -1511,6 +1553,11 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// open, let it process its own HEADERS frame (trailers at this
// point, if it's valid).
if st := sc.streams[f.StreamID]; st != nil {
+ if st.resetQueued {
+ // We're sending RST_STREAM to close the stream, so don't bother
+ // processing this frame.
+ return nil
+ }
return st.processTrailerHeaders(f)
}
@@ -1519,10 +1566,10 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
// endpoint has opened or reserved. [...] An endpoint that
// receives an unexpected stream identifier MUST respond with
// a connection error (Section 5.4.1) of type PROTOCOL_ERROR.
- if id <= sc.maxStreamID {
+ if id <= sc.maxClientStreamID {
return ConnectionError(ErrCodeProtocol)
}
- sc.maxStreamID = id
+ sc.maxClientStreamID = id
if sc.idleTimer != nil {
sc.idleTimer.Stop()
@@ -1673,7 +1720,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
} else {
sc.curClientStreams++
}
- if sc.curClientStreams+sc.curPushedStreams == 1 {
+ if sc.curOpenStreams() == 1 {
sc.setConnState(http.StateActive)
}
@@ -1858,15 +1905,17 @@ func (sc *serverConn) runHandler(rw *responseWriter, req *http.Request, handler
rw.rws.stream.cancelCtx()
if didPanic {
e := recover()
- // Same as net/http:
- const size = 64 << 10
- buf := make([]byte, size)
- buf = buf[:runtime.Stack(buf, false)]
sc.writeFrameFromHandler(FrameWriteRequest{
write: handlerPanicRST{rw.rws.stream.id},
stream: rw.rws.stream,
})
- sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
+ // Same as net/http:
+ if shouldLogPanic(e) {
+ const size = 64 << 10
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ sc.logf("http2: panic serving %v: %v\n%s", sc.conn.RemoteAddr(), e, buf)
+ }
return
}
rw.handlerDone()
@@ -2278,8 +2327,9 @@ func (w *responseWriter) CloseNotify() <-chan bool {
if ch == nil {
ch = make(chan bool, 1)
rws.closeNotifierCh = ch
+ cw := rws.stream.cw
go func() {
- rws.stream.cw.Wait() // wait for close
+ cw.Wait() // wait for close
ch <- true
}()
}
@@ -2432,7 +2482,7 @@ func (w *responseWriter) push(target string, opts pushOptions) error {
}
for k := range opts.Header {
if strings.HasPrefix(k, ":") {
- return fmt.Errorf("promised request headers cannot include psuedo header %q", k)
+ return fmt.Errorf("promised request headers cannot include pseudo header %q", k)
}
// These headers are meaningful only if the request has a body,
// but PUSH_PROMISE requests cannot have a body.
@@ -2525,6 +2575,12 @@ func (sc *serverConn) startPush(msg startPushRequest) {
// http://tools.ietf.org/html/rfc7540#section-5.1.1.
// Streams initiated by the server MUST use even-numbered identifiers.
+ // A server that is unable to establish a new stream identifier can send a GOAWAY
+ // frame so that the client is forced to open a new connection for new streams.
+ if sc.maxPushPromiseID+2 >= 1<<31 {
+ sc.startGracefulShutdown()
+ return 0, ErrPushLimitReached
+ }
sc.maxPushPromiseID += 2
promisedID := sc.maxPushPromiseID
@@ -2539,7 +2595,7 @@ func (sc *serverConn) startPush(msg startPushRequest) {
scheme: msg.url.Scheme,
authority: msg.url.Host,
path: msg.url.RequestURI(),
- header: msg.header,
+ header: cloneHeader(msg.header), // clone since handler runs concurrently with writing the PUSH_PROMISE
})
if err != nil {
// Should not happen, since we've already validated msg.url.
@@ -2646,3 +2702,42 @@ var badTrailer = map[string]bool{
"Transfer-Encoding": true,
"Www-Authenticate": true,
}
+
+// h1ServerShutdownChan returns a channel that will be closed when the
+// provided *http.Server wants to shut down.
+//
+// This is a somewhat hacky way to get at http1 innards. It works
+// when the http2 code is bundled into the net/http package in the
+// standard library. The alternatives ended up making the cmd/go tool
+// depend on http Servers. This is the lightest option for now.
+// This is tested via the TestServeShutdown* tests in net/http.
+func h1ServerShutdownChan(hs *http.Server) <-chan struct{} {
+ if fn := testh1ServerShutdownChan; fn != nil {
+ return fn(hs)
+ }
+ var x interface{} = hs
+ type I interface {
+ getDoneChan() <-chan struct{}
+ }
+ if hs, ok := x.(I); ok {
+ return hs.getDoneChan()
+ }
+ return nil
+}
+
+// optional test hook for h1ServerShutdownChan.
+var testh1ServerShutdownChan func(hs *http.Server) <-chan struct{}
+
+// h1ServerKeepAlivesDisabled reports whether hs has its keep-alives
+// disabled. See comments on h1ServerShutdownChan above for why
+// the code is written this way.
+func h1ServerKeepAlivesDisabled(hs *http.Server) bool {
+ var x interface{} = hs
+ type I interface {
+ doKeepAlives() bool
+ }
+ if hs, ok := x.(I); ok {
+ return !hs.doKeepAlives()
+ }
+ return false
+}