github.com/tumi8/quic-go@v0.37.4-tum/streams_map_outgoing_test.go (about)

     1  package quic
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sort"
     8  	"sync"
     9  	"time"
    10  
    11  	"golang.org/x/exp/rand"
    12  
    13  	"github.com/tumi8/quic-go/noninternal/protocol"
    14  	"github.com/tumi8/quic-go/noninternal/wire"
    15  
    16  	"github.com/golang/mock/gomock"
    17  
    18  	. "github.com/onsi/ginkgo/v2"
    19  	. "github.com/onsi/gomega"
    20  )
    21  
    22  var _ = Describe("Streams Map (outgoing)", func() {
    23  	var (
    24  		m          *outgoingStreamsMap[*mockGenericStream]
    25  		newStr     func(num protocol.StreamNum) *mockGenericStream
    26  		mockSender *MockStreamSender
    27  	)
    28  
    29  	const streamType = 42
    30  
    31  	// waitForEnqueued waits until there are n go routines waiting on OpenStreamSync()
    32  	waitForEnqueued := func(n int) {
    33  		Eventually(func() int {
    34  			m.mutex.Lock()
    35  			defer m.mutex.Unlock()
    36  			return len(m.openQueue)
    37  		}, 50*time.Millisecond, 100*time.Microsecond).Should(Equal(n))
    38  	}
    39  
    40  	BeforeEach(func() {
    41  		newStr = func(num protocol.StreamNum) *mockGenericStream {
    42  			return &mockGenericStream{num: num}
    43  		}
    44  		mockSender = NewMockStreamSender(mockCtrl)
    45  		m = newOutgoingStreamsMap[*mockGenericStream](streamType, newStr, mockSender.queueControlFrame)
    46  	})
    47  
    48  	Context("no stream ID limit", func() {
    49  		BeforeEach(func() {
    50  			m.SetMaxStream(0xffffffff)
    51  		})
    52  
    53  		It("opens streams", func() {
    54  			str, err := m.OpenStream()
    55  			Expect(err).ToNot(HaveOccurred())
    56  			Expect(str.num).To(Equal(protocol.StreamNum(1)))
    57  			str, err = m.OpenStream()
    58  			Expect(err).ToNot(HaveOccurred())
    59  			Expect(str.num).To(Equal(protocol.StreamNum(2)))
    60  		})
    61  
    62  		It("doesn't open streams after it has been closed", func() {
    63  			testErr := errors.New("close")
    64  			m.CloseWithError(testErr)
    65  			_, err := m.OpenStream()
    66  			Expect(err).To(MatchError(testErr))
    67  		})
    68  
    69  		It("gets streams", func() {
    70  			_, err := m.OpenStream()
    71  			Expect(err).ToNot(HaveOccurred())
    72  			str, err := m.GetStream(1)
    73  			Expect(err).ToNot(HaveOccurred())
    74  			Expect(str.num).To(Equal(protocol.StreamNum(1)))
    75  		})
    76  
    77  		It("errors when trying to get a stream that has not yet been opened", func() {
    78  			_, err := m.GetStream(1)
    79  			Expect(err).To(HaveOccurred())
    80  			Expect(err.(streamError).TestError()).To(MatchError("peer attempted to open stream 1"))
    81  		})
    82  
    83  		It("deletes streams", func() {
    84  			_, err := m.OpenStream()
    85  			Expect(err).ToNot(HaveOccurred())
    86  			Expect(m.DeleteStream(1)).To(Succeed())
    87  			Expect(err).ToNot(HaveOccurred())
    88  			str, err := m.GetStream(1)
    89  			Expect(err).ToNot(HaveOccurred())
    90  			Expect(str).To(BeNil())
    91  		})
    92  
    93  		It("errors when deleting a non-existing stream", func() {
    94  			err := m.DeleteStream(1337)
    95  			Expect(err).To(HaveOccurred())
    96  			Expect(err.(streamError).TestError()).To(MatchError("tried to delete unknown outgoing stream 1337"))
    97  		})
    98  
    99  		It("errors when deleting a stream twice", func() {
   100  			_, err := m.OpenStream() // opens firstNewStream
   101  			Expect(err).ToNot(HaveOccurred())
   102  			Expect(m.DeleteStream(1)).To(Succeed())
   103  			err = m.DeleteStream(1)
   104  			Expect(err).To(HaveOccurred())
   105  			Expect(err.(streamError).TestError()).To(MatchError("tried to delete unknown outgoing stream 1"))
   106  		})
   107  
   108  		It("closes all streams when CloseWithError is called", func() {
   109  			str1, err := m.OpenStream()
   110  			Expect(err).ToNot(HaveOccurred())
   111  			str2, err := m.OpenStream()
   112  			Expect(err).ToNot(HaveOccurred())
   113  			testErr := errors.New("test err")
   114  			m.CloseWithError(testErr)
   115  			Expect(str1.closed).To(BeTrue())
   116  			Expect(str1.closeErr).To(MatchError(testErr))
   117  			Expect(str2.closed).To(BeTrue())
   118  			Expect(str2.closeErr).To(MatchError(testErr))
   119  		})
   120  
   121  		It("updates the send window", func() {
   122  			str1, err := m.OpenStream()
   123  			Expect(err).ToNot(HaveOccurred())
   124  			str2, err := m.OpenStream()
   125  			Expect(err).ToNot(HaveOccurred())
   126  			m.UpdateSendWindow(1337)
   127  			Expect(str1.sendWindow).To(BeEquivalentTo(1337))
   128  			Expect(str2.sendWindow).To(BeEquivalentTo(1337))
   129  		})
   130  	})
   131  
   132  	Context("with stream ID limits", func() {
   133  		It("errors when no stream can be opened immediately", func() {
   134  			mockSender.EXPECT().queueControlFrame(gomock.Any())
   135  			_, err := m.OpenStream()
   136  			expectTooManyStreamsError(err)
   137  		})
   138  
   139  		It("returns immediately when called with a canceled context", func() {
   140  			ctx, cancel := context.WithCancel(context.Background())
   141  			cancel()
   142  			_, err := m.OpenStreamSync(ctx)
   143  			Expect(err).To(MatchError("context canceled"))
   144  		})
   145  
   146  		It("blocks until a stream can be opened synchronously", func() {
   147  			mockSender.EXPECT().queueControlFrame(gomock.Any())
   148  			done := make(chan struct{})
   149  			go func() {
   150  				defer GinkgoRecover()
   151  				str, err := m.OpenStreamSync(context.Background())
   152  				Expect(err).ToNot(HaveOccurred())
   153  				Expect(str.num).To(Equal(protocol.StreamNum(1)))
   154  				close(done)
   155  			}()
   156  			waitForEnqueued(1)
   157  
   158  			m.SetMaxStream(1)
   159  			Eventually(done).Should(BeClosed())
   160  		})
   161  
   162  		It("unblocks when the context is canceled", func() {
   163  			mockSender.EXPECT().queueControlFrame(gomock.Any())
   164  			ctx, cancel := context.WithCancel(context.Background())
   165  			done := make(chan struct{})
   166  			go func() {
   167  				defer GinkgoRecover()
   168  				_, err := m.OpenStreamSync(ctx)
   169  				Expect(err).To(MatchError("context canceled"))
   170  				close(done)
   171  			}()
   172  			waitForEnqueued(1)
   173  
   174  			cancel()
   175  			Eventually(done).Should(BeClosed())
   176  
   177  			// make sure that the next stream opened is stream 1
   178  			m.SetMaxStream(1000)
   179  			str, err := m.OpenStream()
   180  			Expect(err).ToNot(HaveOccurred())
   181  			Expect(str.num).To(Equal(protocol.StreamNum(1)))
   182  		})
   183  
   184  		It("opens streams in the right order", func() {
   185  			mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes()
   186  			done1 := make(chan struct{})
   187  			go func() {
   188  				defer GinkgoRecover()
   189  				str, err := m.OpenStreamSync(context.Background())
   190  				Expect(err).ToNot(HaveOccurred())
   191  				Expect(str.num).To(Equal(protocol.StreamNum(1)))
   192  				close(done1)
   193  			}()
   194  			waitForEnqueued(1)
   195  
   196  			done2 := make(chan struct{})
   197  			go func() {
   198  				defer GinkgoRecover()
   199  				str, err := m.OpenStreamSync(context.Background())
   200  				Expect(err).ToNot(HaveOccurred())
   201  				Expect(str.num).To(Equal(protocol.StreamNum(2)))
   202  				close(done2)
   203  			}()
   204  			waitForEnqueued(2)
   205  
   206  			m.SetMaxStream(1)
   207  			Eventually(done1).Should(BeClosed())
   208  			Consistently(done2).ShouldNot(BeClosed())
   209  			m.SetMaxStream(2)
   210  			Eventually(done2).Should(BeClosed())
   211  		})
   212  
   213  		It("opens streams in the right order, when one of the contexts is canceled", func() {
   214  			mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes()
   215  			done1 := make(chan struct{})
   216  			go func() {
   217  				defer GinkgoRecover()
   218  				str, err := m.OpenStreamSync(context.Background())
   219  				Expect(err).ToNot(HaveOccurred())
   220  				Expect(str.num).To(Equal(protocol.StreamNum(1)))
   221  				close(done1)
   222  			}()
   223  			waitForEnqueued(1)
   224  
   225  			done2 := make(chan struct{})
   226  			ctx, cancel := context.WithCancel(context.Background())
   227  			go func() {
   228  				defer GinkgoRecover()
   229  				_, err := m.OpenStreamSync(ctx)
   230  				Expect(err).To(MatchError(context.Canceled))
   231  				close(done2)
   232  			}()
   233  			waitForEnqueued(2)
   234  
   235  			done3 := make(chan struct{})
   236  			go func() {
   237  				defer GinkgoRecover()
   238  				str, err := m.OpenStreamSync(context.Background())
   239  				Expect(err).ToNot(HaveOccurred())
   240  				Expect(str.num).To(Equal(protocol.StreamNum(2)))
   241  				close(done3)
   242  			}()
   243  			waitForEnqueued(3)
   244  
   245  			cancel()
   246  			Eventually(done2).Should(BeClosed())
   247  			m.SetMaxStream(1000)
   248  			Eventually(done1).Should(BeClosed())
   249  			Eventually(done3).Should(BeClosed())
   250  		})
   251  
   252  		It("unblocks multiple OpenStreamSync calls at the same time", func() {
   253  			mockSender.EXPECT().queueControlFrame(gomock.Any()).AnyTimes()
   254  			done := make(chan struct{})
   255  			go func() {
   256  				defer GinkgoRecover()
   257  				_, err := m.OpenStreamSync(context.Background())
   258  				Expect(err).ToNot(HaveOccurred())
   259  				done <- struct{}{}
   260  			}()
   261  			go func() {
   262  				defer GinkgoRecover()
   263  				_, err := m.OpenStreamSync(context.Background())
   264  				Expect(err).ToNot(HaveOccurred())
   265  				done <- struct{}{}
   266  			}()
   267  			waitForEnqueued(2)
   268  			go func() {
   269  				defer GinkgoRecover()
   270  				_, err := m.OpenStreamSync(context.Background())
   271  				Expect(err).To(MatchError("test done"))
   272  				done <- struct{}{}
   273  			}()
   274  			waitForEnqueued(3)
   275  
   276  			m.SetMaxStream(2)
   277  			Eventually(done).Should(Receive())
   278  			Eventually(done).Should(Receive())
   279  			Consistently(done).ShouldNot(Receive())
   280  
   281  			m.CloseWithError(errors.New("test done"))
   282  			Eventually(done).Should(Receive())
   283  		})
   284  
   285  		It("returns an error for OpenStream while an OpenStreamSync call is blocking", func() {
   286  			mockSender.EXPECT().queueControlFrame(gomock.Any()).MaxTimes(2)
   287  			openedSync := make(chan struct{})
   288  			go func() {
   289  				defer GinkgoRecover()
   290  				str, err := m.OpenStreamSync(context.Background())
   291  				Expect(err).ToNot(HaveOccurred())
   292  				Expect(str.num).To(Equal(protocol.StreamNum(1)))
   293  				close(openedSync)
   294  			}()
   295  			waitForEnqueued(1)
   296  
   297  			start := make(chan struct{})
   298  			openend := make(chan struct{})
   299  			go func() {
   300  				defer GinkgoRecover()
   301  				var hasStarted bool
   302  				for {
   303  					str, err := m.OpenStream()
   304  					if err == nil {
   305  						Expect(str.num).To(Equal(protocol.StreamNum(2)))
   306  						close(openend)
   307  						return
   308  					}
   309  					expectTooManyStreamsError(err)
   310  					if !hasStarted {
   311  						close(start)
   312  						hasStarted = true
   313  					}
   314  				}
   315  			}()
   316  
   317  			Eventually(start).Should(BeClosed())
   318  			m.SetMaxStream(1)
   319  			Eventually(openedSync).Should(BeClosed())
   320  			Consistently(openend).ShouldNot(BeClosed())
   321  			m.SetMaxStream(2)
   322  			Eventually(openend).Should(BeClosed())
   323  		})
   324  
   325  		It("stops opening synchronously when it is closed", func() {
   326  			mockSender.EXPECT().queueControlFrame(gomock.Any())
   327  			testErr := errors.New("test error")
   328  			done := make(chan struct{})
   329  			go func() {
   330  				defer GinkgoRecover()
   331  				_, err := m.OpenStreamSync(context.Background())
   332  				Expect(err).To(MatchError(testErr))
   333  				close(done)
   334  			}()
   335  
   336  			Consistently(done).ShouldNot(BeClosed())
   337  			m.CloseWithError(testErr)
   338  			Eventually(done).Should(BeClosed())
   339  		})
   340  
   341  		It("doesn't reduce the stream limit", func() {
   342  			m.SetMaxStream(2)
   343  			m.SetMaxStream(1)
   344  			_, err := m.OpenStream()
   345  			Expect(err).ToNot(HaveOccurred())
   346  			str, err := m.OpenStream()
   347  			Expect(err).ToNot(HaveOccurred())
   348  			Expect(str.num).To(Equal(protocol.StreamNum(2)))
   349  		})
   350  
   351  		It("queues a STREAMS_BLOCKED frame if no stream can be opened", func() {
   352  			m.SetMaxStream(6)
   353  			// open the 6 allowed streams
   354  			for i := 0; i < 6; i++ {
   355  				_, err := m.OpenStream()
   356  				Expect(err).ToNot(HaveOccurred())
   357  			}
   358  
   359  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   360  				bf := f.(*wire.StreamsBlockedFrame)
   361  				Expect(bf.Type).To(BeEquivalentTo(streamType))
   362  				Expect(bf.StreamLimit).To(BeEquivalentTo(6))
   363  			})
   364  			_, err := m.OpenStream()
   365  			Expect(err).To(HaveOccurred())
   366  			Expect(err.Error()).To(Equal(errTooManyOpenStreams.Error()))
   367  		})
   368  
   369  		It("only sends one STREAMS_BLOCKED frame for one stream ID", func() {
   370  			m.SetMaxStream(1)
   371  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   372  				Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(1))
   373  			})
   374  			_, err := m.OpenStream()
   375  			Expect(err).ToNot(HaveOccurred())
   376  			// try to open a stream twice, but expect only one STREAMS_BLOCKED to be sent
   377  			_, err = m.OpenStream()
   378  			expectTooManyStreamsError(err)
   379  			_, err = m.OpenStream()
   380  			expectTooManyStreamsError(err)
   381  		})
   382  
   383  		It("queues a STREAMS_BLOCKED frame when there more streams waiting for OpenStreamSync than MAX_STREAMS allows", func() {
   384  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   385  				Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(0))
   386  			})
   387  			done := make(chan struct{}, 2)
   388  			go func() {
   389  				defer GinkgoRecover()
   390  				_, err := m.OpenStreamSync(context.Background())
   391  				Expect(err).ToNot(HaveOccurred())
   392  				done <- struct{}{}
   393  			}()
   394  			go func() {
   395  				defer GinkgoRecover()
   396  				_, err := m.OpenStreamSync(context.Background())
   397  				Expect(err).ToNot(HaveOccurred())
   398  				done <- struct{}{}
   399  			}()
   400  			waitForEnqueued(2)
   401  
   402  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   403  				Expect(f.(*wire.StreamsBlockedFrame).StreamLimit).To(BeEquivalentTo(1))
   404  			})
   405  			m.SetMaxStream(1)
   406  			Eventually(done).Should(Receive())
   407  			Consistently(done).ShouldNot(Receive())
   408  			m.SetMaxStream(2)
   409  			Eventually(done).Should(Receive())
   410  		})
   411  	})
   412  
   413  	Context("randomized tests", func() {
   414  		It("opens streams", func() {
   415  			rand.Seed(uint64(GinkgoRandomSeed()))
   416  			const n = 100
   417  			fmt.Fprintf(GinkgoWriter, "Opening %d streams concurrently.\n", n)
   418  
   419  			var blockedAt []protocol.StreamNum
   420  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   421  				blockedAt = append(blockedAt, f.(*wire.StreamsBlockedFrame).StreamLimit)
   422  			}).AnyTimes()
   423  			done := make(map[int]chan struct{})
   424  			for i := 1; i <= n; i++ {
   425  				c := make(chan struct{})
   426  				done[i] = c
   427  
   428  				go func(doneChan chan struct{}, id protocol.StreamNum) {
   429  					defer GinkgoRecover()
   430  					defer close(doneChan)
   431  					str, err := m.OpenStreamSync(context.Background())
   432  					Expect(err).ToNot(HaveOccurred())
   433  					Expect(str.num).To(Equal(id))
   434  				}(c, protocol.StreamNum(i))
   435  				waitForEnqueued(i)
   436  			}
   437  
   438  			var limit int
   439  			limits := []protocol.StreamNum{0}
   440  			for limit < n {
   441  				limit += rand.Intn(n/5) + 1
   442  				if limit <= n {
   443  					limits = append(limits, protocol.StreamNum(limit))
   444  				}
   445  				fmt.Fprintf(GinkgoWriter, "Setting stream limit to %d.\n", limit)
   446  				m.SetMaxStream(protocol.StreamNum(limit))
   447  				for i := 1; i <= n; i++ {
   448  					if i <= limit {
   449  						Eventually(done[i]).Should(BeClosed())
   450  					} else {
   451  						Expect(done[i]).ToNot(BeClosed())
   452  					}
   453  				}
   454  				str, err := m.OpenStream()
   455  				if limit <= n {
   456  					Expect(err).To(HaveOccurred())
   457  					Expect(err.Error()).To(Equal(errTooManyOpenStreams.Error()))
   458  				} else {
   459  					Expect(str.num).To(Equal(protocol.StreamNum(n + 1)))
   460  				}
   461  			}
   462  			Expect(blockedAt).To(Equal(limits))
   463  		})
   464  
   465  		It("opens streams, when some of them are getting canceled", func() {
   466  			rand.Seed(uint64(GinkgoRandomSeed()))
   467  			const n = 100
   468  			fmt.Fprintf(GinkgoWriter, "Opening %d streams concurrently.\n", n)
   469  
   470  			var blockedAt []protocol.StreamNum
   471  			mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) {
   472  				blockedAt = append(blockedAt, f.(*wire.StreamsBlockedFrame).StreamLimit)
   473  			}).AnyTimes()
   474  
   475  			ctx, cancel := context.WithCancel(context.Background())
   476  			streamsToCancel := make(map[protocol.StreamNum]struct{}) // used as a set
   477  			for i := 0; i < 10; i++ {
   478  				id := protocol.StreamNum(rand.Intn(n) + 1)
   479  				fmt.Fprintf(GinkgoWriter, "Canceling stream %d.\n", id)
   480  				streamsToCancel[id] = struct{}{}
   481  			}
   482  
   483  			streamWillBeCanceled := func(id protocol.StreamNum) bool {
   484  				_, ok := streamsToCancel[id]
   485  				return ok
   486  			}
   487  
   488  			var streamIDs []int
   489  			var mutex sync.Mutex
   490  			done := make(map[int]chan struct{})
   491  			for i := 1; i <= n; i++ {
   492  				c := make(chan struct{})
   493  				done[i] = c
   494  
   495  				go func(doneChan chan struct{}, id protocol.StreamNum) {
   496  					defer GinkgoRecover()
   497  					defer close(doneChan)
   498  					cont := context.Background()
   499  					if streamWillBeCanceled(id) {
   500  						cont = ctx
   501  					}
   502  					str, err := m.OpenStreamSync(cont)
   503  					if streamWillBeCanceled(id) {
   504  						Expect(err).To(MatchError(context.Canceled))
   505  						return
   506  					}
   507  					Expect(err).ToNot(HaveOccurred())
   508  					mutex.Lock()
   509  					streamIDs = append(streamIDs, int(str.num))
   510  					mutex.Unlock()
   511  				}(c, protocol.StreamNum(i))
   512  				waitForEnqueued(i)
   513  			}
   514  
   515  			cancel()
   516  			for id := range streamsToCancel {
   517  				Eventually(done[int(id)]).Should(BeClosed())
   518  			}
   519  			var limit int
   520  			numStreams := n - len(streamsToCancel)
   521  			var limits []protocol.StreamNum
   522  			for limit < numStreams {
   523  				limits = append(limits, protocol.StreamNum(limit))
   524  				limit += rand.Intn(n/5) + 1
   525  				fmt.Fprintf(GinkgoWriter, "Setting stream limit to %d.\n", limit)
   526  				m.SetMaxStream(protocol.StreamNum(limit))
   527  				l := limit
   528  				if l > numStreams {
   529  					l = numStreams
   530  				}
   531  				Eventually(func() int {
   532  					mutex.Lock()
   533  					defer mutex.Unlock()
   534  					return len(streamIDs)
   535  				}).Should(Equal(l))
   536  				// check that all stream IDs were used
   537  				Expect(streamIDs).To(HaveLen(l))
   538  				sort.Ints(streamIDs)
   539  				for i := 0; i < l; i++ {
   540  					Expect(streamIDs[i]).To(Equal(i + 1))
   541  				}
   542  			}
   543  			Expect(blockedAt).To(Equal(limits))
   544  		})
   545  	})
   546  })