aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrad Fitzpatrick <bradfitz@golang.org>2016-08-01 23:44:22 +0000
committerBrad Fitzpatrick <bradfitz@golang.org>2016-08-02 00:33:01 +0000
commitc558a539b5efaeda4b6f8e61f51c21f64d1b94f6 (patch)
tree8d4043d72a174ba0db1c19ee14595092774551e1
parent111d590f86e2c9a55ec08d95fc4e9adea9232f0c (diff)
downloadgo-c558a539b5efaeda4b6f8e61f51c21f64d1b94f6.tar.gz
go-c558a539b5efaeda4b6f8e61f51c21f64d1b94f6.zip
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/25388 Reviewed-by: Chris Broadfoot <cbro@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
-rw-r--r--src/net/http/h2_bundle.go230
1 files changed, 141 insertions, 89 deletions
diff --git a/src/net/http/h2_bundle.go b/src/net/http/h2_bundle.go
index a117897bcf..cd66c0960e 100644
--- a/src/net/http/h2_bundle.go
+++ b/src/net/http/h2_bundle.go
@@ -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 {