quic: fix wrong final size being used for RESET_STREAM frame Previously, the final size in a RESET_STREAM frame was calculated as min(s.outwin, s.out.end). This could result in a final size value that is larger than the amount of data actually sent, such as when data is buffered but blocked due to connection-level flow control. Therefore, change RESET_STREAM frame to use s.outmaxsent as the final size, which tracks the actual offset of data that has been sent. This might perhaps explain the rare flakes that we have been observing, where HTTP/3 tests would hang indefinitely. For golang/go#78737 Change-Id: I31466712e9fe75d5fcb4461fb6905f4a6a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/774943 LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Nicholas Husin <husin@google.com> Reviewed-by: Damien Neil <dneil@google.com>
diff --git a/quic/stream.go b/quic/stream.go index 445780c..ee7c569 100644 --- a/quic/stream.go +++ b/quic/stream.go
@@ -1013,7 +1013,7 @@ if s.outreset.isSet() { // RESET_STREAM if s.outreset.shouldSendPTO(pto) { - if !w.appendResetStreamFrame(s.id, s.outresetcode, min(s.outwin, s.out.end)) { + if !w.appendResetStreamFrame(s.id, s.outresetcode, s.outmaxsent) { return false } s.outreset.setSent(pnum)
diff --git a/quic/stream_test.go b/quic/stream_test.go index 6b587f5..0a51f1d 100644 --- a/quic/stream_test.go +++ b/quic/stream_test.go
@@ -1064,6 +1064,30 @@ } } +func TestStreamResetWithUnsentData(t *testing.T) { + synctest.Test(t, testStreamResetWithUnsentData) +} +func testStreamResetWithUnsentData(t *testing.T) { + tc, s := newTestConnAndLocalStream(t, clientSide, uniStream, + func(p *transportParameters) { + p.initialMaxStreamsUni = maxStreamsLimit + p.initialMaxData = 0 + p.initialMaxStreamDataUni = 1000 + }) + + s.Write([]byte("data")) + s.Flush() + tc.wantIdle("data is blocked by connection-level flow control") + + s.Reset(0) + tc.wantFrame("stream is reset", + packetType1RTT, debugFrameResetStream{ + id: s.id, + code: 0, + finalSize: 0, // We sent no bytes. + }) +} + func TestStreamCloseWaitsForAcks(t *testing.T) { synctest.Test(t, testStreamCloseWaitsForAcks) }