]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: update copy of http2
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 20 Oct 2015 23:53:31 +0000 (23:53 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 21 Oct 2015 00:28:55 +0000 (00:28 +0000)
Updates to x/net git revision 9946ad7

Change-Id: I95c03daf382667002a5b22f184bd9b7d18144913
Reviewed-on: https://go-review.googlesource.com/16066
Reviewed-by: Andrew Gerrand <adg@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/http/h2_bundle.go
src/vendor/golang.org/x/net/http2/hpack/hpack.go

index 68e6d798615fe7903ff9a0b83a5d5e3db0147ecf..9be5ebfa4f6e3c246b91fd85ebd4a4ea256af993 100644 (file)
@@ -1750,7 +1750,7 @@ const (
 var (
        http2errClientDisconnected = errors.New("client disconnected")
        http2errClosedBody         = errors.New("body closed by handler")
-       http2errStreamBroken       = errors.New("http2: stream broken")
+       http2errStreamClosed       = errors.New("http2: stream closed")
 )
 
 var http2responseWriterStatePool = sync.Pool{
@@ -1819,24 +1819,33 @@ func http2ConfigureServer(s *Server, conf *http2Server) error {
        if conf == nil {
                conf = new(http2Server)
        }
+
        if s.TLSConfig == nil {
                s.TLSConfig = new(tls.Config)
-       }
-
-       if s.TLSConfig.CipherSuites != nil {
+       } else if s.TLSConfig.CipherSuites != nil {
+               // If they already provided a CipherSuite list, return
+               // an error if it has a bad order or is missing
+               // ECDHE_RSA_WITH_AES_128_GCM_SHA256.
                const requiredCipher = tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
                haveRequired := false
-               for _, v := range s.TLSConfig.CipherSuites {
-                       if v == requiredCipher {
+               sawBad := false
+               for i, cs := range s.TLSConfig.CipherSuites {
+                       if cs == requiredCipher {
                                haveRequired = true
-                               break
+                       }
+                       if http2isBadCipher(cs) {
+                               sawBad = true
+                       } else if sawBad {
+                               return fmt.Errorf("http2: TLSConfig.CipherSuites index %d contains an HTTP/2-approved cipher suite (%#04x), but it comes after unapproved cipher suites. With this configuration, clients that don't support previous, approved cipher suites may be given an unapproved one and reject the connection.", i, cs)
                        }
                }
                if !haveRequired {
-                       s.TLSConfig.CipherSuites = append(s.TLSConfig.CipherSuites, requiredCipher)
+                       return fmt.Errorf("http2: TLSConfig.CipherSuites is missing HTTP/2-required TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256")
                }
        }
 
+       s.TLSConfig.PreferServerCipherSuites = true
+
        haveNPN := false
        for _, p := range s.TLSConfig.NextProtos {
                if p == http2NextProtoTLS {
@@ -1861,7 +1870,7 @@ func http2ConfigureServer(s *Server, conf *http2Server) error {
        }
        s.TLSNextProto[http2NextProtoTLS] = protoHandler
        s.TLSNextProto["h2-14"] = protoHandler
-       return nil // temporary manual edit to h2_bundle.go, to be deleted once we update from x/net again
+       return nil
 }
 
 func (srv *http2Server) handleConn(hs *Server, c net.Conn, h Handler) {
@@ -1875,7 +1884,7 @@ func (srv *http2Server) handleConn(hs *Server, c net.Conn, h Handler) {
                streams:          make(map[uint32]*http2stream),
                readFrameCh:      make(chan http2readFrameResult),
                wantWriteFrameCh: make(chan http2frameWriteMsg, 8),
-               wroteFrameCh:     make(chan struct{}, 1),
+               wroteFrameCh:     make(chan http2frameWriteResult, 1),
                bodyReadCh:       make(chan http2bodyReadMsg),
                doneServing:      make(chan struct{}),
                advMaxStreams:    srv.maxConcurrentStreams(),
@@ -1961,15 +1970,15 @@ type http2serverConn struct {
        handler          Handler
        framer           *http2Framer
        hpackDecoder     *hpack.Decoder
-       doneServing      chan struct{}             // closed when serverConn.serve ends
-       readFrameCh      chan http2readFrameResult // written by serverConn.readFrames
-       wantWriteFrameCh chan http2frameWriteMsg   // from handlers -> serve
-       wroteFrameCh     chan struct{}             // from writeFrameAsync -> serve, tickles more frame writes
-       bodyReadCh       chan http2bodyReadMsg     // from handlers -> serve
-       testHookCh       chan func(int)            // code to run on the serve loop
-       flow             http2flow                 // conn-wide (not stream-specific) outbound flow control
-       inflow           http2flow                 // conn-wide inbound flow control
-       tlsState         *tls.ConnectionState      // shared by all handlers, like net/http
+       doneServing      chan struct{}              // closed when serverConn.serve ends
+       readFrameCh      chan http2readFrameResult  // written by serverConn.readFrames
+       wantWriteFrameCh chan http2frameWriteMsg    // from handlers -> serve
+       wroteFrameCh     chan http2frameWriteResult // from writeFrameAsync -> serve, tickles more frame writes
+       bodyReadCh       chan http2bodyReadMsg      // from handlers -> serve
+       testHookCh       chan func(int)             // code to run on the serve loop
+       flow             http2flow                  // conn-wide (not stream-specific) outbound flow control
+       inflow           http2flow                  // conn-wide inbound flow control
+       tlsState         *tls.ConnectionState       // shared by all handlers, like net/http
        remoteAddrStr    string
 
        // Everything following is owned by the serve loop; use serveG.check():
@@ -2086,6 +2095,15 @@ func (sc *http2serverConn) state(streamID uint32) (http2streamState, *http2strea
        return http2stateIdle, nil
 }
 
+// setConnState calls the net/http ConnState hook for this connection, if configured.
+// Note that the net/http package does StateNew and StateClosed for us.
+// There is currently no plan for StateHijacked or hijacking HTTP/2 connections.
+func (sc *http2serverConn) setConnState(state ConnState) {
+       if sc.hs.ConnState != nil {
+               sc.hs.ConnState(sc.conn, state)
+       }
+}
+
 func (sc *http2serverConn) vlogf(format string, args ...interface{}) {
        if http2VerboseLogs {
                sc.logf(format, args...)
@@ -2207,20 +2225,19 @@ func (sc *http2serverConn) readFrames() {
        }
 }
 
+// frameWriteResult is the message passed from writeFrameAsync to the serve goroutine.
+type http2frameWriteResult struct {
+       wm  http2frameWriteMsg // what was written (or attempted)
+       err error              // result of the writeFrame call
+}
+
 // writeFrameAsync runs in its own goroutine and writes a single frame
 // and then reports when it's done.
 // At most one goroutine can be running writeFrameAsync at a time per
 // serverConn.
 func (sc *http2serverConn) writeFrameAsync(wm http2frameWriteMsg) {
        err := wm.write.writeFrame(sc)
-       if ch := wm.done; ch != nil {
-               select {
-               case ch <- err:
-               default:
-                       panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
-               }
-       }
-       sc.wroteFrameCh <- struct{}{}
+       sc.wroteFrameCh <- http2frameWriteResult{wm, err}
 }
 
 func (sc *http2serverConn) closeAllStreamsOnConnClose() {
@@ -2275,6 +2292,9 @@ func (sc *http2serverConn) serve() {
                return
        }
 
+       sc.setConnState(StateActive)
+       sc.setConnState(StateIdle)
+
        go sc.readFrames()
 
        settingsTimer := time.NewTimer(http2firstSettingsTimeout)
@@ -2284,12 +2304,8 @@ func (sc *http2serverConn) serve() {
                select {
                case wm := <-sc.wantWriteFrameCh:
                        sc.writeFrame(wm)
-               case <-sc.wroteFrameCh:
-                       if sc.writingFrame != true {
-                               panic("internal error: expected to be already writing a frame")
-                       }
-                       sc.writingFrame = false
-                       sc.scheduleFrameWrite()
+               case res := <-sc.wroteFrameCh:
+                       sc.wroteFrame(res)
                case res := <-sc.readFrameCh:
                        if !sc.processFrameFromReader(res) {
                                return
@@ -2355,20 +2371,28 @@ var http2errChanPool = sync.Pool{
 // scheduling decisions available.
 func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, writeData *http2writeData) error {
        ch := http2errChanPool.Get().(chan error)
-       sc.writeFrameFromHandler(http2frameWriteMsg{
+       err := sc.writeFrameFromHandler(http2frameWriteMsg{
                write:  writeData,
                stream: stream,
                done:   ch,
        })
-       select {
-       case err := <-ch:
-               http2errChanPool.Put(ch)
+       if err != nil {
                return err
+       }
+       select {
+       case err = <-ch:
        case <-sc.doneServing:
                return http2errClientDisconnected
        case <-stream.cw:
-               return http2errStreamBroken
+
+               select {
+               case err = <-ch:
+               default:
+                       return http2errStreamClosed
+               }
        }
+       http2errChanPool.Put(ch)
+       return err
 }
 
 // writeFrameFromHandler sends wm to sc.wantWriteFrameCh, but aborts
@@ -2378,24 +2402,14 @@ func (sc *http2serverConn) writeDataFromHandler(stream *http2stream, writeData *
 // deadlock writing to sc.wantWriteFrameCh (which is only mildly
 // buffered and is read by serve itself). If you're on the serve
 // goroutine, call writeFrame instead.
-func (sc *http2serverConn) writeFrameFromHandler(wm http2frameWriteMsg) {
+func (sc *http2serverConn) writeFrameFromHandler(wm http2frameWriteMsg) error {
        sc.serveG.checkNotOn()
-       var scheduled bool
        select {
        case sc.wantWriteFrameCh <- wm:
-               scheduled = true
+               return nil
        case <-sc.doneServing:
 
-       case <-wm.stream.cw:
-
-       }
-
-       if !scheduled && wm.done != nil {
-               select {
-               case wm.done <- http2errStreamBroken:
-               default:
-                       panic("expected buffered channel")
-               }
+               return http2errClientDisconnected
        }
 }
 
@@ -2421,7 +2435,6 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
        if sc.writingFrame {
                panic("internal error: can only be writing one frame at a time")
        }
-       sc.writingFrame = true
 
        st := wm.stream
        if st != nil {
@@ -2431,15 +2444,42 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
                case http2stateClosed:
                        if st.sentReset || st.gotReset {
 
-                               sc.wroteFrameCh <- struct{}{}
+                               sc.scheduleFrameWrite()
                                return
                        }
                        panic(fmt.Sprintf("internal error: attempt to send a write %v on a closed stream", wm))
                }
        }
 
+       sc.writingFrame = true
        sc.needsFrameFlush = true
-       if http2endsStream(wm.write) {
+       go sc.writeFrameAsync(wm)
+}
+
+// wroteFrame is called on the serve goroutine with the result of
+// whatever happened on writeFrameAsync.
+func (sc *http2serverConn) wroteFrame(res http2frameWriteResult) {
+       sc.serveG.check()
+       if !sc.writingFrame {
+               panic("internal error: expected to be already writing a frame")
+       }
+       sc.writingFrame = false
+
+       wm := res.wm
+       st := wm.stream
+
+       closeStream := http2endsStream(wm.write)
+
+       if ch := wm.done; ch != nil {
+               select {
+               case ch <- res.err:
+               default:
+                       panic(fmt.Sprintf("unbuffered done channel passed in for type %T", wm.write))
+               }
+       }
+       wm.write = nil
+
+       if closeStream {
                if st == nil {
                        panic("internal error: expecting non-nil stream")
                }
@@ -2453,7 +2493,8 @@ func (sc *http2serverConn) startFrameWrite(wm http2frameWriteMsg) {
                        sc.closeStream(st, nil)
                }
        }
-       go sc.writeFrameAsync(wm)
+
+       sc.scheduleFrameWrite()
 }
 
 // scheduleFrameWrite tickles the frame writing scheduler.
@@ -2692,6 +2733,9 @@ func (sc *http2serverConn) closeStream(st *http2stream, err error) {
        }
        st.state = http2stateClosed
        sc.curOpenStreams--
+       if sc.curOpenStreams == 0 {
+               sc.setConnState(StateIdle)
+       }
        delete(sc.streams, st.id)
        if p := st.body; p != nil {
                p.Close(err)
@@ -2838,6 +2882,9 @@ func (sc *http2serverConn) processHeaders(f *http2HeadersFrame) error {
                http2adjustStreamPriority(sc.streams, st.id, f.Priority)
        }
        sc.curOpenStreams++
+       if sc.curOpenStreams == 1 {
+               sc.setConnState(StateActive)
+       }
        sc.req = http2requestParam{
                stream: st,
                header: make(Header),
@@ -3031,29 +3078,32 @@ func http2handleHeaderListTooLong(w ResponseWriter, r *Request) {
 
 // called from handler goroutines.
 // h may be nil.
-func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeResHeaders) {
+func (sc *http2serverConn) writeHeaders(st *http2stream, headerData *http2writeResHeaders) error {
        sc.serveG.checkNotOn()
        var errc chan error
        if headerData.h != nil {
 
                errc = http2errChanPool.Get().(chan error)
        }
-       sc.writeFrameFromHandler(http2frameWriteMsg{
+       if err := sc.writeFrameFromHandler(http2frameWriteMsg{
                write:  headerData,
                stream: st,
                done:   errc,
-       })
+       }); err != nil {
+               return err
+       }
        if errc != nil {
                select {
-               case <-errc:
-
+               case err := <-errc:
                        http2errChanPool.Put(errc)
+                       return err
                case <-sc.doneServing:
-
+                       return http2errClientDisconnected
                case <-st.cw:
-
+                       return http2errStreamClosed
                }
        }
+       return nil
 }
 
 // called from handler goroutines.
@@ -3227,7 +3277,7 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) {
                        ctype = DetectContentType(p)
                }
                endStream := rws.handlerDone && len(p) == 0
-               rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{
+               err = rws.conn.writeHeaders(rws.stream, &http2writeResHeaders{
                        streamID:      rws.stream.id,
                        httpResCode:   rws.status,
                        h:             rws.snapHeader,
@@ -3235,6 +3285,9 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) {
                        contentType:   ctype,
                        contentLength: clen,
                })
+               if err != nil {
+                       return 0, err
+               }
                if endStream {
                        return 0, nil
                }
@@ -3242,6 +3295,7 @@ func (rws *http2responseWriterState) writeChunk(p []byte) (n int, err error) {
        if len(p) == 0 && !rws.handlerDone {
                return 0, nil
        }
+
        curWrite := &rws.curWrite
        curWrite.streamID = rws.stream.id
        curWrite.p = p
@@ -3603,7 +3657,7 @@ func (t *http2Transport) newClientConn(host, port, key string) (*http2clientConn
                        cc.initialWindowSize = s.Val
                default:
 
-                       log.Printf("Unhandled Setting: %v", s)
+                       t.vlogf("Unhandled Setting: %v", s)
                }
                return nil
        })
@@ -3727,7 +3781,6 @@ func (cc *http2clientConn) encodeHeaders(req *Request) []byte {
 }
 
 func (cc *http2clientConn) writeHeader(name, value string) {
-       log.Printf("sending %q = %q", name, value)
        cc.henc.WriteField(hpack.HeaderField{Name: name, Value: value})
 }
 
@@ -3784,20 +3837,20 @@ func (cc *http2clientConn) readLoop() {
                        cc.readerErr = err
                        return
                }
-               log.Printf("Transport received %v: %#v", f.Header(), f)
+               cc.vlogf("Transport received %v: %#v", f.Header(), f)
 
                streamID := f.Header().StreamID
 
                _, isContinue := f.(*http2ContinuationFrame)
                if isContinue {
                        if streamID != continueStreamID {
-                               log.Printf("Protocol violation: got CONTINUATION with id %d; want %d", streamID, continueStreamID)
+                               cc.logf("Protocol violation: got CONTINUATION with id %d; want %d", streamID, continueStreamID)
                                cc.readerErr = http2ConnectionError(http2ErrCodeProtocol)
                                return
                        }
                } else if continueStreamID != 0 {
 
-                       log.Printf("Protocol violation: got %T for stream %d, want CONTINUATION for %d", f, streamID, continueStreamID)
+                       cc.logf("Protocol violation: got %T for stream %d, want CONTINUATION for %d", f, streamID, continueStreamID)
                        cc.readerErr = http2ConnectionError(http2ErrCodeProtocol)
                        return
                }
@@ -3813,7 +3866,7 @@ func (cc *http2clientConn) readLoop() {
 
                cs := cc.streamByID(streamID, streamEnded)
                if cs == nil {
-                       log.Printf("Received frame for untracked stream ID %d", streamID)
+                       cc.logf("Received frame for untracked stream ID %d", streamID)
                        continue
                }
 
@@ -3829,17 +3882,19 @@ func (cc *http2clientConn) readLoop() {
                case *http2ContinuationFrame:
                        cc.hdec.Write(f.HeaderBlockFragment())
                case *http2DataFrame:
-                       log.Printf("DATA: %q", f.Data())
+                       if http2VerboseLogs {
+                               cc.logf("DATA: %q", f.Data())
+                       }
                        cs.pw.Write(f.Data())
                case *http2GoAwayFrame:
                        cc.t.removeClientConn(cc)
                        if f.ErrCode != 0 {
 
-                               log.Printf("transport got GOAWAY with error code = %v", f.ErrCode)
+                               cc.vlogf("transport got GOAWAY with error code = %v", f.ErrCode)
                        }
                        cc.setGoAway(f)
                default:
-                       log.Printf("Transport: unhandled response frame type %T", f)
+                       cc.logf("Transport: unhandled response frame type %T", f)
                }
                headersEnded := false
                if he, ok := f.(http2headersEnder); ok {
@@ -3870,7 +3925,9 @@ func (cc *http2clientConn) readLoop() {
 
 func (cc *http2clientConn) onNewHeaderField(f hpack.HeaderField) {
 
-       log.Printf("Header field: %+v", f)
+       if http2VerboseLogs {
+               cc.logf("Header field: %+v", f)
+       }
        if f.Name == ":status" {
                code, err := strconv.Atoi(f.Value)
                if err != nil {
@@ -3887,6 +3944,24 @@ func (cc *http2clientConn) onNewHeaderField(f hpack.HeaderField) {
        cc.nextRes.Header.Add(CanonicalHeaderKey(f.Name), f.Value)
 }
 
+func (cc *http2clientConn) logf(format string, args ...interface{}) {
+       cc.t.logf(format, args...)
+}
+
+func (cc *http2clientConn) vlogf(format string, args ...interface{}) {
+       cc.t.vlogf(format, args...)
+}
+
+func (t *http2Transport) vlogf(format string, args ...interface{}) {
+       if http2VerboseLogs {
+               t.logf(format, args...)
+       }
+}
+
+func (t *http2Transport) logf(format string, args ...interface{}) {
+       log.Printf(format, args...)
+}
+
 // writeFramer is implemented by any type that is used to write frames.
 type http2writeFramer interface {
        writeFrame(http2writeContext) error
@@ -3915,6 +3990,9 @@ func http2endsStream(w http2writeFramer) bool {
                return v.endStream
        case *http2writeResHeaders:
                return v.endStream
+       case nil:
+
+               panic("endsStream called on nil writeFramer")
        }
        return false
 }
index f5a9b84e341511c1298c50a5128f5dc33eba8e07..8e9b2f2ebf52e807ec8d2791174ac82b208d2945 100644 (file)
@@ -282,6 +282,11 @@ func (d *Decoder) Write(p []byte) (n int, err error) {
        for len(d.buf) > 0 {
                err = d.parseHeaderFieldRepr()
                if err == errNeedMore {
+                       // Extra paranoia, making sure saveBuf won't
+                       // get too large.  All the varint and string
+                       // reading code earlier should already catch
+                       // overlong things and return ErrStringLength,
+                       // but keep this as a last resort.
                        const varIntOverhead = 8 // conservative
                        if d.maxStrLen != 0 && int64(len(d.buf)) > 2*(int64(d.maxStrLen)+varIntOverhead) {
                                return 0, ErrStringLength
@@ -503,6 +508,7 @@ func (d *Decoder) readString(p []byte, wantStr bool) (s string, remain []byte, e
                buf.Reset() // don't trust others
                defer bufPool.Put(buf)
                if err := huffmanDecode(buf, d.maxStrLen, p[:strLen]); err != nil {
+                       buf.Reset()
                        return "", nil, err
                }
                s = buf.String()