Skip to content

Commit 341cd08

Browse files
committed
http2: add Transport strictness, paranoia, logging for unwanted DATA frames
I can't reproduce the user's bug yet, but this might fix or at least help clarify what's happening. Also deflakes a test. Updates golang/go#13932 Change-Id: If56bdd833f183d4502701e65e56749434bd82150 Reviewed-on: https://go-review.googlesource.com/18576 Reviewed-by: Andrew Gerrand <[email protected]>
1 parent e82aa26 commit 341cd08

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

http2/transport.go

+29-11
Original file line numberDiff line numberDiff line change
@@ -1255,22 +1255,40 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
12551255
cc := rl.cc
12561256
cs := cc.streamByID(f.StreamID, f.StreamEnded())
12571257
if cs == nil {
1258+
cc.mu.Lock()
1259+
neverSent := cc.nextStreamID
1260+
cc.mu.Unlock()
1261+
if f.StreamID >= neverSent {
1262+
// We never asked for this.
1263+
cc.logf("http2: Transport received unsolicited DATA frame; closing connection")
1264+
return ConnectionError(ErrCodeProtocol)
1265+
}
1266+
// We probably did ask for this, but canceled. Just ignore it.
1267+
// TODO: be stricter here? only silently ignore things which
1268+
// we canceled, but not things which were closed normally
1269+
// by the peer? Tough without accumulating too much state.
12581270
return nil
12591271
}
1260-
data := f.Data()
1272+
if data := f.Data(); len(data) > 0 {
1273+
if cs.bufPipe.b == nil {
1274+
// Data frame after it's already closed?
1275+
cc.logf("http2: Transport received DATA frame for closed stream; closing connection")
1276+
return ConnectionError(ErrCodeProtocol)
1277+
}
12611278

1262-
// Check connection-level flow control.
1263-
cc.mu.Lock()
1264-
if cs.inflow.available() >= int32(len(data)) {
1265-
cs.inflow.take(int32(len(data)))
1266-
} else {
1279+
// Check connection-level flow control.
1280+
cc.mu.Lock()
1281+
if cs.inflow.available() >= int32(len(data)) {
1282+
cs.inflow.take(int32(len(data)))
1283+
} else {
1284+
cc.mu.Unlock()
1285+
return ConnectionError(ErrCodeFlowControl)
1286+
}
12671287
cc.mu.Unlock()
1268-
return ConnectionError(ErrCodeFlowControl)
1269-
}
1270-
cc.mu.Unlock()
12711288

1272-
if _, err := cs.bufPipe.Write(data); err != nil {
1273-
return err
1289+
if _, err := cs.bufPipe.Write(data); err != nil {
1290+
return err
1291+
}
12741292
}
12751293

12761294
if f.StreamEnded() {

http2/transport_test.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -1230,16 +1230,17 @@ func TestTransportChecksResponseHeaderListSize(t *testing.T) {
12301230
}
12311231
}
12321232
ct.run()
1233-
12341233
}
12351234

12361235
// Test that the the Transport returns a typed error from Response.Body.Read calls
12371236
// when the server sends an error. (here we use a panic, since that should generate
12381237
// a stream error, but others like cancel should be similar)
12391238
func TestTransportBodyReadErrorType(t *testing.T) {
1239+
doPanic := make(chan bool, 1)
12401240
st := newServerTester(t,
12411241
func(w http.ResponseWriter, r *http.Request) {
12421242
w.(http.Flusher).Flush() // force headers out
1243+
<-doPanic
12431244
panic("boom")
12441245
},
12451246
optOnlyServer,
@@ -1256,6 +1257,7 @@ func TestTransportBodyReadErrorType(t *testing.T) {
12561257
t.Fatal(err)
12571258
}
12581259
defer res.Body.Close()
1260+
doPanic <- true
12591261
buf := make([]byte, 100)
12601262
n, err := res.Body.Read(buf)
12611263
want := StreamError{StreamID: 0x1, Code: 0x2}

0 commit comments

Comments
 (0)