Skip to content

Commit c186220

Browse files
aojeaneild
authored andcommittedMay 31, 2022
http2: prioritize RST_STREAM frames in priority write scheduler
The http2 priority write scheduler should not queue RST_STREAM frames with the DATA frames, and instead treat them as control frames. There can be deadlock situations if data frames block the queue, because if the sender wants to close the stream it sends an RST frame, but if the client is not draining the queue, the RST frame is stuck and the sender is not able to finish. Fixes golang/go#49741 Signed-off-by: Antonio Ojea <antonio.ojea.garcia@gmail.com> Change-Id: Ie4462603380039f7aba8f701fe810d1d84376efa Reviewed-on: https://go-review.googlesource.com/c/net/+/382014 Reviewed-by: Ian Lance Taylor <iant@google.com> Reviewed-by: Damien Neil <dneil@google.com> Run-TryBot: Dave Cheney <dave@cheney.net> TryBot-Result: Gopher Robot <gobot@golang.org>
1 parent 5463443 commit c186220

File tree

2 files changed

+27
-5
lines changed

2 files changed

+27
-5
lines changed
 

‎http2/writesched_priority.go

+4-5
Original file line numberDiff line numberDiff line change
@@ -383,16 +383,15 @@ func (ws *priorityWriteScheduler) AdjustStream(streamID uint32, priority Priorit
383383

384384
func (ws *priorityWriteScheduler) Push(wr FrameWriteRequest) {
385385
var n *priorityNode
386-
if id := wr.StreamID(); id == 0 {
386+
if wr.isControl() {
387387
n = &ws.root
388388
} else {
389+
id := wr.StreamID()
389390
n = ws.nodes[id]
390391
if n == nil {
391392
// id is an idle or closed stream. wr should not be a HEADERS or
392-
// DATA frame. However, wr can be a RST_STREAM. In this case, we
393-
// push wr onto the root, rather than creating a new priorityNode,
394-
// since RST_STREAM is tiny and the stream's priority is unknown
395-
// anyway. See issue #17919.
393+
// DATA frame. In other case, we push wr onto the root, rather
394+
// than creating a new priorityNode.
396395
if wr.DataSize() > 0 {
397396
panic("add DATA on non-open stream")
398397
}

‎http2/writesched_priority_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,29 @@ func TestPriorityPopFrom533Tree(t *testing.T) {
387387
}
388388
}
389389

390+
// #49741 RST_STREAM and Control frames should have more priority than data
391+
// frames to avoid blocking streams caused by clients not able to drain the
392+
// queue.
393+
func TestPriorityRSTFrames(t *testing.T) {
394+
ws := defaultPriorityWriteScheduler()
395+
ws.OpenStream(1, OpenStreamOptions{})
396+
397+
sc := &serverConn{maxFrameSize: 16}
398+
st1 := &stream{id: 1, sc: sc}
399+
400+
ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 16), false}, st1, nil})
401+
ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 16), false}, st1, nil})
402+
ws.Push(makeWriteRSTStream(1))
403+
// No flow-control bytes available.
404+
wr, ok := ws.Pop()
405+
if !ok {
406+
t.Fatalf("Pop should work for control frames and not be limited by flow control")
407+
}
408+
if _, ok := wr.write.(StreamError); !ok {
409+
t.Fatal("expected RST stream frames first", wr)
410+
}
411+
}
412+
390413
func TestPriorityPopFromLinearTree(t *testing.T) {
391414
ws := defaultPriorityWriteScheduler()
392415
ws.OpenStream(1, OpenStreamOptions{})

0 commit comments

Comments
 (0)