]> Cypherpunks repositories - gostls13.git/commitdiff
net/http: update bundled http2
authorBrad Fitzpatrick <bradfitz@golang.org>
Mon, 1 Aug 2016 23:44:22 +0000 (23:44 +0000)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 2 Aug 2016 00:33:01 +0000 (00:33 +0000)
Updates bundled http2 to x/net/http2 rev 28d1bd4f for:

    http2: make Transport work around mod_h2 bug
    https://golang.org/cl/25362

    http2: don't ignore DATA padding in flow control
    https://golang.org/cl/25382

Updates #16519
Updates #16556
Updates #16481

Change-Id: I51f5696e977c91bdb2d80d2d56b8a78e3222da3f
Reviewed-on: https://go-review.googlesource.com/25388
Reviewed-by: Chris Broadfoot <cbro@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/net/http/h2_bundle.go

index a117897bcf42f866b5621f718f42e6573c7fbe66..cd66c0960e76c33812c7db0b4c857274cc2820f5 100644 (file)
@@ -1115,6 +1115,7 @@ func http2parseDataFrame(fh http2FrameHeader, payload []byte) (http2Frame, error
 var (
        http2errStreamID    = errors.New("invalid stream ID")
        http2errDepStreamID = errors.New("invalid dependent stream ID")
+       http2errPadLength   = errors.New("pad length too large")
 )
 
 func http2validStreamIDOrZero(streamID uint32) bool {
@@ -1128,18 +1129,40 @@ func http2validStreamID(streamID uint32) bool {
 // WriteData writes a DATA frame.
 //
 // It will perform exactly one Write to the underlying Writer.
-// It is the caller's responsibility to not call other Write methods concurrently.
+// It is the caller's responsibility not to violate the maximum frame size
+// and to not call other Write methods concurrently.
 func (f *http2Framer) WriteData(streamID uint32, endStream bool, data []byte) error {
+       return f.WriteDataPadded(streamID, endStream, data, nil)
+}
 
+// WriteData writes a DATA frame with optional padding.
+//
+// If pad is nil, the padding bit is not sent.
+// The length of pad must not exceed 255 bytes.
+//
+// It will perform exactly one Write to the underlying Writer.
+// It is the caller's responsibility not to violate the maximum frame size
+// and to not call other Write methods concurrently.
+func (f *http2Framer) WriteDataPadded(streamID uint32, endStream bool, data, pad []byte) error {
        if !http2validStreamID(streamID) && !f.AllowIllegalWrites {
                return http2errStreamID
        }
+       if len(pad) > 255 {
+               return http2errPadLength
+       }
        var flags http2Flags
        if endStream {
                flags |= http2FlagDataEndStream
        }
+       if pad != nil {
+               flags |= http2FlagDataPadded
+       }
        f.startWrite(http2FrameData, flags, streamID)
+       if pad != nil {
+               f.wbuf = append(f.wbuf, byte(len(pad)))
+       }
        f.wbuf = append(f.wbuf, data...)
+       f.wbuf = append(f.wbuf, pad...)
        return f.endWrite()
 }
 
@@ -3898,12 +3921,12 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
        st, ok := sc.streams[id]
        if !ok || st.state != http2stateOpen || st.gotTrailerHeader {
 
-               if int(sc.inflow.available()) < len(data) {
+               if sc.inflow.available() < int32(f.Length) {
                        return http2StreamError{id, http2ErrCodeFlowControl}
                }
 
-               sc.inflow.take(int32(len(data)))
-               sc.sendWindowUpdate(nil, len(data))
+               sc.inflow.take(int32(f.Length))
+               sc.sendWindowUpdate(nil, int(f.Length))
 
                return http2StreamError{id, http2ErrCodeStreamClosed}
        }
@@ -3915,20 +3938,28 @@ func (sc *http2serverConn) processData(f *http2DataFrame) error {
                st.body.CloseWithError(fmt.Errorf("sender tried to send more than declared Content-Length of %d bytes", st.declBodyBytes))
                return http2StreamError{id, http2ErrCodeStreamClosed}
        }
-       if len(data) > 0 {
+       if f.Length > 0 {
 
-               if int(st.inflow.available()) < len(data) {
+               if st.inflow.available() < int32(f.Length) {
                        return http2StreamError{id, http2ErrCodeFlowControl}
                }
-               st.inflow.take(int32(len(data)))
-               wrote, err := st.body.Write(data)
-               if err != nil {
-                       return http2StreamError{id, http2ErrCodeStreamClosed}
+               st.inflow.take(int32(f.Length))
+
+               if len(data) > 0 {
+                       wrote, err := st.body.Write(data)
+                       if err != nil {
+                               return http2StreamError{id, http2ErrCodeStreamClosed}
+                       }
+                       if wrote != len(data) {
+                               panic("internal error: bad Writer")
+                       }
+                       st.bodyBytes += int64(len(data))
                }
-               if wrote != len(data) {
-                       panic("internal error: bad Writer")
+
+               if pad := int32(f.Length) - int32(len(data)); pad > 0 {
+                       sc.sendWindowUpdate32(nil, pad)
+                       sc.sendWindowUpdate32(st, pad)
                }
-               st.bodyBytes += int64(len(data))
        }
        if f.StreamEnded() {
                st.endStream()
@@ -4948,19 +4979,20 @@ type http2ClientConn struct {
        readerDone chan struct{} // closed on error
        readerErr  error         // set before readerDone is closed
 
-       mu           sync.Mutex // guards following
-       cond         *sync.Cond // hold mu; broadcast on flow/closed changes
-       flow         http2flow  // our conn-level flow control quota (cs.flow is per stream)
-       inflow       http2flow  // peer's conn-level flow control
-       closed       bool
-       goAway       *http2GoAwayFrame             // if non-nil, the GoAwayFrame we received
-       goAwayDebug  string                        // goAway frame's debug data, retained as a string
-       streams      map[uint32]*http2clientStream // client-initiated
-       nextStreamID uint32
-       bw           *bufio.Writer
-       br           *bufio.Reader
-       fr           *http2Framer
-       lastActive   time.Time
+       mu              sync.Mutex // guards following
+       cond            *sync.Cond // hold mu; broadcast on flow/closed changes
+       flow            http2flow  // our conn-level flow control quota (cs.flow is per stream)
+       inflow          http2flow  // peer's conn-level flow control
+       closed          bool
+       wantSettingsAck bool                          // we sent a SETTINGS frame and haven't heard back
+       goAway          *http2GoAwayFrame             // if non-nil, the GoAwayFrame we received
+       goAwayDebug     string                        // goAway frame's debug data, retained as a string
+       streams         map[uint32]*http2clientStream // client-initiated
+       nextStreamID    uint32
+       bw              *bufio.Writer
+       br              *bufio.Reader
+       fr              *http2Framer
+       lastActive      time.Time
 
        // Settings from peer:
        maxFrameSize         uint32
@@ -5215,10 +5247,6 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
        if http2VerboseLogs {
                t.vlogf("http2: Transport creating client conn to %v", c.RemoteAddr())
        }
-       if _, err := c.Write(http2clientPreface); err != nil {
-               t.vlogf("client preface write error: %v", err)
-               return nil, err
-       }
 
        cc := &http2ClientConn{
                t:                    t,
@@ -5230,6 +5258,7 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
                maxConcurrentStreams: 1000,
                streams:              make(map[uint32]*http2clientStream),
                singleUse:            singleUse,
+               wantSettingsAck:      true,
        }
        cc.cond = sync.NewCond(&cc.mu)
        cc.flow.add(int32(http2initialWindowSize))
@@ -5254,6 +5283,8 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
        if max := t.maxHeaderListSize(); max != 0 {
                initialSettings = append(initialSettings, http2Setting{ID: http2SettingMaxHeaderListSize, Val: max})
        }
+
+       cc.bw.Write(http2clientPreface)
        cc.fr.WriteSettings(initialSettings...)
        cc.fr.WriteWindowUpdate(0, http2transportDefaultConnFlow)
        cc.inflow.add(http2transportDefaultConnFlow + http2initialWindowSize)
@@ -5262,32 +5293,6 @@ func (t *http2Transport) newClientConn(c net.Conn, singleUse bool) (*http2Client
                return nil, cc.werr
        }
 
-       f, err := cc.fr.ReadFrame()
-       if err != nil {
-               return nil, err
-       }
-       sf, ok := f.(*http2SettingsFrame)
-       if !ok {
-               return nil, fmt.Errorf("expected settings frame, got: %T", f)
-       }
-       cc.fr.WriteSettingsAck()
-       cc.bw.Flush()
-
-       sf.ForeachSetting(func(s http2Setting) error {
-               switch s.ID {
-               case http2SettingMaxFrameSize:
-                       cc.maxFrameSize = s.Val
-               case http2SettingMaxConcurrentStreams:
-                       cc.maxConcurrentStreams = s.Val
-               case http2SettingInitialWindowSize:
-                       cc.initialWindowSize = s.Val
-               default:
-
-                       t.vlogf("Unhandled Setting: %v", s)
-               }
-               return nil
-       })
-
        go cc.readLoop()
        return cc, nil
 }
@@ -5687,26 +5692,24 @@ func (cs *http2clientStream) writeRequestBody(body io.Reader, bodyCloser io.Clos
                }
        }
 
+       var trls []byte
+       if !sentEnd && hasTrailers {
+               cc.mu.Lock()
+               defer cc.mu.Unlock()
+               trls = cc.encodeTrailers(req)
+       }
+
        cc.wmu.Lock()
-       if !sentEnd {
-               var trls []byte
-               if hasTrailers {
-                       cc.mu.Lock()
-                       trls = cc.encodeTrailers(req)
-                       cc.mu.Unlock()
-               }
+       defer cc.wmu.Unlock()
 
-               if len(trls) > 0 {
-                       err = cc.writeHeaders(cs.ID, true, trls)
-               } else {
-                       err = cc.fr.WriteData(cs.ID, true, nil)
-               }
+       if len(trls) > 0 {
+               err = cc.writeHeaders(cs.ID, true, trls)
+       } else {
+               err = cc.fr.WriteData(cs.ID, true, nil)
        }
        if ferr := cc.bw.Flush(); ferr != nil && err == nil {
                err = ferr
        }
-       cc.wmu.Unlock()
-
        return err
 }
 
@@ -5935,6 +5938,14 @@ func (e http2GoAwayError) Error() string {
                e.LastStreamID, e.ErrCode, e.DebugData)
 }
 
+func http2isEOFOrNetReadError(err error) bool {
+       if err == io.EOF {
+               return true
+       }
+       ne, ok := err.(*net.OpError)
+       return ok && ne.Op == "read"
+}
+
 func (rl *http2clientConnReadLoop) cleanup() {
        cc := rl.cc
        defer cc.tconn.Close()
@@ -5943,16 +5954,14 @@ func (rl *http2clientConnReadLoop) cleanup() {
 
        err := cc.readerErr
        cc.mu.Lock()
-       if err == io.EOF {
-               if cc.goAway != nil {
-                       err = http2GoAwayError{
-                               LastStreamID: cc.goAway.LastStreamID,
-                               ErrCode:      cc.goAway.ErrCode,
-                               DebugData:    cc.goAwayDebug,
-                       }
-               } else {
-                       err = io.ErrUnexpectedEOF
+       if cc.goAway != nil && http2isEOFOrNetReadError(err) {
+               err = http2GoAwayError{
+                       LastStreamID: cc.goAway.LastStreamID,
+                       ErrCode:      cc.goAway.ErrCode,
+                       DebugData:    cc.goAwayDebug,
                }
+       } else if err == io.EOF {
+               err = io.ErrUnexpectedEOF
        }
        for _, cs := range rl.activeRes {
                cs.bufPipe.CloseWithError(err)
@@ -5973,6 +5982,7 @@ func (rl *http2clientConnReadLoop) run() error {
        cc := rl.cc
        rl.closeWhenIdle = cc.t.disableKeepAlives() || cc.singleUse
        gotReply := false
+       gotSettings := false
        for {
                f, err := cc.fr.ReadFrame()
                if err != nil {
@@ -5989,6 +5999,13 @@ func (rl *http2clientConnReadLoop) run() error {
                if http2VerboseLogs {
                        cc.vlogf("http2: Transport received %s", http2summarizeFrame(f))
                }
+               if !gotSettings {
+                       if _, ok := f.(*http2SettingsFrame); !ok {
+                               cc.logf("protocol error: received %T before a SETTINGS frame", f)
+                               return http2ConnectionError(http2ErrCodeProtocol)
+                       }
+                       gotSettings = true
+               }
                maybeIdle := false
 
                switch f := f.(type) {
@@ -6294,33 +6311,49 @@ func (rl *http2clientConnReadLoop) processData(f *http2DataFrame) error {
                        return http2ConnectionError(http2ErrCodeProtocol)
                }
 
-               if len(data) > 0 {
+               if f.Length > 0 {
+                       cc.mu.Lock()
+                       cc.inflow.add(int32(f.Length))
+                       cc.mu.Unlock()
+
                        cc.wmu.Lock()
-                       cc.fr.WriteWindowUpdate(0, uint32(len(data)))
+                       cc.fr.WriteWindowUpdate(0, uint32(f.Length))
                        cc.bw.Flush()
                        cc.wmu.Unlock()
                }
                return nil
        }
-       if len(data) > 0 {
-               if cs.bufPipe.b == nil {
+       if f.Length > 0 {
+               if len(data) > 0 && cs.bufPipe.b == nil {
 
                        cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
                        return http2ConnectionError(http2ErrCodeProtocol)
                }
 
                cc.mu.Lock()
-               if cs.inflow.available() >= int32(len(data)) {
-                       cs.inflow.take(int32(len(data)))
+               if cs.inflow.available() >= int32(f.Length) {
+                       cs.inflow.take(int32(f.Length))
                } else {
                        cc.mu.Unlock()
                        return http2ConnectionError(http2ErrCodeFlowControl)
                }
+
+               if pad := int32(f.Length) - int32(len(data)); pad > 0 {
+                       cs.inflow.add(pad)
+                       cc.inflow.add(pad)
+                       cc.wmu.Lock()
+                       cc.fr.WriteWindowUpdate(0, uint32(pad))
+                       cc.fr.WriteWindowUpdate(cs.ID, uint32(pad))
+                       cc.bw.Flush()
+                       cc.wmu.Unlock()
+               }
                cc.mu.Unlock()
 
-               if _, err := cs.bufPipe.Write(data); err != nil {
-                       rl.endStreamError(cs, err)
-                       return err
+               if len(data) > 0 {
+                       if _, err := cs.bufPipe.Write(data); err != nil {
+                               rl.endStreamError(cs, err)
+                               return err
+                       }
                }
        }
 
@@ -6375,7 +6408,16 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error
        cc := rl.cc
        cc.mu.Lock()
        defer cc.mu.Unlock()
-       return f.ForeachSetting(func(s http2Setting) error {
+
+       if f.IsAck() {
+               if cc.wantSettingsAck {
+                       cc.wantSettingsAck = false
+                       return nil
+               }
+               return http2ConnectionError(http2ErrCodeProtocol)
+       }
+
+       err := f.ForeachSetting(func(s http2Setting) error {
                switch s.ID {
                case http2SettingMaxFrameSize:
                        cc.maxFrameSize = s.Val
@@ -6390,6 +6432,16 @@ func (rl *http2clientConnReadLoop) processSettings(f *http2SettingsFrame) error
                }
                return nil
        })
+       if err != nil {
+               return err
+       }
+
+       cc.wmu.Lock()
+       defer cc.wmu.Unlock()
+
+       cc.fr.WriteSettingsAck()
+       cc.bw.Flush()
+       return cc.werr
 }
 
 func (rl *http2clientConnReadLoop) processWindowUpdate(f *http2WindowUpdateFrame) error {