github.com/TugasAkhir-QUIC/quic-go@v0.0.2-0.20240215011318-d20e25a9054c/integrationtests/self/cancelation_test.go (about) 1 package self_test 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "math/rand" 8 "net" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/TugasAkhir-QUIC/quic-go" 14 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 ) 18 19 var _ = Describe("Stream Cancellations", func() { 20 const numStreams = 80 21 22 Context("canceling the read side", func() { 23 var server *quic.Listener 24 25 // The server accepts a single connection, and then opens numStreams unidirectional streams. 26 // On each of these streams, it (tries to) write PRData. 27 // When done, it sends the number of canceled streams on the channel. 28 runServer := func(data []byte) <-chan int32 { 29 numCanceledStreamsChan := make(chan int32) 30 var err error 31 server, err = quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) 32 Expect(err).ToNot(HaveOccurred()) 33 34 var canceledCounter atomic.Int32 35 go func() { 36 defer GinkgoRecover() 37 var wg sync.WaitGroup 38 wg.Add(numStreams) 39 conn, err := server.Accept(context.Background()) 40 Expect(err).ToNot(HaveOccurred()) 41 for i := 0; i < numStreams; i++ { 42 go func() { 43 defer GinkgoRecover() 44 defer wg.Done() 45 str, err := conn.OpenUniStreamSync(context.Background()) 46 Expect(err).ToNot(HaveOccurred()) 47 if _, err := str.Write(data); err != nil { 48 Expect(err).To(Equal(&quic.StreamError{ 49 StreamID: str.StreamID(), 50 ErrorCode: quic.StreamErrorCode(str.StreamID()), 51 Remote: true, 52 })) 53 canceledCounter.Add(1) 54 return 55 } 56 if err := str.Close(); err != nil { 57 Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) 58 canceledCounter.Add(1) 59 return 60 } 61 }() 62 } 63 wg.Wait() 64 numCanceledStreamsChan <- canceledCounter.Load() 65 }() 66 return numCanceledStreamsChan 67 } 68 69 AfterEach(func() { 70 Expect(server.Close()).To(Succeed()) 71 }) 72 73 It("downloads when the client immediately cancels most streams", func() { 74 serverCanceledCounterChan := runServer(PRData) 75 conn, err := quic.DialAddr( 76 context.Background(), 77 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 78 getTLSClientConfig(), 79 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 80 ) 81 Expect(err).ToNot(HaveOccurred()) 82 83 var canceledCounter atomic.Int32 84 var wg sync.WaitGroup 85 wg.Add(numStreams) 86 for i := 0; i < numStreams; i++ { 87 go func() { 88 defer GinkgoRecover() 89 defer wg.Done() 90 str, err := conn.AcceptUniStream(context.Background()) 91 Expect(err).ToNot(HaveOccurred()) 92 // cancel around 2/3 of the streams 93 if rand.Int31()%3 != 0 { 94 canceledCounter.Add(1) 95 resetErr := quic.StreamErrorCode(str.StreamID()) 96 str.CancelRead(resetErr) 97 _, err := str.Read([]byte{0}) 98 Expect(err).To(Equal(&quic.StreamError{ 99 StreamID: str.StreamID(), 100 ErrorCode: resetErr, 101 Remote: false, 102 })) 103 return 104 } 105 data, err := io.ReadAll(str) 106 Expect(err).ToNot(HaveOccurred()) 107 Expect(data).To(Equal(PRData)) 108 }() 109 } 110 wg.Wait() 111 112 var serverCanceledCounter int32 113 Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter)) 114 Expect(conn.CloseWithError(0, "")).To(Succeed()) 115 116 clientCanceledCounter := canceledCounter.Load() 117 // The server will only count a stream as being reset if learns about the cancelation before it finished writing all data. 118 Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter)) 119 fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams) 120 Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10)) 121 Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10)) 122 }) 123 124 It("downloads when the client cancels streams after reading from them for a bit", func() { 125 serverCanceledCounterChan := runServer(PRData) 126 127 conn, err := quic.DialAddr( 128 context.Background(), 129 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 130 getTLSClientConfig(), 131 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 132 ) 133 Expect(err).ToNot(HaveOccurred()) 134 135 var canceledCounter atomic.Int32 136 var wg sync.WaitGroup 137 wg.Add(numStreams) 138 for i := 0; i < numStreams; i++ { 139 go func() { 140 defer GinkgoRecover() 141 defer wg.Done() 142 str, err := conn.AcceptUniStream(context.Background()) 143 Expect(err).ToNot(HaveOccurred()) 144 // only read some data from about 1/3 of the streams 145 if rand.Int31()%3 != 0 { 146 length := int(rand.Int31n(int32(len(PRData) - 1))) 147 data, err := io.ReadAll(io.LimitReader(str, int64(length))) 148 Expect(err).ToNot(HaveOccurred()) 149 str.CancelRead(quic.StreamErrorCode(str.StreamID())) 150 Expect(data).To(Equal(PRData[:length])) 151 canceledCounter.Add(1) 152 return 153 } 154 data, err := io.ReadAll(str) 155 Expect(err).ToNot(HaveOccurred()) 156 Expect(data).To(Equal(PRData)) 157 }() 158 } 159 wg.Wait() 160 161 var serverCanceledCounter int32 162 Eventually(serverCanceledCounterChan).Should(Receive(&serverCanceledCounter)) 163 Expect(conn.CloseWithError(0, "")).To(Succeed()) 164 165 clientCanceledCounter := canceledCounter.Load() 166 // The server will only count a stream as being reset if learns about the cancelation before it finished writing all data. 167 Expect(clientCanceledCounter).To(BeNumerically(">=", serverCanceledCounter)) 168 fmt.Fprintf(GinkgoWriter, "Canceled reading on %d of %d streams.\n", clientCanceledCounter, numStreams) 169 Expect(clientCanceledCounter).To(BeNumerically(">", numStreams/10)) 170 Expect(numStreams - clientCanceledCounter).To(BeNumerically(">", numStreams/10)) 171 }) 172 173 It("allows concurrent Read and CancelRead calls", func() { 174 // This test is especially valuable when run with race detector, 175 // see https://github.com/quic-go/quic-go/issues/3239. 176 serverCanceledCounterChan := runServer(make([]byte, 100)) // make sure the FIN is sent with the STREAM frame 177 178 conn, err := quic.DialAddr( 179 context.Background(), 180 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 181 getTLSClientConfig(), 182 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 183 ) 184 Expect(err).ToNot(HaveOccurred()) 185 186 var wg sync.WaitGroup 187 wg.Add(numStreams) 188 var counter atomic.Int32 189 for i := 0; i < numStreams; i++ { 190 go func() { 191 defer GinkgoRecover() 192 defer wg.Done() 193 str, err := conn.AcceptUniStream(context.Background()) 194 Expect(err).ToNot(HaveOccurred()) 195 196 done := make(chan struct{}) 197 go func() { 198 defer GinkgoRecover() 199 defer close(done) 200 b := make([]byte, 32) 201 if _, err := str.Read(b); err != nil { 202 counter.Add(1) 203 Expect(err).To(Equal(&quic.StreamError{ 204 StreamID: str.StreamID(), 205 ErrorCode: 1234, 206 Remote: false, 207 })) 208 return 209 } 210 }() 211 go str.CancelRead(1234) 212 Eventually(done).Should(BeClosed()) 213 }() 214 } 215 wg.Wait() 216 Expect(conn.CloseWithError(0, "")).To(Succeed()) 217 numCanceled := counter.Load() 218 fmt.Fprintf(GinkgoWriter, "canceled %d out of %d streams", numCanceled, numStreams) 219 Expect(numCanceled).ToNot(BeZero()) 220 Eventually(serverCanceledCounterChan).Should(Receive()) 221 }) 222 }) 223 224 Context("canceling the write side", func() { 225 runClient := func(server *quic.Listener) int32 /* number of canceled streams */ { 226 conn, err := quic.DialAddr( 227 context.Background(), 228 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 229 getTLSClientConfig(), 230 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 231 ) 232 Expect(err).ToNot(HaveOccurred()) 233 234 var wg sync.WaitGroup 235 var counter atomic.Int32 236 wg.Add(numStreams) 237 for i := 0; i < numStreams; i++ { 238 go func() { 239 defer GinkgoRecover() 240 defer wg.Done() 241 str, err := conn.AcceptUniStream(context.Background()) 242 Expect(err).ToNot(HaveOccurred()) 243 data, err := io.ReadAll(str) 244 if err != nil { 245 counter.Add(1) 246 Expect(err).To(MatchError(&quic.StreamError{ 247 StreamID: str.StreamID(), 248 ErrorCode: quic.StreamErrorCode(str.StreamID()), 249 })) 250 return 251 } 252 Expect(data).To(Equal(PRData)) 253 }() 254 } 255 wg.Wait() 256 257 streamCount := counter.Load() 258 fmt.Fprintf(GinkgoWriter, "Canceled writing on %d of %d streams\n", streamCount, numStreams) 259 Expect(streamCount).To(BeNumerically(">", numStreams/10)) 260 Expect(numStreams - streamCount).To(BeNumerically(">", numStreams/10)) 261 Expect(conn.CloseWithError(0, "")).To(Succeed()) 262 Expect(server.Close()).To(Succeed()) 263 return streamCount 264 } 265 266 It("downloads when the server cancels some streams immediately", func() { 267 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) 268 Expect(err).ToNot(HaveOccurred()) 269 270 var canceledCounter atomic.Int32 271 go func() { 272 defer GinkgoRecover() 273 conn, err := server.Accept(context.Background()) 274 Expect(err).ToNot(HaveOccurred()) 275 for i := 0; i < numStreams; i++ { 276 go func() { 277 defer GinkgoRecover() 278 str, err := conn.OpenUniStreamSync(context.Background()) 279 Expect(err).ToNot(HaveOccurred()) 280 // cancel about 2/3 of the streams 281 if rand.Int31()%3 != 0 { 282 str.CancelWrite(quic.StreamErrorCode(str.StreamID())) 283 canceledCounter.Add(1) 284 return 285 } 286 _, err = str.Write(PRData) 287 Expect(err).ToNot(HaveOccurred()) 288 Expect(str.Close()).To(Succeed()) 289 }() 290 } 291 }() 292 293 clientCanceledStreams := runClient(server) 294 Expect(clientCanceledStreams).To(Equal(canceledCounter.Load())) 295 }) 296 297 It("downloads when the server cancels some streams after sending some data", func() { 298 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) 299 Expect(err).ToNot(HaveOccurred()) 300 301 var canceledCounter atomic.Int32 302 go func() { 303 defer GinkgoRecover() 304 conn, err := server.Accept(context.Background()) 305 Expect(err).ToNot(HaveOccurred()) 306 for i := 0; i < numStreams; i++ { 307 go func() { 308 defer GinkgoRecover() 309 str, err := conn.OpenUniStreamSync(context.Background()) 310 Expect(err).ToNot(HaveOccurred()) 311 // only write some data from about 1/3 of the streams, then cancel 312 if rand.Int31()%3 != 0 { 313 length := int(rand.Int31n(int32(len(PRData) - 1))) 314 _, err = str.Write(PRData[:length]) 315 Expect(err).ToNot(HaveOccurred()) 316 str.CancelWrite(quic.StreamErrorCode(str.StreamID())) 317 canceledCounter.Add(1) 318 return 319 } 320 _, err = str.Write(PRData) 321 Expect(err).ToNot(HaveOccurred()) 322 Expect(str.Close()).To(Succeed()) 323 }() 324 } 325 }() 326 327 clientCanceledStreams := runClient(server) 328 Expect(clientCanceledStreams).To(Equal(canceledCounter.Load())) 329 }) 330 }) 331 332 Context("canceling both read and write side", func() { 333 It("downloads data when both sides cancel streams immediately", func() { 334 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) 335 Expect(err).ToNot(HaveOccurred()) 336 337 done := make(chan struct{}) 338 go func() { 339 defer GinkgoRecover() 340 var wg sync.WaitGroup 341 wg.Add(numStreams) 342 conn, err := server.Accept(context.Background()) 343 Expect(err).ToNot(HaveOccurred()) 344 for i := 0; i < numStreams; i++ { 345 go func() { 346 defer GinkgoRecover() 347 defer wg.Done() 348 str, err := conn.OpenUniStreamSync(context.Background()) 349 Expect(err).ToNot(HaveOccurred()) 350 // cancel about half of the streams 351 if rand.Int31()%2 == 0 { 352 str.CancelWrite(quic.StreamErrorCode(str.StreamID())) 353 return 354 } 355 if _, err = str.Write(PRData); err != nil { 356 Expect(err).To(MatchError(&quic.StreamError{ 357 StreamID: str.StreamID(), 358 ErrorCode: quic.StreamErrorCode(str.StreamID()), 359 })) 360 return 361 } 362 if err := str.Close(); err != nil { 363 Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) 364 return 365 } 366 }() 367 } 368 wg.Wait() 369 close(done) 370 }() 371 372 conn, err := quic.DialAddr( 373 context.Background(), 374 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 375 getTLSClientConfig(), 376 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 377 ) 378 Expect(err).ToNot(HaveOccurred()) 379 380 var wg sync.WaitGroup 381 var counter atomic.Int32 382 wg.Add(numStreams) 383 for i := 0; i < numStreams; i++ { 384 go func() { 385 defer GinkgoRecover() 386 defer wg.Done() 387 str, err := conn.AcceptUniStream(context.Background()) 388 Expect(err).ToNot(HaveOccurred()) 389 // cancel around half of the streams 390 if rand.Int31()%2 == 0 { 391 str.CancelRead(quic.StreamErrorCode(str.StreamID())) 392 return 393 } 394 data, err := io.ReadAll(str) 395 if err != nil { 396 Expect(err).To(MatchError(&quic.StreamError{ 397 StreamID: str.StreamID(), 398 ErrorCode: quic.StreamErrorCode(str.StreamID()), 399 })) 400 return 401 } 402 counter.Add(1) 403 Expect(data).To(Equal(PRData)) 404 }() 405 } 406 wg.Wait() 407 408 count := counter.Load() 409 Expect(count).To(BeNumerically(">", numStreams/15)) 410 fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) 411 412 Expect(conn.CloseWithError(0, "")).To(Succeed()) 413 Eventually(done).Should(BeClosed()) 414 Expect(server.Close()).To(Succeed()) 415 }) 416 417 It("downloads data when both sides cancel streams after a while", func() { 418 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), nil) 419 Expect(err).ToNot(HaveOccurred()) 420 421 done := make(chan struct{}) 422 go func() { 423 defer GinkgoRecover() 424 defer close(done) 425 conn, err := server.Accept(context.Background()) 426 Expect(err).ToNot(HaveOccurred()) 427 var wg sync.WaitGroup 428 wg.Add(numStreams) 429 for i := 0; i < numStreams; i++ { 430 go func() { 431 defer GinkgoRecover() 432 defer wg.Done() 433 str, err := conn.OpenUniStreamSync(context.Background()) 434 Expect(err).ToNot(HaveOccurred()) 435 // cancel about half of the streams 436 length := len(PRData) 437 if rand.Int31()%2 == 0 { 438 length = int(rand.Int31n(int32(len(PRData) - 1))) 439 } 440 if _, err = str.Write(PRData[:length]); err != nil { 441 Expect(err).To(MatchError(&quic.StreamError{ 442 StreamID: str.StreamID(), 443 ErrorCode: quic.StreamErrorCode(str.StreamID()), 444 })) 445 return 446 } 447 if length < len(PRData) { 448 str.CancelWrite(quic.StreamErrorCode(str.StreamID())) 449 } else if err := str.Close(); err != nil { 450 Expect(err).To(MatchError(fmt.Sprintf("close called for canceled stream %d", str.StreamID()))) 451 return 452 } 453 }() 454 } 455 wg.Wait() 456 }() 457 458 conn, err := quic.DialAddr( 459 context.Background(), 460 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 461 getTLSClientConfig(), 462 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 2}), 463 ) 464 Expect(err).ToNot(HaveOccurred()) 465 466 var wg sync.WaitGroup 467 var counter atomic.Int32 468 wg.Add(numStreams) 469 for i := 0; i < numStreams; i++ { 470 go func() { 471 defer GinkgoRecover() 472 defer wg.Done() 473 474 str, err := conn.AcceptUniStream(context.Background()) 475 Expect(err).ToNot(HaveOccurred()) 476 477 r := io.Reader(str) 478 length := len(PRData) 479 // cancel around half of the streams 480 if rand.Int31()%2 == 0 { 481 length = int(rand.Int31n(int32(len(PRData) - 1))) 482 r = io.LimitReader(str, int64(length)) 483 } 484 data, err := io.ReadAll(r) 485 if err != nil { 486 Expect(err).To(MatchError(&quic.StreamError{ 487 StreamID: str.StreamID(), 488 ErrorCode: quic.StreamErrorCode(str.StreamID()), 489 })) 490 return 491 } 492 Expect(data).To(Equal(PRData[:length])) 493 if length < len(PRData) { 494 str.CancelRead(quic.StreamErrorCode(str.StreamID())) 495 return 496 } 497 498 counter.Add(1) 499 Expect(data).To(Equal(PRData)) 500 }() 501 } 502 wg.Wait() 503 Eventually(done).Should(BeClosed()) 504 505 count := counter.Load() 506 Expect(count).To(BeNumerically(">", numStreams/15)) 507 fmt.Fprintf(GinkgoWriter, "Successfully read from %d of %d streams.\n", count, numStreams) 508 509 Expect(conn.CloseWithError(0, "")).To(Succeed()) 510 Expect(server.Close()).To(Succeed()) 511 }) 512 }) 513 514 Context("canceling the context", func() { 515 It("downloads data when the receiving peer cancels the context for accepting streams", func() { 516 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) 517 Expect(err).ToNot(HaveOccurred()) 518 519 go func() { 520 defer GinkgoRecover() 521 conn, err := server.Accept(context.Background()) 522 Expect(err).ToNot(HaveOccurred()) 523 ticker := time.NewTicker(5 * time.Millisecond) 524 for i := 0; i < numStreams; i++ { 525 <-ticker.C 526 go func() { 527 defer GinkgoRecover() 528 str, err := conn.OpenUniStreamSync(context.Background()) 529 Expect(err).ToNot(HaveOccurred()) 530 _, err = str.Write(PRData) 531 Expect(err).ToNot(HaveOccurred()) 532 Expect(str.Close()).To(Succeed()) 533 }() 534 } 535 }() 536 537 conn, err := quic.DialAddr( 538 context.Background(), 539 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 540 getTLSClientConfig(), 541 getQuicConfig(&quic.Config{MaxIncomingUniStreams: numStreams / 3}), 542 ) 543 Expect(err).ToNot(HaveOccurred()) 544 545 var numToAccept int 546 var counter atomic.Int32 547 var wg sync.WaitGroup 548 wg.Add(numStreams) 549 for numToAccept < numStreams { 550 ctx, cancel := context.WithCancel(context.Background()) 551 // cancel accepting half of the streams 552 if rand.Int31()%2 == 0 { 553 cancel() 554 } else { 555 numToAccept++ 556 defer cancel() 557 } 558 559 go func() { 560 defer GinkgoRecover() 561 str, err := conn.AcceptUniStream(ctx) 562 if err != nil { 563 if err.Error() == "context canceled" { 564 counter.Add(1) 565 } 566 return 567 } 568 data, err := io.ReadAll(str) 569 Expect(err).ToNot(HaveOccurred()) 570 Expect(data).To(Equal(PRData)) 571 wg.Done() 572 }() 573 } 574 wg.Wait() 575 576 count := counter.Load() 577 fmt.Fprintf(GinkgoWriter, "Canceled AcceptStream %d times\n", count) 578 Expect(count).To(BeNumerically(">", numStreams/2)) 579 Expect(conn.CloseWithError(0, "")).To(Succeed()) 580 Expect(server.Close()).To(Succeed()) 581 }) 582 583 It("downloads data when the sending peer cancels the context for opening streams", func() { 584 const ( 585 numStreams = 15 586 maxIncomingStreams = 5 587 ) 588 server, err := quic.ListenAddr("localhost:0", getTLSConfig(), getQuicConfig(nil)) 589 Expect(err).ToNot(HaveOccurred()) 590 591 msg := make(chan struct{}, 1) 592 var numCanceled atomic.Int32 593 go func() { 594 defer GinkgoRecover() 595 defer close(msg) 596 conn, err := server.Accept(context.Background()) 597 Expect(err).ToNot(HaveOccurred()) 598 599 var numOpened int 600 for numOpened < numStreams { 601 ctx, cancel := context.WithTimeout(context.Background(), scaleDuration(20*time.Millisecond)) 602 defer cancel() 603 str, err := conn.OpenUniStreamSync(ctx) 604 if err != nil { 605 Expect(err).To(MatchError(context.DeadlineExceeded)) 606 numCanceled.Add(1) 607 select { 608 case msg <- struct{}{}: 609 default: 610 } 611 continue 612 } 613 numOpened++ 614 go func(str quic.SendStream) { 615 defer GinkgoRecover() 616 _, err = str.Write(PRData) 617 Expect(err).ToNot(HaveOccurred()) 618 Expect(str.Close()).To(Succeed()) 619 }(str) 620 } 621 }() 622 623 conn, err := quic.DialAddr( 624 context.Background(), 625 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 626 getTLSClientConfig(), 627 getQuicConfig(&quic.Config{MaxIncomingUniStreams: maxIncomingStreams}), 628 ) 629 Expect(err).ToNot(HaveOccurred()) 630 631 var wg sync.WaitGroup 632 wg.Add(numStreams) 633 for i := 0; i < numStreams; i++ { 634 <-msg 635 str, err := conn.AcceptUniStream(context.Background()) 636 Expect(err).ToNot(HaveOccurred()) 637 go func(str quic.ReceiveStream) { 638 defer GinkgoRecover() 639 data, err := io.ReadAll(str) 640 Expect(err).ToNot(HaveOccurred()) 641 Expect(data).To(Equal(PRData)) 642 wg.Done() 643 }(str) 644 } 645 wg.Wait() 646 647 count := numCanceled.Load() 648 fmt.Fprintf(GinkgoWriter, "Canceled OpenStreamSync %d times\n", count) 649 Expect(count).To(BeNumerically(">=", numStreams-maxIncomingStreams)) 650 Expect(conn.CloseWithError(0, "")).To(Succeed()) 651 Expect(server.Close()).To(Succeed()) 652 }) 653 }) 654 655 It("doesn't run into any errors when streams are canceled all the time", func() { 656 const maxIncomingStreams = 1000 657 server, err := quic.ListenAddr( 658 "localhost:0", 659 getTLSConfig(), 660 getQuicConfig(&quic.Config{MaxIncomingStreams: maxIncomingStreams, MaxIdleTimeout: 10 * time.Second}), 661 ) 662 Expect(err).ToNot(HaveOccurred()) 663 defer server.Close() 664 665 var wg sync.WaitGroup 666 wg.Add(2 * 4 * maxIncomingStreams) 667 handleStream := func(str quic.Stream) { 668 str.SetDeadline(time.Now().Add(time.Second)) 669 go func() { 670 defer wg.Done() 671 if rand.Int31()%2 == 0 { 672 defer GinkgoRecover() 673 io.ReadAll(str) 674 } 675 }() 676 go func() { 677 defer wg.Done() 678 if rand.Int31()%2 == 0 { 679 str.Write([]byte("foobar")) 680 if rand.Int31()%2 == 0 { 681 str.Close() 682 } 683 } 684 }() 685 go func() { 686 defer wg.Done() 687 // Make sure we at least send out *something* for the last stream, 688 // otherwise the peer might never receive this anything for this stream. 689 if rand.Int31()%2 == 0 || str.StreamID() == 4*(maxIncomingStreams-1) { 690 str.CancelWrite(1234) 691 } 692 }() 693 go func() { 694 defer wg.Done() 695 if rand.Int31()%2 == 0 { 696 str.CancelRead(1234) 697 } 698 }() 699 } 700 701 serverRunning := make(chan struct{}) 702 go func() { 703 defer GinkgoRecover() 704 defer close(serverRunning) 705 conn, err := server.Accept(context.Background()) 706 Expect(err).ToNot(HaveOccurred()) 707 for { 708 str, err := conn.AcceptStream(context.Background()) 709 if err != nil { 710 // Make sure the connection is closed regularly. 711 Expect(err).To(BeAssignableToTypeOf(&quic.ApplicationError{})) 712 return 713 } 714 handleStream(str) 715 } 716 }() 717 718 conn, err := quic.DialAddr( 719 context.Background(), 720 fmt.Sprintf("localhost:%d", server.Addr().(*net.UDPAddr).Port), 721 getTLSClientConfig(), 722 getQuicConfig(&quic.Config{}), 723 ) 724 Expect(err).ToNot(HaveOccurred()) 725 726 for i := 0; i < maxIncomingStreams; i++ { 727 str, err := conn.OpenStreamSync(context.Background()) 728 Expect(err).ToNot(HaveOccurred()) 729 handleStream(str) 730 } 731 732 // We don't expect to accept any stream here. 733 // We're just making sure the connection stays open and there's no error. 734 ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond) 735 defer cancel() 736 _, err = conn.AcceptStream(ctx) 737 Expect(err).To(MatchError(context.DeadlineExceeded)) 738 739 wg.Wait() 740 741 Expect(conn.CloseWithError(0, "")).To(Succeed()) 742 Eventually(serverRunning).Should(BeClosed()) 743 }) 744 })