github.com/MerlinKodo/quic-go@v0.39.2/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/MerlinKodo/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 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  							atomic.AddInt32(&canceledCounter, 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  							atomic.AddInt32(&canceledCounter, 1)
    59  							return
    60  						}
    61  					}()
    62  				}
    63  				wg.Wait()
    64  				numCanceledStreamsChan <- atomic.LoadInt32(&canceledCounter)
    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 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  						atomic.AddInt32(&canceledCounter, 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 := atomic.LoadInt32(&canceledCounter)
   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 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  						atomic.AddInt32(&canceledCounter, 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 := atomic.LoadInt32(&canceledCounter)
   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/MerlinKodo/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 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  							atomic.AddInt32(&counter, 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 := atomic.LoadInt32(&counter)
   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 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  						atomic.AddInt32(&counter, 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 := atomic.LoadInt32(&counter)
   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 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  							atomic.AddInt32(&canceledCounter, 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(atomic.LoadInt32(&canceledCounter)))
   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 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  							atomic.AddInt32(&canceledCounter, 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(atomic.LoadInt32(&canceledCounter)))
   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 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  					atomic.AddInt32(&counter, 1)
   403  					Expect(data).To(Equal(PRData))
   404  				}()
   405  			}
   406  			wg.Wait()
   407  
   408  			count := atomic.LoadInt32(&counter)
   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 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  					atomic.AddInt32(&counter, 1)
   499  					Expect(data).To(Equal(PRData))
   500  				}()
   501  			}
   502  			wg.Wait()
   503  			Eventually(done).Should(BeClosed())
   504  
   505  			count := atomic.LoadInt32(&counter)
   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 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  							atomic.AddInt32(&counter, 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 := atomic.LoadInt32(&counter)
   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 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  						atomic.AddInt32(&numCanceled, 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 := atomic.LoadInt32(&numCanceled)
   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  })