1
2
3
4
5
6
7 package quic
8
9 import (
10 "context"
11 "crypto/tls"
12 "fmt"
13 "testing"
14 )
15
16
17
18 func lostFrameTest(t *testing.T, f func(t *testing.T, pto bool)) {
19 t.Run("lost", func(t *testing.T) {
20 f(t, false)
21 })
22 t.Run("pto", func(t *testing.T) {
23 f(t, true)
24 })
25 }
26
27
28
29 func (tc *testConn) triggerLossOrPTO(ptype packetType, pto bool) {
30 tc.t.Helper()
31 if pto {
32 if !tc.conn.loss.ptoTimerArmed {
33 tc.t.Fatalf("PTO timer not armed, expected it to be")
34 }
35 if *testVV {
36 tc.t.Logf("advancing to PTO timer")
37 }
38 tc.advanceTo(tc.conn.loss.timer)
39 return
40 }
41 if *testVV {
42 *testVV = false
43 defer func() {
44 tc.t.Logf("cause conn to declare last packet lost")
45 *testVV = true
46 }()
47 }
48 defer func(ignoreFrames map[byte]bool) {
49 tc.ignoreFrames = ignoreFrames
50 }(tc.ignoreFrames)
51 tc.ignoreFrames = map[byte]bool{
52 frameTypeAck: true,
53 frameTypePadding: true,
54 }
55
56
57
58 const lossThreshold = 3
59 var num packetNumber
60 for i := 0; i < lossThreshold; i++ {
61 tc.conn.ping(spaceForPacketType(ptype))
62 d := tc.readDatagram()
63 if d == nil {
64 tc.t.Fatalf("conn is idle; want PING frame")
65 }
66 if d.packets[0].ptype != ptype {
67 tc.t.Fatalf("conn sent %v packet; want %v", d.packets[0].ptype, ptype)
68 }
69 num = d.packets[0].num
70 }
71 tc.writeFrames(ptype, debugFrameAck{
72 ranges: []i64range[packetNumber]{
73 {num, num + 1},
74 },
75 })
76 }
77
78 func TestLostResetStreamFrame(t *testing.T) {
79
80
81
82 lostFrameTest(t, func(t *testing.T, pto bool) {
83 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, permissiveTransportParameters)
84 tc.ignoreFrame(frameTypeAck)
85
86 s.Reset(1)
87 tc.wantFrame("reset stream",
88 packetType1RTT, debugFrameResetStream{
89 id: s.id,
90 code: 1,
91 })
92
93 tc.triggerLossOrPTO(packetType1RTT, pto)
94 tc.wantFrame("resent RESET_STREAM frame",
95 packetType1RTT, debugFrameResetStream{
96 id: s.id,
97 code: 1,
98 })
99 })
100 }
101
102 func TestLostStopSendingFrame(t *testing.T) {
103
104
105
106
107
108
109
110
111 lostFrameTest(t, func(t *testing.T, pto bool) {
112 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, permissiveTransportParameters)
113 tc.ignoreFrame(frameTypeAck)
114
115 s.CloseRead()
116 tc.wantFrame("stream is read-closed",
117 packetType1RTT, debugFrameStopSending{
118 id: s.id,
119 })
120
121 tc.triggerLossOrPTO(packetType1RTT, pto)
122 tc.wantFrame("resent STOP_SENDING frame",
123 packetType1RTT, debugFrameStopSending{
124 id: s.id,
125 })
126 })
127 }
128
129 func TestLostCryptoFrame(t *testing.T) {
130
131
132 lostFrameTest(t, func(t *testing.T, pto bool) {
133 tc := newTestConn(t, clientSide)
134 tc.ignoreFrame(frameTypeAck)
135
136 tc.wantFrame("client sends Initial CRYPTO frame",
137 packetTypeInitial, debugFrameCrypto{
138 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
139 })
140 tc.triggerLossOrPTO(packetTypeInitial, pto)
141 tc.wantFrame("client resends Initial CRYPTO frame",
142 packetTypeInitial, debugFrameCrypto{
143 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
144 })
145
146 tc.writeFrames(packetTypeInitial,
147 debugFrameCrypto{
148 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
149 })
150 tc.writeFrames(packetTypeHandshake,
151 debugFrameCrypto{
152 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
153 })
154
155 tc.wantFrame("client sends Handshake CRYPTO frame",
156 packetTypeHandshake, debugFrameCrypto{
157 data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
158 })
159 tc.wantFrame("client provides server with an additional connection ID",
160 packetType1RTT, debugFrameNewConnectionID{
161 seq: 1,
162 connID: testLocalConnID(1),
163 token: testLocalStatelessResetToken(1),
164 })
165 tc.triggerLossOrPTO(packetTypeHandshake, pto)
166 tc.wantFrame("client resends Handshake CRYPTO frame",
167 packetTypeHandshake, debugFrameCrypto{
168 data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
169 })
170 })
171 }
172
173 func TestLostStreamFrameEmpty(t *testing.T) {
174
175
176 lostFrameTest(t, func(t *testing.T, pto bool) {
177 ctx := canceledContext()
178 tc := newTestConn(t, clientSide, permissiveTransportParameters)
179 tc.handshake()
180 tc.ignoreFrame(frameTypeAck)
181
182 c, err := tc.conn.NewStream(ctx)
183 if err != nil {
184 t.Fatalf("NewStream: %v", err)
185 }
186 c.Flush()
187 tc.wantFrame("created bidirectional stream 0",
188 packetType1RTT, debugFrameStream{
189 id: newStreamID(clientSide, bidiStream, 0),
190 data: []byte{},
191 })
192
193 tc.triggerLossOrPTO(packetType1RTT, pto)
194 tc.wantFrame("resent stream frame",
195 packetType1RTT, debugFrameStream{
196 id: newStreamID(clientSide, bidiStream, 0),
197 data: []byte{},
198 })
199 })
200 }
201
202 func TestLostStreamWithData(t *testing.T) {
203
204
205
206
207
208 lostFrameTest(t, func(t *testing.T, pto bool) {
209 data := []byte{0, 1, 2, 3, 4, 5, 6, 7}
210 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
211 p.initialMaxStreamsUni = 1
212 p.initialMaxData = 1 << 20
213 p.initialMaxStreamDataUni = 1 << 20
214 })
215 s.Write(data[:4])
216 s.Flush()
217 tc.wantFrame("send [0,4)",
218 packetType1RTT, debugFrameStream{
219 id: s.id,
220 off: 0,
221 data: data[:4],
222 })
223 s.Write(data[4:8])
224 s.Flush()
225 tc.wantFrame("send [4,8)",
226 packetType1RTT, debugFrameStream{
227 id: s.id,
228 off: 4,
229 data: data[4:8],
230 })
231 s.CloseWrite()
232 tc.wantFrame("send FIN",
233 packetType1RTT, debugFrameStream{
234 id: s.id,
235 off: 8,
236 fin: true,
237 data: []byte{},
238 })
239
240 tc.triggerLossOrPTO(packetType1RTT, pto)
241 tc.wantFrame("resend data",
242 packetType1RTT, debugFrameStream{
243 id: s.id,
244 off: 0,
245 fin: true,
246 data: data[:8],
247 })
248 })
249 }
250
251 func TestLostStreamPartialLoss(t *testing.T) {
252
253
254
255
256
257
258
259
260 data := []byte{0, 1, 2, 3}
261 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
262 p.initialMaxStreamsUni = 1
263 p.initialMaxData = 1 << 20
264 p.initialMaxStreamDataUni = 1 << 20
265 })
266 for i := range data {
267 s.Write(data[i : i+1])
268 s.Flush()
269 tc.wantFrame(fmt.Sprintf("send STREAM frame with byte %v", i),
270 packetType1RTT, debugFrameStream{
271 id: s.id,
272 off: int64(i),
273 data: data[i : i+1],
274 })
275 if i%2 == 0 {
276 tc.writeAckForLatest()
277 }
278 }
279 const pto = false
280 tc.triggerLossOrPTO(packetType1RTT, pto)
281 tc.wantFrame("resend byte 1",
282 packetType1RTT, debugFrameStream{
283 id: s.id,
284 off: 1,
285 data: data[1:2],
286 })
287 tc.wantFrame("resend byte 3",
288 packetType1RTT, debugFrameStream{
289 id: s.id,
290 off: 3,
291 data: data[3:4],
292 })
293 tc.wantIdle("no more frames sent after packet loss")
294 }
295
296 func TestLostMaxDataFrame(t *testing.T) {
297
298
299
300 lostFrameTest(t, func(t *testing.T, pto bool) {
301 const maxWindowSize = 32
302 buf := make([]byte, maxWindowSize)
303 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
304 c.MaxConnReadBufferSize = 32
305 })
306
307
308 tc.writeFrames(packetType1RTT, debugFrameStream{
309 id: s.id,
310 off: 0,
311 data: make([]byte, maxWindowSize-1),
312 })
313 if n, err := s.Read(buf[:maxWindowSize]); err != nil || n != maxWindowSize-1 {
314 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize-1)
315 }
316 tc.wantFrame("conn window is extended after reading data",
317 packetType1RTT, debugFrameMaxData{
318 max: (maxWindowSize * 2) - 1,
319 })
320
321
322 tc.writeFrames(packetType1RTT, debugFrameStream{
323 id: s.id,
324 off: maxWindowSize - 1,
325 data: make([]byte, 1),
326 })
327 if n, err := s.Read(buf[:1]); err != nil || n != 1 {
328 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
329 }
330 tc.wantIdle("read doesn't extend window enough to send another MAX_DATA")
331
332
333 tc.triggerLossOrPTO(packetType1RTT, pto)
334 tc.wantFrame("resent MAX_DATA includes most current value",
335 packetType1RTT, debugFrameMaxData{
336 max: maxWindowSize * 2,
337 })
338 })
339 }
340
341 func TestLostMaxStreamDataFrame(t *testing.T) {
342
343
344
345 lostFrameTest(t, func(t *testing.T, pto bool) {
346 const maxWindowSize = 32
347 buf := make([]byte, maxWindowSize)
348 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
349 c.MaxStreamReadBufferSize = maxWindowSize
350 })
351
352
353 tc.writeFrames(packetType1RTT, debugFrameStream{
354 id: s.id,
355 off: 0,
356 data: make([]byte, maxWindowSize-1),
357 })
358 if n, err := s.Read(buf[:maxWindowSize]); err != nil || n != maxWindowSize-1 {
359 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize-1)
360 }
361 tc.wantFrame("stream window is extended after reading data",
362 packetType1RTT, debugFrameMaxStreamData{
363 id: s.id,
364 max: (maxWindowSize * 2) - 1,
365 })
366
367
368 tc.writeFrames(packetType1RTT, debugFrameStream{
369 id: s.id,
370 off: maxWindowSize - 1,
371 data: make([]byte, 1),
372 })
373 if n, err := s.Read(buf); err != nil || n != 1 {
374 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
375 }
376 tc.wantIdle("read doesn't extend window enough to send another MAX_STREAM_DATA")
377
378
379 tc.triggerLossOrPTO(packetType1RTT, pto)
380 tc.wantFrame("resent MAX_STREAM_DATA includes most current value",
381 packetType1RTT, debugFrameMaxStreamData{
382 id: s.id,
383 max: maxWindowSize * 2,
384 })
385 })
386 }
387
388 func TestLostMaxStreamDataFrameAfterStreamFinReceived(t *testing.T) {
389
390
391
392 lostFrameTest(t, func(t *testing.T, pto bool) {
393 const maxWindowSize = 10
394 buf := make([]byte, maxWindowSize)
395 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
396 c.MaxStreamReadBufferSize = maxWindowSize
397 })
398
399 tc.writeFrames(packetType1RTT, debugFrameStream{
400 id: s.id,
401 off: 0,
402 data: make([]byte, maxWindowSize),
403 })
404 if n, err := s.Read(buf); err != nil || n != maxWindowSize {
405 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize)
406 }
407 tc.wantFrame("stream window is extended after reading data",
408 packetType1RTT, debugFrameMaxStreamData{
409 id: s.id,
410 max: 2 * maxWindowSize,
411 })
412
413 tc.writeFrames(packetType1RTT, debugFrameStream{
414 id: s.id,
415 off: maxWindowSize,
416 fin: true,
417 })
418
419 tc.ignoreFrame(frameTypePing)
420 tc.triggerLossOrPTO(packetType1RTT, pto)
421 tc.wantIdle("lost MAX_STREAM_DATA not resent for stream in 'size known'")
422 })
423 }
424
425 func TestLostMaxStreamsFrameMostRecent(t *testing.T) {
426
427
428
429 testStreamTypes(t, "", func(t *testing.T, styp streamType) {
430 lostFrameTest(t, func(t *testing.T, pto bool) {
431 ctx := canceledContext()
432 tc := newTestConn(t, serverSide, func(c *Config) {
433 c.MaxUniRemoteStreams = 1
434 c.MaxBidiRemoteStreams = 1
435 })
436 tc.handshake()
437 tc.ignoreFrame(frameTypeAck)
438 tc.writeFrames(packetType1RTT, debugFrameStream{
439 id: newStreamID(clientSide, styp, 0),
440 fin: true,
441 })
442 s, err := tc.conn.AcceptStream(ctx)
443 if err != nil {
444 t.Fatalf("AcceptStream() = %v", err)
445 }
446 s.SetWriteContext(ctx)
447 s.Close()
448 if styp == bidiStream {
449 tc.wantFrame("stream is closed",
450 packetType1RTT, debugFrameStream{
451 id: s.id,
452 data: []byte{},
453 fin: true,
454 })
455 tc.writeAckForAll()
456 }
457 tc.wantFrame("closing stream updates peer's MAX_STREAMS",
458 packetType1RTT, debugFrameMaxStreams{
459 streamType: styp,
460 max: 2,
461 })
462
463 tc.triggerLossOrPTO(packetType1RTT, pto)
464 tc.wantFrame("lost MAX_STREAMS is resent",
465 packetType1RTT, debugFrameMaxStreams{
466 streamType: styp,
467 max: 2,
468 })
469 })
470 })
471 }
472
473 func TestLostMaxStreamsFrameNotMostRecent(t *testing.T) {
474
475
476
477
478 const pto = false
479 ctx := canceledContext()
480 tc := newTestConn(t, serverSide, func(c *Config) {
481 c.MaxUniRemoteStreams = 2
482 })
483 tc.handshake()
484 tc.ignoreFrame(frameTypeAck)
485 for i := int64(0); i < 2; i++ {
486 tc.writeFrames(packetType1RTT, debugFrameStream{
487 id: newStreamID(clientSide, uniStream, i),
488 fin: true,
489 })
490 s, err := tc.conn.AcceptStream(ctx)
491 if err != nil {
492 t.Fatalf("AcceptStream() = %v", err)
493 }
494 if err := s.Close(); err != nil {
495 t.Fatalf("stream.Close() = %v", err)
496 }
497 tc.wantFrame("closing stream updates peer's MAX_STREAMS",
498 packetType1RTT, debugFrameMaxStreams{
499 streamType: uniStream,
500 max: 3 + i,
501 })
502 }
503
504
505 tc.writeAckForLatest()
506
507
508 tc.conn.ping(appDataSpace)
509 tc.wantFrame("connection should send a PING frame",
510 packetType1RTT, debugFramePing{})
511 tc.triggerLossOrPTO(packetType1RTT, pto)
512 tc.wantIdle("superseded MAX_DATA is not resent on loss")
513 }
514
515 func TestLostStreamDataBlockedFrame(t *testing.T) {
516
517
518
519 lostFrameTest(t, func(t *testing.T, pto bool) {
520 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
521 p.initialMaxStreamsUni = 1
522 p.initialMaxData = 1 << 20
523 })
524
525 w := runAsync(tc, func(ctx context.Context) (int, error) {
526 return s.Write([]byte{0, 1, 2, 3})
527 })
528 defer w.cancel()
529 tc.wantFrame("write is blocked by flow control",
530 packetType1RTT, debugFrameStreamDataBlocked{
531 id: s.id,
532 max: 0,
533 })
534
535 tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
536 id: s.id,
537 max: 1,
538 })
539 tc.wantFrame("write makes some progress, but is still blocked by flow control",
540 packetType1RTT, debugFrameStreamDataBlocked{
541 id: s.id,
542 max: 1,
543 })
544 tc.wantFrame("write consuming available window",
545 packetType1RTT, debugFrameStream{
546 id: s.id,
547 off: 0,
548 data: []byte{0},
549 })
550
551 tc.triggerLossOrPTO(packetType1RTT, pto)
552 tc.wantFrame("STREAM_DATA_BLOCKED is resent",
553 packetType1RTT, debugFrameStreamDataBlocked{
554 id: s.id,
555 max: 1,
556 })
557 tc.wantFrame("STREAM is resent as well",
558 packetType1RTT, debugFrameStream{
559 id: s.id,
560 off: 0,
561 data: []byte{0},
562 })
563 })
564 }
565
566 func TestLostStreamDataBlockedFrameAfterStreamUnblocked(t *testing.T) {
567
568
569
570 lostFrameTest(t, func(t *testing.T, pto bool) {
571 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
572 p.initialMaxStreamsUni = 1
573 p.initialMaxData = 1 << 20
574 })
575
576 data := []byte{0, 1, 2, 3}
577 w := runAsync(tc, func(ctx context.Context) (int, error) {
578 return s.Write(data)
579 })
580 defer w.cancel()
581 tc.wantFrame("write is blocked by flow control",
582 packetType1RTT, debugFrameStreamDataBlocked{
583 id: s.id,
584 max: 0,
585 })
586
587 tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
588 id: s.id,
589 max: 10,
590 })
591 tc.wantFrame("write completes after flow control available",
592 packetType1RTT, debugFrameStream{
593 id: s.id,
594 off: 0,
595 data: data,
596 })
597
598 tc.triggerLossOrPTO(packetType1RTT, pto)
599 tc.wantFrame("STREAM data is resent",
600 packetType1RTT, debugFrameStream{
601 id: s.id,
602 off: 0,
603 data: data,
604 })
605 tc.wantIdle("STREAM_DATA_BLOCKED is not resent, since the stream is not blocked")
606 })
607 }
608
609 func TestLostNewConnectionIDFrame(t *testing.T) {
610
611
612 lostFrameTest(t, func(t *testing.T, pto bool) {
613 tc := newTestConn(t, serverSide)
614 tc.handshake()
615 tc.ignoreFrame(frameTypeAck)
616
617 tc.writeFrames(packetType1RTT,
618 debugFrameRetireConnectionID{
619 seq: 1,
620 })
621 tc.wantFrame("provide a new connection ID after peer retires old one",
622 packetType1RTT, debugFrameNewConnectionID{
623 seq: 2,
624 connID: testLocalConnID(2),
625 token: testLocalStatelessResetToken(2),
626 })
627
628 tc.triggerLossOrPTO(packetType1RTT, pto)
629 tc.wantFrame("resend new connection ID",
630 packetType1RTT, debugFrameNewConnectionID{
631 seq: 2,
632 connID: testLocalConnID(2),
633 token: testLocalStatelessResetToken(2),
634 })
635 })
636 }
637
638 func TestLostRetireConnectionIDFrame(t *testing.T) {
639
640
641
642 lostFrameTest(t, func(t *testing.T, pto bool) {
643 tc := newTestConn(t, clientSide)
644 tc.handshake()
645 tc.ignoreFrame(frameTypeAck)
646
647 tc.writeFrames(packetType1RTT,
648 debugFrameNewConnectionID{
649 seq: 2,
650 retirePriorTo: 1,
651 connID: testPeerConnID(2),
652 })
653 tc.wantFrame("peer requested connection id be retired",
654 packetType1RTT, debugFrameRetireConnectionID{
655 seq: 0,
656 })
657
658 tc.triggerLossOrPTO(packetType1RTT, pto)
659 tc.wantFrame("resend RETIRE_CONNECTION_ID",
660 packetType1RTT, debugFrameRetireConnectionID{
661 seq: 0,
662 })
663 })
664 }
665
666 func TestLostPathResponseFrame(t *testing.T) {
667
668
669 lostFrameTest(t, func(t *testing.T, pto bool) {
670 tc := newTestConn(t, clientSide)
671 tc.handshake()
672 tc.ignoreFrame(frameTypeAck)
673 tc.ignoreFrame(frameTypePing)
674
675 data := pathChallengeData{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef}
676 tc.writeFrames(packetType1RTT, debugFramePathChallenge{
677 data: data,
678 })
679 tc.wantFrame("response to PATH_CHALLENGE",
680 packetType1RTT, debugFramePathResponse{
681 data: data,
682 })
683
684 tc.triggerLossOrPTO(packetType1RTT, pto)
685 tc.wantIdle("lost PATH_RESPONSE frame is not retransmitted")
686 })
687 }
688
689 func TestLostHandshakeDoneFrame(t *testing.T) {
690
691
692 lostFrameTest(t, func(t *testing.T, pto bool) {
693 tc := newTestConn(t, serverSide)
694 tc.ignoreFrame(frameTypeAck)
695
696 tc.writeFrames(packetTypeInitial,
697 debugFrameCrypto{
698 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
699 })
700 tc.wantFrame("server sends Initial CRYPTO frame",
701 packetTypeInitial, debugFrameCrypto{
702 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
703 })
704 tc.wantFrame("server sends Handshake CRYPTO frame",
705 packetTypeHandshake, debugFrameCrypto{
706 data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
707 })
708 tc.wantFrame("server provides an additional connection ID",
709 packetType1RTT, debugFrameNewConnectionID{
710 seq: 1,
711 connID: testLocalConnID(1),
712 token: testLocalStatelessResetToken(1),
713 })
714 tc.writeFrames(packetTypeHandshake,
715 debugFrameCrypto{
716 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
717 })
718
719 tc.wantFrame("server sends HANDSHAKE_DONE after handshake completes",
720 packetType1RTT, debugFrameHandshakeDone{})
721
722 tc.triggerLossOrPTO(packetType1RTT, pto)
723 tc.wantFrame("server resends HANDSHAKE_DONE",
724 packetType1RTT, debugFrameHandshakeDone{})
725 })
726 }
727
View as plain text