Commit c558a539 authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

net/http: update bundled http2

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