1
2
3
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
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
114
115
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
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
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
181
182 ws.OpenStream(5, OpenStreamOptions{})
183 ws.AdjustStream(5, PriorityParam{StreamDep: 1, Weight: 15, Exclusive: true})
184
185
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
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})
220 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15})
221 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20})
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})
241 ws.AdjustStream(2, PriorityParam{StreamDep: 0, Weight: 15})
242 ws.AdjustStream(3, PriorityParam{StreamDep: 2, Weight: 20})
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
256
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
278
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
300
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
313
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
337
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 ))
380 ws.Push(makeWriteNonStreamRequest())
381 ws.Push(makeWriteHeadersRequest(5 ))
382 ws.Push(makeWriteHeadersRequest(1 ))
383 t.Log("tree:", fmtTree(ws, fmtNodeParentSkipRoot))
384
385 if err := checkPopAll(ws, []uint32{0 , 1, 3, 5}); err != nil {
386 t.Error(err)
387 }
388 }
389
390
391
392
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
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 , 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
447 if wr, ok := ws.Pop(); ok {
448 t.Fatalf("Pop(limited by flow control)=%v,true, want false", wr)
449 }
450
451
452
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
479
480
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
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
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
535
536
537
538
539
540
541
542
543
544
545
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 }
565
View as plain text