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),
312 })
313 if n, err := s.Read(buf[:maxWindowSize-1]); 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 if n, err := s.Read(buf); err != nil || n != 1 {
323 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
324 }
325 tc.wantIdle("read doesn't extend window enough to send another MAX_DATA")
326
327
328 tc.triggerLossOrPTO(packetType1RTT, pto)
329 tc.wantFrame("resent MAX_DATA includes most current value",
330 packetType1RTT, debugFrameMaxData{
331 max: maxWindowSize * 2,
332 })
333 })
334 }
335
336 func TestLostMaxStreamDataFrame(t *testing.T) {
337
338
339
340 lostFrameTest(t, func(t *testing.T, pto bool) {
341 const maxWindowSize = 32
342 buf := make([]byte, maxWindowSize)
343 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
344 c.MaxStreamReadBufferSize = maxWindowSize
345 })
346
347
348 tc.writeFrames(packetType1RTT, debugFrameStream{
349 id: s.id,
350 off: 0,
351 data: make([]byte, maxWindowSize),
352 })
353 if n, err := s.Read(buf[:maxWindowSize-1]); err != nil || n != maxWindowSize-1 {
354 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize-1)
355 }
356 tc.wantFrame("stream window is extended after reading data",
357 packetType1RTT, debugFrameMaxStreamData{
358 id: s.id,
359 max: (maxWindowSize * 2) - 1,
360 })
361
362
363 if n, err := s.Read(buf); err != nil || n != 1 {
364 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, 1)
365 }
366 tc.wantIdle("read doesn't extend window enough to send another MAX_STREAM_DATA")
367
368
369 tc.triggerLossOrPTO(packetType1RTT, pto)
370 tc.wantFrame("resent MAX_STREAM_DATA includes most current value",
371 packetType1RTT, debugFrameMaxStreamData{
372 id: s.id,
373 max: maxWindowSize * 2,
374 })
375 })
376 }
377
378 func TestLostMaxStreamDataFrameAfterStreamFinReceived(t *testing.T) {
379
380
381
382 lostFrameTest(t, func(t *testing.T, pto bool) {
383 const maxWindowSize = 10
384 buf := make([]byte, maxWindowSize)
385 tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
386 c.MaxStreamReadBufferSize = maxWindowSize
387 })
388
389 tc.writeFrames(packetType1RTT, debugFrameStream{
390 id: s.id,
391 off: 0,
392 data: make([]byte, maxWindowSize),
393 })
394 if n, err := s.Read(buf); err != nil || n != maxWindowSize {
395 t.Fatalf("Read() = %v, %v; want %v, nil", n, err, maxWindowSize)
396 }
397 tc.wantFrame("stream window is extended after reading data",
398 packetType1RTT, debugFrameMaxStreamData{
399 id: s.id,
400 max: 2 * maxWindowSize,
401 })
402
403 tc.writeFrames(packetType1RTT, debugFrameStream{
404 id: s.id,
405 off: maxWindowSize,
406 fin: true,
407 })
408
409 tc.ignoreFrame(frameTypePing)
410 tc.triggerLossOrPTO(packetType1RTT, pto)
411 tc.wantIdle("lost MAX_STREAM_DATA not resent for stream in 'size known'")
412 })
413 }
414
415 func TestLostMaxStreamsFrameMostRecent(t *testing.T) {
416
417
418
419 testStreamTypes(t, "", func(t *testing.T, styp streamType) {
420 lostFrameTest(t, func(t *testing.T, pto bool) {
421 ctx := canceledContext()
422 tc := newTestConn(t, serverSide, func(c *Config) {
423 c.MaxUniRemoteStreams = 1
424 c.MaxBidiRemoteStreams = 1
425 })
426 tc.handshake()
427 tc.ignoreFrame(frameTypeAck)
428 tc.writeFrames(packetType1RTT, debugFrameStream{
429 id: newStreamID(clientSide, styp, 0),
430 fin: true,
431 })
432 s, err := tc.conn.AcceptStream(ctx)
433 if err != nil {
434 t.Fatalf("AcceptStream() = %v", err)
435 }
436 s.CloseContext(ctx)
437 if styp == bidiStream {
438 tc.wantFrame("stream is closed",
439 packetType1RTT, debugFrameStream{
440 id: s.id,
441 data: []byte{},
442 fin: true,
443 })
444 tc.writeAckForAll()
445 }
446 tc.wantFrame("closing stream updates peer's MAX_STREAMS",
447 packetType1RTT, debugFrameMaxStreams{
448 streamType: styp,
449 max: 2,
450 })
451
452 tc.triggerLossOrPTO(packetType1RTT, pto)
453 tc.wantFrame("lost MAX_STREAMS is resent",
454 packetType1RTT, debugFrameMaxStreams{
455 streamType: styp,
456 max: 2,
457 })
458 })
459 })
460 }
461
462 func TestLostMaxStreamsFrameNotMostRecent(t *testing.T) {
463
464
465
466
467 const pto = false
468 ctx := canceledContext()
469 tc := newTestConn(t, serverSide, func(c *Config) {
470 c.MaxUniRemoteStreams = 2
471 })
472 tc.handshake()
473 tc.ignoreFrame(frameTypeAck)
474 for i := int64(0); i < 2; i++ {
475 tc.writeFrames(packetType1RTT, debugFrameStream{
476 id: newStreamID(clientSide, uniStream, i),
477 fin: true,
478 })
479 s, err := tc.conn.AcceptStream(ctx)
480 if err != nil {
481 t.Fatalf("AcceptStream() = %v", err)
482 }
483 if err := s.CloseContext(ctx); err != nil {
484 t.Fatalf("stream.Close() = %v", err)
485 }
486 tc.wantFrame("closing stream updates peer's MAX_STREAMS",
487 packetType1RTT, debugFrameMaxStreams{
488 streamType: uniStream,
489 max: 3 + i,
490 })
491 }
492
493
494 tc.writeAckForLatest()
495
496
497 tc.conn.ping(appDataSpace)
498 tc.wantFrame("connection should send a PING frame",
499 packetType1RTT, debugFramePing{})
500 tc.triggerLossOrPTO(packetType1RTT, pto)
501 tc.wantIdle("superseded MAX_DATA is not resent on loss")
502 }
503
504 func TestLostStreamDataBlockedFrame(t *testing.T) {
505
506
507
508 lostFrameTest(t, func(t *testing.T, pto bool) {
509 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
510 p.initialMaxStreamsUni = 1
511 p.initialMaxData = 1 << 20
512 })
513
514 w := runAsync(tc, func(ctx context.Context) (int, error) {
515 return s.WriteContext(ctx, []byte{0, 1, 2, 3})
516 })
517 defer w.cancel()
518 tc.wantFrame("write is blocked by flow control",
519 packetType1RTT, debugFrameStreamDataBlocked{
520 id: s.id,
521 max: 0,
522 })
523
524 tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
525 id: s.id,
526 max: 1,
527 })
528 tc.wantFrame("write makes some progress, but is still blocked by flow control",
529 packetType1RTT, debugFrameStreamDataBlocked{
530 id: s.id,
531 max: 1,
532 })
533 tc.wantFrame("write consuming available window",
534 packetType1RTT, debugFrameStream{
535 id: s.id,
536 off: 0,
537 data: []byte{0},
538 })
539
540 tc.triggerLossOrPTO(packetType1RTT, pto)
541 tc.wantFrame("STREAM_DATA_BLOCKED is resent",
542 packetType1RTT, debugFrameStreamDataBlocked{
543 id: s.id,
544 max: 1,
545 })
546 tc.wantFrame("STREAM is resent as well",
547 packetType1RTT, debugFrameStream{
548 id: s.id,
549 off: 0,
550 data: []byte{0},
551 })
552 })
553 }
554
555 func TestLostStreamDataBlockedFrameAfterStreamUnblocked(t *testing.T) {
556
557
558
559 lostFrameTest(t, func(t *testing.T, pto bool) {
560 tc, s := newTestConnAndLocalStream(t, serverSide, uniStream, func(p *transportParameters) {
561 p.initialMaxStreamsUni = 1
562 p.initialMaxData = 1 << 20
563 })
564
565 data := []byte{0, 1, 2, 3}
566 w := runAsync(tc, func(ctx context.Context) (int, error) {
567 return s.WriteContext(ctx, data)
568 })
569 defer w.cancel()
570 tc.wantFrame("write is blocked by flow control",
571 packetType1RTT, debugFrameStreamDataBlocked{
572 id: s.id,
573 max: 0,
574 })
575
576 tc.writeFrames(packetType1RTT, debugFrameMaxStreamData{
577 id: s.id,
578 max: 10,
579 })
580 tc.wantFrame("write completes after flow control available",
581 packetType1RTT, debugFrameStream{
582 id: s.id,
583 off: 0,
584 data: data,
585 })
586
587 tc.triggerLossOrPTO(packetType1RTT, pto)
588 tc.wantFrame("STREAM data is resent",
589 packetType1RTT, debugFrameStream{
590 id: s.id,
591 off: 0,
592 data: data,
593 })
594 tc.wantIdle("STREAM_DATA_BLOCKED is not resent, since the stream is not blocked")
595 })
596 }
597
598 func TestLostNewConnectionIDFrame(t *testing.T) {
599
600
601 lostFrameTest(t, func(t *testing.T, pto bool) {
602 tc := newTestConn(t, serverSide)
603 tc.handshake()
604 tc.ignoreFrame(frameTypeAck)
605
606 tc.writeFrames(packetType1RTT,
607 debugFrameRetireConnectionID{
608 seq: 1,
609 })
610 tc.wantFrame("provide a new connection ID after peer retires old one",
611 packetType1RTT, debugFrameNewConnectionID{
612 seq: 2,
613 connID: testLocalConnID(2),
614 token: testLocalStatelessResetToken(2),
615 })
616
617 tc.triggerLossOrPTO(packetType1RTT, pto)
618 tc.wantFrame("resend new connection ID",
619 packetType1RTT, debugFrameNewConnectionID{
620 seq: 2,
621 connID: testLocalConnID(2),
622 token: testLocalStatelessResetToken(2),
623 })
624 })
625 }
626
627 func TestLostRetireConnectionIDFrame(t *testing.T) {
628
629
630
631 lostFrameTest(t, func(t *testing.T, pto bool) {
632 tc := newTestConn(t, clientSide)
633 tc.handshake()
634 tc.ignoreFrame(frameTypeAck)
635
636 tc.writeFrames(packetType1RTT,
637 debugFrameNewConnectionID{
638 seq: 2,
639 retirePriorTo: 1,
640 connID: testPeerConnID(2),
641 })
642 tc.wantFrame("peer requested connection id be retired",
643 packetType1RTT, debugFrameRetireConnectionID{
644 seq: 0,
645 })
646
647 tc.triggerLossOrPTO(packetType1RTT, pto)
648 tc.wantFrame("resend RETIRE_CONNECTION_ID",
649 packetType1RTT, debugFrameRetireConnectionID{
650 seq: 0,
651 })
652 })
653 }
654
655 func TestLostHandshakeDoneFrame(t *testing.T) {
656
657
658 lostFrameTest(t, func(t *testing.T, pto bool) {
659 tc := newTestConn(t, serverSide)
660 tc.ignoreFrame(frameTypeAck)
661
662 tc.writeFrames(packetTypeInitial,
663 debugFrameCrypto{
664 data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
665 })
666 tc.wantFrame("server sends Initial CRYPTO frame",
667 packetTypeInitial, debugFrameCrypto{
668 data: tc.cryptoDataOut[tls.QUICEncryptionLevelInitial],
669 })
670 tc.wantFrame("server sends Handshake CRYPTO frame",
671 packetTypeHandshake, debugFrameCrypto{
672 data: tc.cryptoDataOut[tls.QUICEncryptionLevelHandshake],
673 })
674 tc.wantFrame("server provides an additional connection ID",
675 packetType1RTT, debugFrameNewConnectionID{
676 seq: 1,
677 connID: testLocalConnID(1),
678 token: testLocalStatelessResetToken(1),
679 })
680 tc.writeFrames(packetTypeHandshake,
681 debugFrameCrypto{
682 data: tc.cryptoDataIn[tls.QUICEncryptionLevelHandshake],
683 })
684
685 tc.wantFrame("server sends HANDSHAKE_DONE after handshake completes",
686 packetType1RTT, debugFrameHandshakeDone{})
687
688 tc.triggerLossOrPTO(packetType1RTT, pto)
689 tc.wantFrame("server resends HANDSHAKE_DONE",
690 packetType1RTT, debugFrameHandshakeDone{})
691 })
692 }
693
View as plain text