golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/http2/writesched_priority_test.go (about) 1 // Copyright 2016 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package http2 6 7 import ( 8 "bytes" 9 "fmt" 10 "sort" 11 "testing" 12 ) 13 14 func defaultPriorityWriteScheduler() *priorityWriteScheduler { 15 return NewPriorityWriteScheduler(nil).(*priorityWriteScheduler) 16 } 17 18 func checkPriorityWellFormed(ws *priorityWriteScheduler) error { 19 for id, n := range ws.nodes { 20 if id != n.id { 21 return fmt.Errorf("bad ws.nodes: ws.nodes[%d] = %d", id, n.id) 22 } 23 if n.parent == nil { 24 if n.next != nil || n.prev != nil { 25 return fmt.Errorf("bad node %d: nil parent but prev/next not nil", id) 26 } 27 continue 28 } 29 found := false 30 for k := n.parent.kids; k != nil; k = k.next { 31 if k.id == id { 32 found = true 33 break 34 } 35 } 36 if !found { 37 return fmt.Errorf("bad node %d: not found in parent %d kids list", id, n.parent.id) 38 } 39 } 40 return nil 41 } 42 43 func fmtTree(ws *priorityWriteScheduler, fmtNode func(*priorityNode) string) string { 44 var ids []int 45 for _, n := range ws.nodes { 46 ids = append(ids, int(n.id)) 47 } 48 sort.Ints(ids) 49 50 var buf bytes.Buffer 51 for _, id := range ids { 52 if buf.Len() != 0 { 53 buf.WriteString(" ") 54 } 55 if id == 0 { 56 buf.WriteString(fmtNode(&ws.root)) 57 } else { 58 buf.WriteString(fmtNode(ws.nodes[uint32(id)])) 59 } 60 } 61 return buf.String() 62 } 63 64 func fmtNodeParentSkipRoot(n *priorityNode) string { 65 switch { 66 case n.id == 0: 67 return "" 68 case n.parent == nil: 69 return fmt.Sprintf("%d{parent:nil}", n.id) 70 default: 71 return fmt.Sprintf("%d{parent:%d}", n.id, n.parent.id) 72 } 73 } 74 75 func fmtNodeWeightParentSkipRoot(n *priorityNode) string { 76 switch { 77 case n.id == 0: 78 return "" 79 case n.parent == nil: 80 return fmt.Sprintf("%d{weight:%d,parent:nil}", n.id, n.weight) 81 default: 82 return fmt.Sprintf("%d{weight:%d,parent:%d}", n.id, n.weight, n.parent.id) 83 } 84 } 85 86 func TestPriorityTwoStreams(t *testing.T) { 87 ws := defaultPriorityWriteScheduler() 88 ws.OpenStream(1, OpenStreamOptions{}) 89 ws.OpenStream(2, OpenStreamOptions{}) 90 91 want := "1{weight:15,parent:0} 2{weight:15,parent:0}" 92 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 93 t.Errorf("After open\ngot %q\nwant %q", got, want) 94 } 95 96 // Move 1's parent to 2. 97 ws.AdjustStream(1, PriorityParam{ 98 StreamDep: 2, 99 Weight: 32, 100 Exclusive: false, 101 }) 102 want = "1{weight:32,parent:2} 2{weight:15,parent:0}" 103 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 104 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 105 } 106 107 if err := checkPriorityWellFormed(ws); err != nil { 108 t.Error(err) 109 } 110 } 111 112 func TestPriorityAdjustExclusiveZero(t *testing.T) { 113 // 1, 2, and 3 are all children of the 0 stream. 114 // Exclusive reprioritization to any of the streams should bring 115 // the rest of the streams under the reprioritized stream. 116 ws := defaultPriorityWriteScheduler() 117 ws.OpenStream(1, OpenStreamOptions{}) 118 ws.OpenStream(2, OpenStreamOptions{}) 119 ws.OpenStream(3, OpenStreamOptions{}) 120 121 want := "1{weight:15,parent:0} 2{weight:15,parent:0} 3{weight:15,parent:0}" 122 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 123 t.Errorf("After open\ngot %q\nwant %q", got, want) 124 } 125 126 ws.AdjustStream(2, PriorityParam{ 127 StreamDep: 0, 128 Weight: 20, 129 Exclusive: true, 130 }) 131 want = "1{weight:15,parent:2} 2{weight:20,parent:0} 3{weight:15,parent:2}" 132 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 133 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 134 } 135 136 if err := checkPriorityWellFormed(ws); err != nil { 137 t.Error(err) 138 } 139 } 140 141 func TestPriorityAdjustOwnParent(t *testing.T) { 142 // Assigning a node as its own parent should have no effect. 143 ws := defaultPriorityWriteScheduler() 144 ws.OpenStream(1, OpenStreamOptions{}) 145 ws.OpenStream(2, OpenStreamOptions{}) 146 ws.AdjustStream(2, PriorityParam{ 147 StreamDep: 2, 148 Weight: 20, 149 Exclusive: true, 150 }) 151 want := "1{weight:15,parent:0} 2{weight:15,parent:0}" 152 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 153 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 154 } 155 if err := checkPriorityWellFormed(ws); err != nil { 156 t.Error(err) 157 } 158 } 159 160 func TestPriorityClosedStreams(t *testing.T) { 161 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxClosedNodesInTree: 2}).(*priorityWriteScheduler) 162 ws.OpenStream(1, OpenStreamOptions{}) 163 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 164 ws.OpenStream(3, OpenStreamOptions{PusherID: 2}) 165 ws.OpenStream(4, OpenStreamOptions{PusherID: 3}) 166 167 // Close the first three streams. We lose 1, but keep 2 and 3. 168 ws.CloseStream(1) 169 ws.CloseStream(2) 170 ws.CloseStream(3) 171 172 want := "2{weight:15,parent:0} 3{weight:15,parent:2} 4{weight:15,parent:3}" 173 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 174 t.Errorf("After close\ngot %q\nwant %q", got, want) 175 } 176 if err := checkPriorityWellFormed(ws); err != nil { 177 t.Error(err) 178 } 179 180 // Adding a stream as an exclusive child of 1 gives it default 181 // priorities, since 1 is gone. 182 ws.OpenStream(5, OpenStreamOptions{}) 183 ws.AdjustStream(5, PriorityParam{StreamDep: 1, Weight: 15, Exclusive: true}) 184 185 // Adding a stream as an exclusive child of 2 should work, since 2 is not gone. 186 ws.OpenStream(6, OpenStreamOptions{}) 187 ws.AdjustStream(6, PriorityParam{StreamDep: 2, Weight: 15, Exclusive: true}) 188 189 want = "2{weight:15,parent:0} 3{weight:15,parent:6} 4{weight:15,parent:3} 5{weight:15,parent:0} 6{weight:15,parent:2}" 190 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 191 t.Errorf("After add streams\ngot %q\nwant %q", got, want) 192 } 193 if err := checkPriorityWellFormed(ws); err != nil { 194 t.Error(err) 195 } 196 } 197 198 func TestPriorityClosedStreamsDisabled(t *testing.T) { 199 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler) 200 ws.OpenStream(1, OpenStreamOptions{}) 201 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 202 ws.OpenStream(3, OpenStreamOptions{PusherID: 2}) 203 204 // Close the first two streams. We keep only 3. 205 ws.CloseStream(1) 206 ws.CloseStream(2) 207 208 want := "3{weight:15,parent:0}" 209 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 210 t.Errorf("After close\ngot %q\nwant %q", got, want) 211 } 212 if err := checkPriorityWellFormed(ws); err != nil { 213 t.Error(err) 214 } 215 } 216 217 func TestPriorityIdleStreams(t *testing.T) { 218 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{MaxIdleNodesInTree: 2}).(*priorityWriteScheduler) 219 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle 220 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle 221 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle 222 ws.OpenStream(4, OpenStreamOptions{}) 223 ws.OpenStream(5, OpenStreamOptions{}) 224 ws.OpenStream(6, OpenStreamOptions{}) 225 ws.AdjustStream(4, PriorityParam{StreamDep: 1, Weight: 15}) 226 ws.AdjustStream(5, PriorityParam{StreamDep: 2, Weight: 15}) 227 ws.AdjustStream(6, PriorityParam{StreamDep: 3, Weight: 15}) 228 229 want := "2{weight:15,parent:0} 3{weight:20,parent:2} 4{weight:15,parent:0} 5{weight:15,parent:2} 6{weight:15,parent:3}" 230 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 231 t.Errorf("After open\ngot %q\nwant %q", got, want) 232 } 233 if err := checkPriorityWellFormed(ws); err != nil { 234 t.Error(err) 235 } 236 } 237 238 func TestPriorityIdleStreamsDisabled(t *testing.T) { 239 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{}).(*priorityWriteScheduler) 240 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 15}) // idle 241 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15}) // idle 242 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20}) // idle 243 ws.OpenStream(4, OpenStreamOptions{}) 244 245 want := "4{weight:15,parent:0}" 246 if got := fmtTree(ws, fmtNodeWeightParentSkipRoot); got != want { 247 t.Errorf("After open\ngot %q\nwant %q", got, want) 248 } 249 if err := checkPriorityWellFormed(ws); err != nil { 250 t.Error(err) 251 } 252 } 253 254 func TestPrioritySection531NonExclusive(t *testing.T) { 255 // Example from RFC 7540 Section 5.3.1. 256 // A,B,C,D = 1,2,3,4 257 ws := defaultPriorityWriteScheduler() 258 ws.OpenStream(1, OpenStreamOptions{}) 259 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 260 ws.OpenStream(3, OpenStreamOptions{PusherID: 1}) 261 ws.OpenStream(4, OpenStreamOptions{}) 262 ws.AdjustStream(4, PriorityParam{ 263 StreamDep: 1, 264 Weight: 15, 265 Exclusive: false, 266 }) 267 want := "1{parent:0} 2{parent:1} 3{parent:1} 4{parent:1}" 268 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want { 269 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 270 } 271 if err := checkPriorityWellFormed(ws); err != nil { 272 t.Error(err) 273 } 274 } 275 276 func TestPrioritySection531Exclusive(t *testing.T) { 277 // Example from RFC 7540 Section 5.3.1. 278 // A,B,C,D = 1,2,3,4 279 ws := defaultPriorityWriteScheduler() 280 ws.OpenStream(1, OpenStreamOptions{}) 281 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 282 ws.OpenStream(3, OpenStreamOptions{PusherID: 1}) 283 ws.OpenStream(4, OpenStreamOptions{}) 284 ws.AdjustStream(4, PriorityParam{ 285 StreamDep: 1, 286 Weight: 15, 287 Exclusive: true, 288 }) 289 want := "1{parent:0} 2{parent:4} 3{parent:4} 4{parent:1}" 290 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want { 291 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 292 } 293 if err := checkPriorityWellFormed(ws); err != nil { 294 t.Error(err) 295 } 296 } 297 298 func makeSection533Tree() *priorityWriteScheduler { 299 // Initial tree from RFC 7540 Section 5.3.3. 300 // A,B,C,D,E,F = 1,2,3,4,5,6 301 ws := defaultPriorityWriteScheduler() 302 ws.OpenStream(1, OpenStreamOptions{}) 303 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 304 ws.OpenStream(3, OpenStreamOptions{PusherID: 1}) 305 ws.OpenStream(4, OpenStreamOptions{PusherID: 3}) 306 ws.OpenStream(5, OpenStreamOptions{PusherID: 3}) 307 ws.OpenStream(6, OpenStreamOptions{PusherID: 4}) 308 return ws 309 } 310 311 func TestPrioritySection533NonExclusive(t *testing.T) { 312 // Example from RFC 7540 Section 5.3.3. 313 // A,B,C,D,E,F = 1,2,3,4,5,6 314 ws := defaultPriorityWriteScheduler() 315 ws.OpenStream(1, OpenStreamOptions{}) 316 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 317 ws.OpenStream(3, OpenStreamOptions{PusherID: 1}) 318 ws.OpenStream(4, OpenStreamOptions{PusherID: 3}) 319 ws.OpenStream(5, OpenStreamOptions{PusherID: 3}) 320 ws.OpenStream(6, OpenStreamOptions{PusherID: 4}) 321 ws.AdjustStream(1, PriorityParam{ 322 StreamDep: 4, 323 Weight: 15, 324 Exclusive: false, 325 }) 326 want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:4}" 327 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want { 328 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 329 } 330 if err := checkPriorityWellFormed(ws); err != nil { 331 t.Error(err) 332 } 333 } 334 335 func TestPrioritySection533Exclusive(t *testing.T) { 336 // Example from RFC 7540 Section 5.3.3. 337 // A,B,C,D,E,F = 1,2,3,4,5,6 338 ws := defaultPriorityWriteScheduler() 339 ws.OpenStream(1, OpenStreamOptions{}) 340 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 341 ws.OpenStream(3, OpenStreamOptions{PusherID: 1}) 342 ws.OpenStream(4, OpenStreamOptions{PusherID: 3}) 343 ws.OpenStream(5, OpenStreamOptions{PusherID: 3}) 344 ws.OpenStream(6, OpenStreamOptions{PusherID: 4}) 345 ws.AdjustStream(1, PriorityParam{ 346 StreamDep: 4, 347 Weight: 15, 348 Exclusive: true, 349 }) 350 want := "1{parent:4} 2{parent:1} 3{parent:1} 4{parent:0} 5{parent:3} 6{parent:1}" 351 if got := fmtTree(ws, fmtNodeParentSkipRoot); got != want { 352 t.Errorf("After adjust\ngot %q\nwant %q", got, want) 353 } 354 if err := checkPriorityWellFormed(ws); err != nil { 355 t.Error(err) 356 } 357 } 358 359 func checkPopAll(ws WriteScheduler, order []uint32) error { 360 for k, id := range order { 361 wr, ok := ws.Pop() 362 if !ok { 363 return fmt.Errorf("Pop[%d]: got ok=false, want %d (order=%v)", k, id, order) 364 } 365 if got := wr.StreamID(); got != id { 366 return fmt.Errorf("Pop[%d]: got %v, want %d (order=%v)", k, got, id, order) 367 } 368 } 369 wr, ok := ws.Pop() 370 if ok { 371 return fmt.Errorf("Pop[%d]: got %v, want ok=false (order=%v)", len(order), wr.StreamID(), order) 372 } 373 return nil 374 } 375 376 func TestPriorityPopFrom533Tree(t *testing.T) { 377 ws := makeSection533Tree() 378 379 ws.Push(makeWriteHeadersRequest(3 /*C*/)) 380 ws.Push(makeWriteNonStreamRequest()) 381 ws.Push(makeWriteHeadersRequest(5 /*E*/)) 382 ws.Push(makeWriteHeadersRequest(1 /*A*/)) 383 t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot)) 384 385 if err := checkPopAll(ws, []uint32{0 /*NonStream*/, 1, 3, 5}); err != nil { 386 t.Error(err) 387 } 388 } 389 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 413 func TestPriorityPopFromLinearTree(t *testing.T) { 414 ws := defaultPriorityWriteScheduler() 415 ws.OpenStream(1, OpenStreamOptions{}) 416 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 417 ws.OpenStream(3, OpenStreamOptions{PusherID: 2}) 418 ws.OpenStream(4, OpenStreamOptions{PusherID: 3}) 419 420 ws.Push(makeWriteHeadersRequest(3)) 421 ws.Push(makeWriteHeadersRequest(4)) 422 ws.Push(makeWriteHeadersRequest(1)) 423 ws.Push(makeWriteHeadersRequest(2)) 424 ws.Push(makeWriteNonStreamRequest()) 425 ws.Push(makeWriteNonStreamRequest()) 426 t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot)) 427 428 if err := checkPopAll(ws, []uint32{0, 0 /*NonStreams*/, 1, 2, 3, 4}); err != nil { 429 t.Error(err) 430 } 431 } 432 433 func TestPriorityFlowControl(t *testing.T) { 434 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: false}) 435 ws.OpenStream(1, OpenStreamOptions{}) 436 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 437 438 sc := &serverConn{maxFrameSize: 16} 439 st1 := &stream{id: 1, sc: sc} 440 st2 := &stream{id: 2, sc: sc} 441 442 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 16), false}, st1, nil}) 443 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 16), false}, st2, nil}) 444 ws.AdjustStream(2, PriorityParam{StreamDep: 1}) 445 446 // No flow-control bytes available. 447 if wr, ok := ws.Pop(); ok { 448 t.Fatalf("Pop(limited by flow control)=%v,true, want false", wr) 449 } 450 451 // Add enough flow-control bytes to write st2 in two Pop calls. 452 // Should write data from st2 even though it's lower priority than st1. 453 for i := 1; i <= 2; i++ { 454 st2.flow.add(8) 455 wr, ok := ws.Pop() 456 if !ok { 457 t.Fatalf("Pop(%d)=false, want true", i) 458 } 459 if got, want := wr.DataSize(), 8; got != want { 460 t.Fatalf("Pop(%d)=%d bytes, want %d bytes", i, got, want) 461 } 462 } 463 } 464 465 func TestPriorityThrottleOutOfOrderWrites(t *testing.T) { 466 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ThrottleOutOfOrderWrites: true}) 467 ws.OpenStream(1, OpenStreamOptions{}) 468 ws.OpenStream(2, OpenStreamOptions{PusherID: 1}) 469 470 sc := &serverConn{maxFrameSize: 4096} 471 st1 := &stream{id: 1, sc: sc} 472 st2 := &stream{id: 2, sc: sc} 473 st1.flow.add(4096) 474 st2.flow.add(4096) 475 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 4096), false}, st2, nil}) 476 ws.AdjustStream(2, PriorityParam{StreamDep: 1}) 477 478 // We have enough flow-control bytes to write st2 in a single Pop call. 479 // However, due to out-of-order write throttling, the first call should 480 // only write 1KB. 481 wr, ok := ws.Pop() 482 if !ok { 483 t.Fatalf("Pop(st2.first)=false, want true") 484 } 485 if got, want := wr.StreamID(), uint32(2); got != want { 486 t.Fatalf("Pop(st2.first)=stream %d, want stream %d", got, want) 487 } 488 if got, want := wr.DataSize(), 1024; got != want { 489 t.Fatalf("Pop(st2.first)=%d bytes, want %d bytes", got, want) 490 } 491 492 // Now add data on st1. This should take precedence. 493 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 4096), false}, st1, nil}) 494 wr, ok = ws.Pop() 495 if !ok { 496 t.Fatalf("Pop(st1)=false, want true") 497 } 498 if got, want := wr.StreamID(), uint32(1); got != want { 499 t.Fatalf("Pop(st1)=stream %d, want stream %d", got, want) 500 } 501 if got, want := wr.DataSize(), 4096; got != want { 502 t.Fatalf("Pop(st1)=%d bytes, want %d bytes", got, want) 503 } 504 505 // Should go back to writing 1KB from st2. 506 wr, ok = ws.Pop() 507 if !ok { 508 t.Fatalf("Pop(st2.last)=false, want true") 509 } 510 if got, want := wr.StreamID(), uint32(2); got != want { 511 t.Fatalf("Pop(st2.last)=stream %d, want stream %d", got, want) 512 } 513 if got, want := wr.DataSize(), 1024; got != want { 514 t.Fatalf("Pop(st2.last)=%d bytes, want %d bytes", got, want) 515 } 516 } 517 518 func TestPriorityWeights(t *testing.T) { 519 ws := defaultPriorityWriteScheduler() 520 ws.OpenStream(1, OpenStreamOptions{}) 521 ws.OpenStream(2, OpenStreamOptions{}) 522 523 sc := &serverConn{maxFrameSize: 8} 524 st1 := &stream{id: 1, sc: sc} 525 st2 := &stream{id: 2, sc: sc} 526 st1.flow.add(40) 527 st2.flow.add(40) 528 529 ws.Push(FrameWriteRequest{&writeData{1, make([]byte, 40), false}, st1, nil}) 530 ws.Push(FrameWriteRequest{&writeData{2, make([]byte, 40), false}, st2, nil}) 531 ws.AdjustStream(1, PriorityParam{StreamDep: 0, Weight: 34}) 532 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 9}) 533 534 // st1 gets 3.5x the bandwidth of st2 (3.5 = (34+1)/(9+1)). 535 // The maximum frame size is 8 bytes. The write sequence should be: 536 // st1, total bytes so far is (st1=8, st=0) 537 // st2, total bytes so far is (st1=8, st=8) 538 // st1, total bytes so far is (st1=16, st=8) 539 // st1, total bytes so far is (st1=24, st=8) // 3x bandwidth 540 // st1, total bytes so far is (st1=32, st=8) // 4x bandwidth 541 // st2, total bytes so far is (st1=32, st=16) // 2x bandwidth 542 // st1, total bytes so far is (st1=40, st=16) 543 // st2, total bytes so far is (st1=40, st=24) 544 // st2, total bytes so far is (st1=40, st=32) 545 // st2, total bytes so far is (st1=40, st=40) 546 if err := checkPopAll(ws, []uint32{1, 2, 1, 1, 1, 2, 1, 2, 2, 2}); err != nil { 547 t.Error(err) 548 } 549 } 550 551 func TestPriorityRstStreamOnNonOpenStreams(t *testing.T) { 552 ws := NewPriorityWriteScheduler(&PriorityWriteSchedulerConfig{ 553 MaxClosedNodesInTree: 0, 554 MaxIdleNodesInTree: 0, 555 }) 556 ws.OpenStream(1, OpenStreamOptions{}) 557 ws.CloseStream(1) 558 ws.Push(FrameWriteRequest{write: streamError(1, ErrCodeProtocol)}) 559 ws.Push(FrameWriteRequest{write: streamError(2, ErrCodeProtocol)}) 560 561 if err := checkPopAll(ws, []uint32{1, 2}); err != nil { 562 t.Error(err) 563 } 564 }