golang.org/x/net@v0.25.1-0.20240516223405-c87a5b62e243/quic/conn_loss_test.go (about) 1 // Copyright 2023 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 //go:build go1.21 6 7 package quic 8 9 import ( 10 "context" 11 "crypto/tls" 12 "fmt" 13 "testing" 14 ) 15 16 // Frames may be retransmitted either when the packet containing the frame is lost, or on PTO. 17 // lostFrameTest runs a test in both configurations. 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 // triggerLossOrPTO causes the conn to declare the last sent packet lost, 28 // or advances to the PTO timer. 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 // Send three packets containing PINGs, and then respond with an ACK for the 56 // last one. This puts the last packet before the PINGs outside the packet 57 // reordering threshold, and it will be declared lost. 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 // "Cancellation of stream transmission, as carried in a RESET_STREAM frame, 80 // is sent until acknowledged or until all stream data is acknowledged by the peer [...]" 81 // https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.4 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 // "[...] a request to cancel stream transmission, as encoded in a STOP_SENDING frame, 104 // is sent until the receiving part of the stream enters either a "Data Recvd" or 105 // "Reset Recvd" state [...]" 106 // https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.5 107 // 108 // Technically, we can stop sending a STOP_SENDING frame if the peer sends 109 // us all the data for the stream or resets it. We don't bother tracking this, 110 // however, so we'll keep sending the frame until it is acked. This is harmless. 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 // "Data sent in CRYPTO frames is retransmitted [...] until all data has been acknowledged." 131 // https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.1 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 // A STREAM frame opening a stream, but containing no stream data, should 175 // be retransmitted if lost. 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() // open the stream 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 // "Application data sent in STREAM frames is retransmitted in new STREAM 204 // frames unless the endpoint has sent a RESET_STREAM for that stream." 205 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.2 206 // 207 // TODO: Lost stream frame after RESET_STREAM 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 // Conn sends four STREAM packets. 253 // ACKs are received for the packets containing bytes 0 and 2. 254 // The remaining packets are declared lost. 255 // The Conn resends only the lost data. 256 // 257 // This test doesn't have a PTO mode, because the ACK for the packet containing byte 2 258 // starts the loss timer for the packet containing byte 1, and the PTO timer is not 259 // armed when the loss timer is. 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 // "An updated value is sent in a MAX_DATA frame if the packet 298 // containing the most recently sent MAX_DATA frame is declared lost [...]" 299 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.7 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 // We send MAX_DATA = 63. 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 // MAX_DATA = 64, which is only one more byte, so we don't send the frame. 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 // The MAX_DATA = 63 packet was lost, so we send 64. 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 // "[...] an updated value is sent when the packet containing 343 // the most recent MAX_STREAM_DATA frame for a stream is lost" 344 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.8 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 // We send MAX_STREAM_DATA = 63. 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 // MAX_STREAM_DATA = 64, which is only one more byte, so we don't send the frame. 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 // The MAX_STREAM_DATA = 63 packet was lost, so we send 64. 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 // "An endpoint SHOULD stop sending MAX_STREAM_DATA frames when 390 // the receiving part of the stream enters a "Size Known" or "Reset Recvd" state." 391 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.8 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 // "[...] an updated value is sent when a packet containing the 427 // most recent MAX_STREAMS for a stream type frame is declared lost [...]" 428 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.9 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 // Send two MAX_STREAMS frames, lose the first one. 475 // 476 // No PTO mode for this test: The ack that causes the first frame 477 // to be lost arms the loss timer for the second, so the PTO timer is not armed. 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 // The second MAX_STREAMS frame is acked. 505 tc.writeAckForLatest() 506 507 // The first MAX_STREAMS frame is lost. 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 // "A new [STREAM_DATA_BLOCKED] frame is sent if a packet containing 517 // the most recent frame for a scope is lost [...]" 518 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.10 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 // "A new [STREAM_DATA_BLOCKED] frame is sent [...] only while 568 // the endpoint is blocked on the corresponding limit." 569 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.10 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 // "New connection IDs are [...] retransmitted if the packet containing them is lost." 611 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.13 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 // "[...] retired connection IDs are [...] retransmitted 640 // if the packet containing them is lost." 641 // https://www.rfc-editor.org/rfc/rfc9000#section-13.3-3.13 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 // "Responses to path validation using PATH_RESPONSE frames are sent just once." 668 // https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.12 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 // "The HANDSHAKE_DONE frame MUST be retransmitted until it is acknowledged." 691 // https://www.rfc-editor.org/rfc/rfc9000.html#section-13.3-3.16 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 }