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 })