github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/send_stream_test.go (about) 1 package quic 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "io" 8 mrand "math/rand" 9 "runtime" 10 "time" 11 12 "golang.org/x/exp/rand" 13 14 "github.com/apernet/quic-go/internal/ackhandler" 15 "github.com/apernet/quic-go/internal/mocks" 16 "github.com/apernet/quic-go/internal/protocol" 17 "github.com/apernet/quic-go/internal/wire" 18 19 . "github.com/onsi/ginkgo/v2" 20 . "github.com/onsi/gomega" 21 "github.com/onsi/gomega/gbytes" 22 "go.uber.org/mock/gomock" 23 ) 24 25 var _ = Describe("Send Stream", func() { 26 const streamID protocol.StreamID = 1337 27 28 var ( 29 str *sendStream 30 strWithTimeout io.Writer // str wrapped with gbytes.TimeoutWriter 31 mockFC *mocks.MockStreamFlowController 32 mockSender *MockStreamSender 33 ) 34 35 BeforeEach(func() { 36 mockSender = NewMockStreamSender(mockCtrl) 37 mockFC = mocks.NewMockStreamFlowController(mockCtrl) 38 str = newSendStream(context.Background(), streamID, mockSender, mockFC) 39 40 timeout := scaleDuration(250 * time.Millisecond) 41 strWithTimeout = gbytes.TimeoutWriter(str, timeout) 42 }) 43 44 expectedFrameHeaderLen := func(offset protocol.ByteCount) protocol.ByteCount { 45 return (&wire.StreamFrame{ 46 StreamID: streamID, 47 Offset: offset, 48 DataLenPresent: true, 49 }).Length(protocol.Version1) 50 } 51 52 waitForWrite := func() { 53 EventuallyWithOffset(0, func() bool { 54 str.mutex.Lock() 55 hasData := str.dataForWriting != nil || str.nextFrame != nil 56 str.mutex.Unlock() 57 return hasData 58 }).Should(BeTrue()) 59 } 60 61 getDataAtOffset := func(offset, length protocol.ByteCount) []byte { 62 b := make([]byte, length) 63 for i := protocol.ByteCount(0); i < length; i++ { 64 b[i] = uint8(offset + i) 65 } 66 return b 67 } 68 69 getData := func(length protocol.ByteCount) []byte { 70 return getDataAtOffset(0, length) 71 } 72 73 It("gets stream id", func() { 74 Expect(str.StreamID()).To(Equal(protocol.StreamID(1337))) 75 }) 76 77 Context("writing", func() { 78 It("writes and gets all data at once", func() { 79 done := make(chan struct{}) 80 go func() { 81 defer GinkgoRecover() 82 defer close(done) 83 mockSender.EXPECT().onHasStreamData(streamID) 84 n, err := strWithTimeout.Write([]byte("foobar")) 85 Expect(err).ToNot(HaveOccurred()) 86 Expect(n).To(Equal(6)) 87 }() 88 waitForWrite() 89 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 90 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) 91 frame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 92 Expect(ok).To(BeTrue()) 93 f := frame.Frame 94 Expect(f.Data).To(Equal([]byte("foobar"))) 95 Expect(f.Fin).To(BeFalse()) 96 Expect(f.Offset).To(BeZero()) 97 Expect(f.DataLenPresent).To(BeTrue()) 98 Expect(str.writeOffset).To(Equal(protocol.ByteCount(6))) 99 Expect(str.dataForWriting).To(BeNil()) 100 Eventually(done).Should(BeClosed()) 101 }) 102 103 It("writes and gets data in two turns", func() { 104 done := make(chan struct{}) 105 go func() { 106 defer GinkgoRecover() 107 mockSender.EXPECT().onHasStreamData(streamID) 108 n, err := strWithTimeout.Write([]byte("foobar")) 109 Expect(err).ToNot(HaveOccurred()) 110 Expect(n).To(Equal(6)) 111 close(done) 112 }() 113 waitForWrite() 114 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 115 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)).Times(2) 116 frame, ok, _ := str.popStreamFrame(expectedFrameHeaderLen(0)+3, protocol.Version1) 117 Expect(ok).To(BeTrue()) 118 f := frame.Frame 119 Expect(f.Offset).To(BeZero()) 120 Expect(f.Fin).To(BeFalse()) 121 Expect(f.Data).To(Equal([]byte("foo"))) 122 Expect(f.DataLenPresent).To(BeTrue()) 123 frame, ok, _ = str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 124 Expect(ok).To(BeTrue()) 125 f = frame.Frame 126 Expect(f.Data).To(Equal([]byte("bar"))) 127 Expect(f.Fin).To(BeFalse()) 128 Expect(f.Offset).To(Equal(protocol.ByteCount(3))) 129 Expect(f.DataLenPresent).To(BeTrue()) 130 _, ok, _ = str.popStreamFrame(1000, protocol.Version1) 131 Expect(ok).To(BeFalse()) 132 Eventually(done).Should(BeClosed()) 133 }) 134 135 It("bundles small writes", func() { 136 done := make(chan struct{}) 137 go func() { 138 defer GinkgoRecover() 139 mockSender.EXPECT().onHasStreamData(streamID).Times(2) 140 n, err := strWithTimeout.Write([]byte("foo")) 141 Expect(err).ToNot(HaveOccurred()) 142 Expect(n).To(Equal(3)) 143 n, err = strWithTimeout.Write([]byte("bar")) 144 Expect(err).ToNot(HaveOccurred()) 145 Expect(n).To(Equal(3)) 146 close(done) 147 }() 148 Eventually(done).Should(BeClosed()) // both Write calls returned without any data having been dequeued yet 149 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 150 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) 151 frame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 152 Expect(ok).To(BeTrue()) 153 f := frame.Frame 154 Expect(f.Offset).To(BeZero()) 155 Expect(f.Fin).To(BeFalse()) 156 Expect(f.Data).To(Equal([]byte("foobar"))) 157 }) 158 159 It("writes and gets data in multiple turns, for large writes", func() { 160 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(5) 161 var totalBytesSent protocol.ByteCount 162 mockFC.EXPECT().AddBytesSent(gomock.Any()).Do(func(l protocol.ByteCount) { totalBytesSent += l }).Times(5) 163 done := make(chan struct{}) 164 go func() { 165 defer GinkgoRecover() 166 mockSender.EXPECT().onHasStreamData(streamID) 167 n, err := strWithTimeout.Write(getData(5000)) 168 Expect(err).ToNot(HaveOccurred()) 169 Expect(n).To(Equal(5000)) 170 close(done) 171 }() 172 waitForWrite() 173 for i := 0; i < 5; i++ { 174 frame, ok, _ := str.popStreamFrame(1100, protocol.Version1) 175 Expect(ok).To(BeTrue()) 176 f := frame.Frame 177 Expect(f.Offset).To(BeNumerically("~", 1100*i, 10*i)) 178 Expect(f.Fin).To(BeFalse()) 179 Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen()))) 180 Expect(f.DataLenPresent).To(BeTrue()) 181 } 182 Expect(totalBytesSent).To(Equal(protocol.ByteCount(5000))) 183 Eventually(done).Should(BeClosed()) 184 }) 185 186 It("unblocks Write as soon as a STREAM frame can be buffered", func() { 187 done := make(chan struct{}) 188 go func() { 189 defer GinkgoRecover() 190 defer close(done) 191 mockSender.EXPECT().onHasStreamData(streamID) 192 _, err := strWithTimeout.Write(getData(protocol.MaxPacketBufferSize + 3)) 193 Expect(err).ToNot(HaveOccurred()) 194 }() 195 waitForWrite() 196 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 197 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) 198 frame, ok, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0)+2, protocol.Version1) 199 Expect(ok).To(BeTrue()) 200 Expect(hasMoreData).To(BeTrue()) 201 f := frame.Frame 202 Expect(f.DataLen()).To(Equal(protocol.ByteCount(2))) 203 Consistently(done).ShouldNot(BeClosed()) 204 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1)) 205 frame, ok, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(1)+1, protocol.Version1) 206 Expect(ok).To(BeTrue()) 207 Expect(hasMoreData).To(BeTrue()) 208 f = frame.Frame 209 Expect(f.DataLen()).To(Equal(protocol.ByteCount(1))) 210 Eventually(done).Should(BeClosed()) 211 }) 212 213 It("only unblocks Write once a previously buffered STREAM frame has been fully dequeued", func() { 214 mockSender.EXPECT().onHasStreamData(streamID) 215 _, err := strWithTimeout.Write([]byte("foobar")) 216 Expect(err).ToNot(HaveOccurred()) 217 done := make(chan struct{}) 218 go func() { 219 defer GinkgoRecover() 220 defer close(done) 221 mockSender.EXPECT().onHasStreamData(streamID) 222 _, err := str.Write(getData(protocol.MaxPacketBufferSize)) 223 Expect(err).ToNot(HaveOccurred()) 224 }() 225 waitForWrite() 226 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 227 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) 228 frame, ok, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0)+2, protocol.Version1) 229 Expect(ok).To(BeTrue()) 230 Expect(hasMoreData).To(BeTrue()) 231 f := frame.Frame 232 Expect(f.Data).To(Equal([]byte("fo"))) 233 Consistently(done).ShouldNot(BeClosed()) 234 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(4)) 235 frame, ok, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(2)+4, protocol.Version1) 236 Expect(ok).To(BeTrue()) 237 Expect(hasMoreData).To(BeTrue()) 238 f = frame.Frame 239 Expect(f.Data).To(Equal([]byte("obar"))) 240 Eventually(done).Should(BeClosed()) 241 }) 242 243 It("popStreamFrame returns nil if no data is available", func() { 244 _, ok, hasMoreData := str.popStreamFrame(1000, protocol.Version1) 245 Expect(ok).To(BeFalse()) 246 Expect(hasMoreData).To(BeFalse()) 247 }) 248 249 It("says if it has more data for writing", func() { 250 done := make(chan struct{}) 251 go func() { 252 defer GinkgoRecover() 253 defer close(done) 254 mockSender.EXPECT().onHasStreamData(streamID) 255 n, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100)) 256 Expect(err).ToNot(HaveOccurred()) 257 Expect(n).To(Equal(100)) 258 }() 259 waitForWrite() 260 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 261 mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2) 262 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 263 Expect(ok).To(BeTrue()) 264 Expect(frame.Frame.Fin).To(BeFalse()) 265 Expect(hasMoreData).To(BeTrue()) 266 frame, ok, hasMoreData = str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 267 Expect(ok).To(BeTrue()) 268 Expect(frame.Frame.Fin).To(BeFalse()) 269 Expect(hasMoreData).To(BeFalse()) 270 _, ok, _ = str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 271 Expect(ok).To(BeFalse()) 272 Eventually(done).Should(BeClosed()) 273 }) 274 275 It("copies the slice while writing", func() { 276 frameHeaderSize := protocol.ByteCount(4) 277 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 278 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1)) 279 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2)) 280 s := []byte("foo") 281 done := make(chan struct{}) 282 go func() { 283 defer GinkgoRecover() 284 defer close(done) 285 mockSender.EXPECT().onHasStreamData(streamID) 286 n, err := strWithTimeout.Write(s) 287 Expect(err).ToNot(HaveOccurred()) 288 Expect(n).To(Equal(3)) 289 }() 290 waitForWrite() 291 frame, ok, _ := str.popStreamFrame(frameHeaderSize+1, protocol.Version1) 292 Expect(ok).To(BeTrue()) 293 f := frame.Frame 294 Expect(f.Data).To(Equal([]byte("f"))) 295 frame, ok, _ = str.popStreamFrame(100, protocol.Version1) 296 Expect(ok).To(BeTrue()) 297 Expect(frame).ToNot(BeNil()) 298 f = frame.Frame 299 Expect(f.Data).To(Equal([]byte("oo"))) 300 s[1] = 'e' 301 Expect(f.Data).To(Equal([]byte("oo"))) 302 Eventually(done).Should(BeClosed()) 303 }) 304 305 It("returns when given a nil input", func() { 306 n, err := strWithTimeout.Write(nil) 307 Expect(n).To(BeZero()) 308 Expect(err).ToNot(HaveOccurred()) 309 }) 310 311 It("returns when given an empty slice", func() { 312 n, err := strWithTimeout.Write([]byte("")) 313 Expect(n).To(BeZero()) 314 Expect(err).ToNot(HaveOccurred()) 315 }) 316 317 It("cancels the context when Close is called", func() { 318 mockSender.EXPECT().onHasStreamData(streamID) 319 Expect(str.Context().Done()).ToNot(BeClosed()) 320 Expect(str.Close()).To(Succeed()) 321 Expect(str.Context().Done()).To(BeClosed()) 322 Expect(context.Cause(str.Context())).To(MatchError(context.Canceled)) 323 }) 324 325 Context("flow control blocking", func() { 326 It("queues a BLOCKED frame if the stream is flow control blocked", func() { 327 mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(0)) 328 mockFC.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(12)) 329 mockSender.EXPECT().queueControlFrame(&wire.StreamDataBlockedFrame{ 330 StreamID: streamID, 331 MaximumStreamData: 12, 332 }) 333 done := make(chan struct{}) 334 go func() { 335 defer GinkgoRecover() 336 defer close(done) 337 mockSender.EXPECT().onHasStreamData(streamID) 338 _, err := str.Write([]byte("foobar")) 339 Expect(err).ToNot(HaveOccurred()) 340 }() 341 waitForWrite() 342 _, ok, hasMoreData := str.popStreamFrame(1000, protocol.Version1) 343 Expect(ok).To(BeFalse()) 344 Expect(hasMoreData).To(BeFalse()) 345 // make the Write go routine return 346 str.closeForShutdown(nil) 347 Eventually(done).Should(BeClosed()) 348 }) 349 350 It("says that it doesn't have any more data, when it is flow control blocked", func() { 351 done := make(chan struct{}) 352 go func() { 353 defer GinkgoRecover() 354 defer close(done) 355 mockSender.EXPECT().onHasStreamData(streamID) 356 _, err := str.Write([]byte("foobar")) 357 Expect(err).ToNot(HaveOccurred()) 358 }() 359 waitForWrite() 360 361 // first pop a STREAM frame of the maximum size allowed by flow control 362 mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(3)) 363 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)) 364 f, ok, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0)+3, protocol.Version1) 365 Expect(ok).To(BeTrue()) 366 Expect(f).ToNot(BeNil()) 367 Expect(hasMoreData).To(BeTrue()) 368 369 // try to pop again, this time noticing that we're blocked 370 mockFC.EXPECT().SendWindowSize() 371 // don't use offset 3 here, to make sure the BLOCKED frame contains the number returned by the flow controller 372 mockFC.EXPECT().IsNewlyBlocked().Return(true, protocol.ByteCount(10)) 373 mockSender.EXPECT().queueControlFrame(&wire.StreamDataBlockedFrame{ 374 StreamID: streamID, 375 MaximumStreamData: 10, 376 }) 377 _, ok, hasMoreData = str.popStreamFrame(1000, protocol.Version1) 378 Expect(ok).To(BeFalse()) 379 Expect(hasMoreData).To(BeFalse()) 380 // make the Write go routine return 381 str.closeForShutdown(nil) 382 Eventually(done).Should(BeClosed()) 383 }) 384 }) 385 386 Context("deadlines", func() { 387 It("returns an error when Write is called after the deadline", func() { 388 str.SetWriteDeadline(time.Now().Add(-time.Second)) 389 n, err := strWithTimeout.Write([]byte("foobar")) 390 Expect(err).To(MatchError(errDeadline)) 391 Expect(n).To(BeZero()) 392 }) 393 394 It("unblocks after the deadline", func() { 395 mockSender.EXPECT().onHasStreamData(streamID) 396 deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) 397 str.SetWriteDeadline(deadline) 398 n, err := strWithTimeout.Write(getData(5000)) 399 Expect(err).To(MatchError(errDeadline)) 400 Expect(n).To(BeZero()) 401 Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond))) 402 }) 403 404 It("unblocks when the deadline is changed to the past", func() { 405 mockSender.EXPECT().onHasStreamData(streamID) 406 str.SetWriteDeadline(time.Now().Add(time.Hour)) 407 done := make(chan struct{}) 408 go func() { 409 defer GinkgoRecover() 410 _, err := str.Write(getData(5000)) 411 Expect(err).To(MatchError(errDeadline)) 412 close(done) 413 }() 414 Consistently(done).ShouldNot(BeClosed()) 415 str.SetWriteDeadline(time.Now().Add(-time.Hour)) 416 Eventually(done).Should(BeClosed()) 417 }) 418 419 It("returns the number of bytes written, when the deadline expires", func() { 420 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() 421 mockFC.EXPECT().AddBytesSent(gomock.Any()) 422 deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) 423 str.SetWriteDeadline(deadline) 424 var n int 425 writeReturned := make(chan struct{}) 426 go func() { 427 defer GinkgoRecover() 428 defer close(writeReturned) 429 mockSender.EXPECT().onHasStreamData(streamID) 430 var err error 431 n, err = strWithTimeout.Write(getData(5000)) 432 Expect(err).To(MatchError(errDeadline)) 433 Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond))) 434 }() 435 waitForWrite() 436 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 437 Expect(ok).To(BeTrue()) 438 Expect(frame).ToNot(BeNil()) 439 Expect(hasMoreData).To(BeTrue()) 440 Eventually(writeReturned, scaleDuration(80*time.Millisecond)).Should(BeClosed()) 441 Expect(n).To(BeEquivalentTo(frame.Frame.DataLen())) 442 }) 443 444 It("doesn't pop any data after the deadline expired", func() { 445 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() 446 mockFC.EXPECT().AddBytesSent(gomock.Any()) 447 deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) 448 str.SetWriteDeadline(deadline) 449 writeReturned := make(chan struct{}) 450 go func() { 451 defer GinkgoRecover() 452 defer close(writeReturned) 453 mockSender.EXPECT().onHasStreamData(streamID) 454 _, err := strWithTimeout.Write(getData(5000)) 455 Expect(err).To(MatchError(errDeadline)) 456 }() 457 waitForWrite() 458 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 459 Expect(ok).To(BeTrue()) 460 Expect(frame).ToNot(BeNil()) 461 Expect(hasMoreData).To(BeTrue()) 462 Eventually(writeReturned, scaleDuration(80*time.Millisecond)).Should(BeClosed()) 463 _, ok, hasMoreData = str.popStreamFrame(50, protocol.Version1) 464 Expect(ok).To(BeFalse()) 465 Expect(hasMoreData).To(BeFalse()) 466 }) 467 468 It("doesn't unblock if the deadline is changed before the first one expires", func() { 469 mockSender.EXPECT().onHasStreamData(streamID) 470 deadline1 := time.Now().Add(scaleDuration(50 * time.Millisecond)) 471 deadline2 := time.Now().Add(scaleDuration(100 * time.Millisecond)) 472 str.SetWriteDeadline(deadline1) 473 done := make(chan struct{}) 474 go func() { 475 defer GinkgoRecover() 476 time.Sleep(scaleDuration(20 * time.Millisecond)) 477 str.SetWriteDeadline(deadline2) 478 // make sure that this was actually execute before the deadline expires 479 Expect(time.Now()).To(BeTemporally("<", deadline1)) 480 close(done) 481 }() 482 runtime.Gosched() 483 n, err := strWithTimeout.Write(getData(5000)) 484 Expect(err).To(MatchError(errDeadline)) 485 Expect(n).To(BeZero()) 486 Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond))) 487 Eventually(done).Should(BeClosed()) 488 }) 489 490 It("unblocks earlier, when a new deadline is set", func() { 491 mockSender.EXPECT().onHasStreamData(streamID) 492 deadline1 := time.Now().Add(scaleDuration(200 * time.Millisecond)) 493 deadline2 := time.Now().Add(scaleDuration(50 * time.Millisecond)) 494 done := make(chan struct{}) 495 go func() { 496 defer GinkgoRecover() 497 time.Sleep(scaleDuration(10 * time.Millisecond)) 498 str.SetWriteDeadline(deadline2) 499 // make sure that this was actually execute before the deadline expires 500 Expect(time.Now()).To(BeTemporally("<", deadline2)) 501 close(done) 502 }() 503 str.SetWriteDeadline(deadline1) 504 runtime.Gosched() 505 _, err := strWithTimeout.Write(getData(5000)) 506 Expect(err).To(MatchError(errDeadline)) 507 Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond))) 508 Eventually(done).Should(BeClosed()) 509 }) 510 511 It("doesn't unblock if the deadline is removed", func() { 512 mockSender.EXPECT().onHasStreamData(streamID) 513 deadline := time.Now().Add(scaleDuration(50 * time.Millisecond)) 514 str.SetWriteDeadline(deadline) 515 deadlineUnset := make(chan struct{}) 516 go func() { 517 defer GinkgoRecover() 518 time.Sleep(scaleDuration(20 * time.Millisecond)) 519 str.SetWriteDeadline(time.Time{}) 520 // make sure that this was actually execute before the deadline expires 521 Expect(time.Now()).To(BeTemporally("<", deadline)) 522 close(deadlineUnset) 523 }() 524 done := make(chan struct{}) 525 go func() { 526 defer GinkgoRecover() 527 _, err := strWithTimeout.Write(getData(5000)) 528 Expect(err).To(MatchError("test done")) 529 close(done) 530 }() 531 runtime.Gosched() 532 Eventually(deadlineUnset).Should(BeClosed()) 533 Consistently(done, scaleDuration(100*time.Millisecond)).ShouldNot(BeClosed()) 534 // make the go routine return 535 str.closeForShutdown(errors.New("test done")) 536 Eventually(done).Should(BeClosed()) 537 }) 538 }) 539 540 Context("closing", func() { 541 It("doesn't allow writes after it has been closed", func() { 542 mockSender.EXPECT().onHasStreamData(streamID) 543 str.Close() 544 _, err := strWithTimeout.Write([]byte("foobar")) 545 Expect(err).To(MatchError("write on closed stream 1337")) 546 }) 547 548 It("allows FIN", func() { 549 mockSender.EXPECT().onHasStreamData(streamID) 550 str.Close() 551 frame, ok, hasMoreData := str.popStreamFrame(1000, protocol.Version1) 552 Expect(ok).To(BeTrue()) 553 Expect(frame).ToNot(BeNil()) 554 f := frame.Frame 555 Expect(f.Data).To(BeEmpty()) 556 Expect(f.Fin).To(BeTrue()) 557 Expect(f.DataLenPresent).To(BeTrue()) 558 Expect(hasMoreData).To(BeFalse()) 559 }) 560 561 It("doesn't send a FIN when there's still data", func() { 562 const frameHeaderLen protocol.ByteCount = 4 563 mockSender.EXPECT().onHasStreamData(streamID).Times(2) 564 _, err := strWithTimeout.Write([]byte("foobar")) 565 Expect(err).ToNot(HaveOccurred()) 566 Expect(str.Close()).To(Succeed()) 567 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2) 568 mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2) 569 frame, ok, _ := str.popStreamFrame(3+frameHeaderLen, protocol.Version1) 570 Expect(ok).To(BeTrue()) 571 Expect(frame).ToNot(BeNil()) 572 f := frame.Frame 573 Expect(f.Data).To(Equal([]byte("foo"))) 574 Expect(f.Fin).To(BeFalse()) 575 frame, ok, _ = str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 576 Expect(ok).To(BeTrue()) 577 f = frame.Frame 578 Expect(f.Data).To(Equal([]byte("bar"))) 579 Expect(f.Fin).To(BeTrue()) 580 }) 581 582 It("doesn't send a FIN when there's still data, for long writes", func() { 583 done := make(chan struct{}) 584 go func() { 585 defer GinkgoRecover() 586 defer close(done) 587 mockSender.EXPECT().onHasStreamData(streamID) 588 _, err := strWithTimeout.Write(getData(5000)) 589 Expect(err).ToNot(HaveOccurred()) 590 mockSender.EXPECT().onHasStreamData(streamID) 591 Expect(str.Close()).To(Succeed()) 592 }() 593 waitForWrite() 594 for i := 1; i <= 5; i++ { 595 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 596 mockFC.EXPECT().AddBytesSent(gomock.Any()) 597 if i == 5 { 598 Eventually(done).Should(BeClosed()) 599 } 600 frame, ok, _ := str.popStreamFrame(1100, protocol.Version1) 601 Expect(ok).To(BeTrue()) 602 Expect(frame).ToNot(BeNil()) 603 f := frame.Frame 604 Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen()))) 605 Expect(f.Fin).To(Equal(i == 5)) // the last frame should have the FIN bit set 606 } 607 }) 608 609 It("doesn't allow FIN after it is closed for shutdown", func() { 610 str.closeForShutdown(errors.New("test")) 611 _, ok, hasMoreData := str.popStreamFrame(1000, protocol.Version1) 612 Expect(ok).To(BeFalse()) 613 Expect(hasMoreData).To(BeFalse()) 614 615 Expect(str.Close()).To(Succeed()) 616 _, ok, hasMoreData = str.popStreamFrame(1000, protocol.Version1) 617 Expect(ok).To(BeFalse()) 618 Expect(hasMoreData).To(BeFalse()) 619 }) 620 621 It("doesn't allow FIN twice", func() { 622 mockSender.EXPECT().onHasStreamData(streamID) 623 str.Close() 624 frame, ok, _ := str.popStreamFrame(1000, protocol.Version1) 625 Expect(ok).To(BeTrue()) 626 Expect(frame).ToNot(BeNil()) 627 f := frame.Frame 628 Expect(f.Data).To(BeEmpty()) 629 Expect(f.Fin).To(BeTrue()) 630 _, ok, hasMoreData := str.popStreamFrame(1000, protocol.Version1) 631 Expect(ok).To(BeFalse()) 632 Expect(hasMoreData).To(BeFalse()) 633 }) 634 }) 635 636 Context("closing for shutdown", func() { 637 testErr := errors.New("test") 638 639 It("returns errors when the stream is cancelled", func() { 640 str.closeForShutdown(testErr) 641 n, err := strWithTimeout.Write([]byte("foo")) 642 Expect(n).To(BeZero()) 643 Expect(err).To(MatchError(testErr)) 644 }) 645 646 It("doesn't get data for writing if an error occurred", func() { 647 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 648 mockFC.EXPECT().AddBytesSent(gomock.Any()) 649 mockSender.EXPECT().onHasStreamData(streamID) 650 done := make(chan struct{}) 651 go func() { 652 defer GinkgoRecover() 653 _, err := strWithTimeout.Write(getData(5000)) 654 Expect(err).To(MatchError(testErr)) 655 close(done) 656 }() 657 waitForWrite() 658 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) // get a STREAM frame containing some data, but not all 659 Expect(ok).To(BeTrue()) 660 Expect(frame).ToNot(BeNil()) 661 Expect(hasMoreData).To(BeTrue()) 662 str.closeForShutdown(testErr) 663 _, ok, hasMoreData = str.popStreamFrame(1000, protocol.Version1) 664 Expect(ok).To(BeFalse()) 665 Expect(hasMoreData).To(BeFalse()) 666 Eventually(done).Should(BeClosed()) 667 }) 668 669 It("cancels the context", func() { 670 Expect(str.Context().Done()).ToNot(BeClosed()) 671 str.closeForShutdown(testErr) 672 Expect(str.Context().Done()).To(BeClosed()) 673 Expect(context.Cause(str.Context())).To(MatchError(testErr)) 674 }) 675 }) 676 }) 677 678 Context("handling MAX_STREAM_DATA frames", func() { 679 It("informs the flow controller", func() { 680 mockFC.EXPECT().UpdateSendWindow(protocol.ByteCount(0x1337)) 681 str.updateSendWindow(0x1337) 682 }) 683 684 It("says when it has data for sending", func() { 685 mockFC.EXPECT().UpdateSendWindow(gomock.Any()).Return(true) 686 mockSender.EXPECT().onHasStreamData(streamID) 687 done := make(chan struct{}) 688 go func() { 689 defer GinkgoRecover() 690 _, err := str.Write([]byte("foobar")) 691 Expect(err).ToNot(HaveOccurred()) 692 close(done) 693 }() 694 waitForWrite() 695 mockSender.EXPECT().onHasStreamData(streamID) 696 str.updateSendWindow(42) 697 // make sure the Write go routine returns 698 str.closeForShutdown(nil) 699 Eventually(done).Should(BeClosed()) 700 }) 701 702 It("doesn't say it has data for sending if the MAX_STREAM_DATA frame was reordered", func() { 703 mockFC.EXPECT().UpdateSendWindow(gomock.Any()).Return(false) // reordered frame 704 mockSender.EXPECT().onHasStreamData(streamID) 705 done := make(chan struct{}) 706 go func() { 707 defer GinkgoRecover() 708 _, err := str.Write([]byte("foobar")) 709 Expect(err).ToNot(HaveOccurred()) 710 close(done) 711 }() 712 waitForWrite() 713 // don't expect any calls to onHasStreamData 714 str.updateSendWindow(42) 715 // make sure the Write go routine returns 716 str.closeForShutdown(nil) 717 Eventually(done).Should(BeClosed()) 718 }) 719 }) 720 721 Context("stream cancellations", func() { 722 Context("canceling writing", func() { 723 It("queues a RESET_STREAM frame", func() { 724 gomock.InOrder( 725 mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{ 726 StreamID: streamID, 727 FinalSize: 1234, 728 ErrorCode: 9876, 729 }), 730 mockSender.EXPECT().onStreamCompleted(streamID), 731 ) 732 str.writeOffset = 1234 733 str.CancelWrite(9876) 734 }) 735 736 // This test is inherently racy, as it tests a concurrent call to Write() and CancelRead(). 737 // A single successful run of this test therefore doesn't mean a lot, 738 // for reliable results it has to be run many times. 739 It("returns a nil error when the whole slice has been sent out", func() { 740 mockSender.EXPECT().queueControlFrame(gomock.Any()).MaxTimes(1) 741 mockSender.EXPECT().onHasStreamData(streamID).MaxTimes(1) 742 mockSender.EXPECT().onStreamCompleted(streamID).MaxTimes(1) 743 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).MaxTimes(1) 744 mockFC.EXPECT().AddBytesSent(gomock.Any()).MaxTimes(1) 745 errChan := make(chan error) 746 go func() { 747 defer GinkgoRecover() 748 n, err := strWithTimeout.Write(getData(100)) 749 if n == 0 { 750 errChan <- nil 751 return 752 } 753 errChan <- err 754 }() 755 756 runtime.Gosched() 757 go str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 758 go str.CancelWrite(1234) 759 Eventually(errChan).Should(Receive(Not(HaveOccurred()))) 760 }) 761 762 It("unblocks Write", func() { 763 mockSender.EXPECT().queueControlFrame(gomock.Any()) 764 mockSender.EXPECT().onHasStreamData(streamID) 765 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 766 mockFC.EXPECT().AddBytesSent(gomock.Any()) 767 writeReturned := make(chan struct{}) 768 var n int 769 go func() { 770 defer GinkgoRecover() 771 var err error 772 n, err = strWithTimeout.Write(getData(5000)) 773 Expect(err).To(Equal(&StreamError{ 774 StreamID: streamID, 775 ErrorCode: 1234, 776 Remote: false, 777 })) 778 close(writeReturned) 779 }() 780 waitForWrite() 781 frame, ok, _ := str.popStreamFrame(50, protocol.Version1) 782 Expect(ok).To(BeTrue()) 783 Expect(frame).ToNot(BeNil()) 784 mockSender.EXPECT().onStreamCompleted(streamID) 785 str.CancelWrite(1234) 786 Eventually(writeReturned).Should(BeClosed()) 787 Expect(n).To(BeEquivalentTo(frame.Frame.DataLen())) 788 }) 789 790 It("doesn't pop STREAM frames after being canceled", func() { 791 mockSender.EXPECT().queueControlFrame(gomock.Any()) 792 mockSender.EXPECT().onHasStreamData(streamID) 793 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 794 mockFC.EXPECT().AddBytesSent(gomock.Any()) 795 writeReturned := make(chan struct{}) 796 go func() { 797 defer GinkgoRecover() 798 strWithTimeout.Write(getData(100)) 799 close(writeReturned) 800 }() 801 waitForWrite() 802 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 803 Expect(ok).To(BeTrue()) 804 Expect(hasMoreData).To(BeTrue()) 805 Expect(frame).ToNot(BeNil()) 806 mockSender.EXPECT().onStreamCompleted(streamID) 807 str.CancelWrite(1234) 808 _, ok, hasMoreData = str.popStreamFrame(10, protocol.Version1) 809 Expect(ok).To(BeFalse()) 810 Expect(hasMoreData).To(BeFalse()) 811 Eventually(writeReturned).Should(BeClosed()) 812 }) 813 814 It("doesn't pop STREAM frames after being canceled, for large writes", func() { 815 mockSender.EXPECT().queueControlFrame(gomock.Any()) 816 mockSender.EXPECT().onHasStreamData(streamID) 817 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 818 mockFC.EXPECT().AddBytesSent(gomock.Any()) 819 writeReturned := make(chan struct{}) 820 go func() { 821 defer GinkgoRecover() 822 _, err := strWithTimeout.Write(getData(5000)) 823 Expect(err).To(Equal(&StreamError{ 824 StreamID: streamID, 825 ErrorCode: 1234, 826 Remote: false, 827 })) 828 close(writeReturned) 829 }() 830 waitForWrite() 831 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 832 Expect(ok).To(BeTrue()) 833 Expect(hasMoreData).To(BeTrue()) 834 Expect(frame).ToNot(BeNil()) 835 mockSender.EXPECT().onStreamCompleted(streamID) 836 str.CancelWrite(1234) 837 _, ok, hasMoreData = str.popStreamFrame(10, protocol.Version1) 838 Expect(ok).To(BeFalse()) 839 Expect(hasMoreData).To(BeFalse()) 840 Eventually(writeReturned).Should(BeClosed()) 841 }) 842 843 It("ignores acknowledgements for STREAM frames after it was cancelled", func() { 844 mockSender.EXPECT().queueControlFrame(gomock.Any()) 845 mockSender.EXPECT().onHasStreamData(streamID) 846 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 847 mockFC.EXPECT().AddBytesSent(gomock.Any()) 848 writeReturned := make(chan struct{}) 849 go func() { 850 defer GinkgoRecover() 851 strWithTimeout.Write(getData(100)) 852 close(writeReturned) 853 }() 854 waitForWrite() 855 frame, ok, hasMoreData := str.popStreamFrame(50, protocol.Version1) 856 Expect(ok).To(BeTrue()) 857 Expect(hasMoreData).To(BeTrue()) 858 Expect(frame).ToNot(BeNil()) 859 mockSender.EXPECT().onStreamCompleted(streamID) 860 str.CancelWrite(1234) 861 frame.Handler.OnAcked(frame.Frame) 862 }) 863 864 It("cancels the context", func() { 865 mockSender.EXPECT().queueControlFrame(gomock.Any()) 866 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 867 Expect(str.Context().Done()).ToNot(BeClosed()) 868 str.CancelWrite(1234) 869 Expect(str.Context().Done()).To(BeClosed()) 870 Expect(context.Cause(str.Context())).To(BeAssignableToTypeOf(&StreamError{})) 871 Expect(context.Cause(str.Context()).(*StreamError).ErrorCode).To(Equal(StreamErrorCode(1234))) 872 }) 873 874 It("doesn't allow further calls to Write", func() { 875 mockSender.EXPECT().queueControlFrame(gomock.Any()) 876 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 877 str.CancelWrite(1234) 878 _, err := strWithTimeout.Write([]byte("foobar")) 879 Expect(err).To(MatchError(&StreamError{ 880 StreamID: streamID, 881 ErrorCode: 1234, 882 Remote: false, 883 })) 884 }) 885 886 It("only cancels once", func() { 887 mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{StreamID: streamID, ErrorCode: 1234}) 888 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 889 str.CancelWrite(1234) 890 str.CancelWrite(4321) 891 }) 892 893 It("queues a RESET_STREAM frame, even if the stream was already closed", func() { 894 mockSender.EXPECT().onHasStreamData(streamID) 895 mockSender.EXPECT().queueControlFrame(gomock.Any()).Do(func(f wire.Frame) { 896 Expect(f).To(BeAssignableToTypeOf(&wire.ResetStreamFrame{})) 897 }) 898 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 899 Expect(str.Close()).To(Succeed()) 900 // don't EXPECT any calls to queueControlFrame 901 str.CancelWrite(123) 902 }) 903 }) 904 905 Context("receiving STOP_SENDING frames", func() { 906 It("queues a RESET_STREAM frames, and copies the error code from the STOP_SENDING frame", func() { 907 mockSender.EXPECT().queueControlFrame(&wire.ResetStreamFrame{ 908 StreamID: streamID, 909 ErrorCode: 101, 910 }) 911 // Don't EXPECT calls to onStreamCompleted. 912 // The application needs to learn about the cancellation first. 913 str.handleStopSendingFrame(&wire.StopSendingFrame{ 914 StreamID: streamID, 915 ErrorCode: 101, 916 }) 917 }) 918 919 It("unblocks Write", func() { 920 mockSender.EXPECT().onHasStreamData(streamID) 921 mockSender.EXPECT().queueControlFrame(gomock.Any()) 922 done := make(chan struct{}) 923 go func() { 924 defer GinkgoRecover() 925 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 926 _, err := str.Write(getData(5000)) 927 Expect(err).To(Equal(&StreamError{ 928 StreamID: streamID, 929 ErrorCode: 123, 930 Remote: true, 931 })) 932 close(done) 933 }() 934 waitForWrite() 935 str.handleStopSendingFrame(&wire.StopSendingFrame{ 936 StreamID: streamID, 937 ErrorCode: 123, 938 }) 939 Eventually(done).Should(BeClosed()) 940 }) 941 942 It("doesn't allow further calls to Write", func() { 943 mockSender.EXPECT().queueControlFrame(gomock.Any()) 944 str.handleStopSendingFrame(&wire.StopSendingFrame{ 945 StreamID: streamID, 946 ErrorCode: 123, 947 }) 948 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 949 _, err := str.Write([]byte("foobar")) 950 Expect(err).To(Equal(&StreamError{ 951 StreamID: streamID, 952 ErrorCode: 123, 953 Remote: true, 954 })) 955 }) 956 957 It("handles Close after STOP_SENDING", func() { 958 mockSender.EXPECT().queueControlFrame(gomock.Any()) 959 str.handleStopSendingFrame(&wire.StopSendingFrame{ 960 StreamID: streamID, 961 ErrorCode: 123, 962 }) 963 mockSender.EXPECT().onStreamCompleted(gomock.Any()) 964 str.Close() 965 }) 966 967 It("handles STOP_SENDING after sending the FIN", func() { 968 mockSender.EXPECT().onHasStreamData(gomock.Any()) 969 str.Close() 970 _, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 971 Expect(ok).To(BeTrue()) 972 gomock.InOrder( 973 mockSender.EXPECT().queueControlFrame(gomock.Any()), 974 mockSender.EXPECT().onStreamCompleted(gomock.Any()), 975 ) 976 str.handleStopSendingFrame(&wire.StopSendingFrame{ 977 StreamID: streamID, 978 ErrorCode: 123, 979 }) 980 }) 981 982 It("handles STOP_SENDING after Close, but before sending the FIN", func() { 983 mockSender.EXPECT().onHasStreamData(gomock.Any()) 984 str.Close() 985 gomock.InOrder( 986 mockSender.EXPECT().queueControlFrame(gomock.Any()), 987 mockSender.EXPECT().onStreamCompleted(gomock.Any()), 988 ) 989 str.handleStopSendingFrame(&wire.StopSendingFrame{ 990 StreamID: streamID, 991 ErrorCode: 123, 992 }) 993 }) 994 }) 995 }) 996 997 Context("retransmissions", func() { 998 It("queues and retrieves frames", func() { 999 str.numOutstandingFrames = 1 1000 f := &wire.StreamFrame{ 1001 Data: []byte("foobar"), 1002 Offset: 0x42, 1003 DataLenPresent: false, 1004 } 1005 mockSender.EXPECT().onHasStreamData(streamID) 1006 (*sendStreamAckHandler)(str).OnLost(f) 1007 frame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1008 Expect(ok).To(BeTrue()) 1009 Expect(frame).ToNot(BeNil()) 1010 f = frame.Frame 1011 Expect(f.Offset).To(Equal(protocol.ByteCount(0x42))) 1012 Expect(f.Data).To(Equal([]byte("foobar"))) 1013 Expect(f.DataLenPresent).To(BeTrue()) 1014 }) 1015 1016 It("splits a retransmission", func() { 1017 str.numOutstandingFrames = 1 1018 sf := &wire.StreamFrame{ 1019 Data: []byte("foobar"), 1020 Offset: 0x42, 1021 DataLenPresent: false, 1022 } 1023 mockSender.EXPECT().onHasStreamData(streamID) 1024 (*sendStreamAckHandler)(str).OnLost(sf) 1025 frame, ok, hasMoreData := str.popStreamFrame(sf.Length(protocol.Version1)-3, protocol.Version1) 1026 Expect(ok).To(BeTrue()) 1027 Expect(frame).ToNot(BeNil()) 1028 f := frame.Frame 1029 Expect(hasMoreData).To(BeTrue()) 1030 Expect(f.Offset).To(Equal(protocol.ByteCount(0x42))) 1031 Expect(f.Data).To(Equal([]byte("foo"))) 1032 Expect(f.DataLenPresent).To(BeTrue()) 1033 frame, ok, _ = str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1034 Expect(ok).To(BeTrue()) 1035 Expect(frame).ToNot(BeNil()) 1036 f = frame.Frame 1037 Expect(f.Offset).To(Equal(protocol.ByteCount(0x45))) 1038 Expect(f.Data).To(Equal([]byte("bar"))) 1039 Expect(f.DataLenPresent).To(BeTrue()) 1040 }) 1041 1042 It("returns nil if the size is too small", func() { 1043 str.numOutstandingFrames = 1 1044 f := &wire.StreamFrame{ 1045 Data: []byte("foobar"), 1046 Offset: 0x42, 1047 DataLenPresent: false, 1048 } 1049 mockSender.EXPECT().onHasStreamData(streamID) 1050 (*sendStreamAckHandler)(str).OnLost(f) 1051 _, ok, hasMoreData := str.popStreamFrame(2, protocol.Version1) 1052 Expect(ok).To(BeFalse()) 1053 Expect(hasMoreData).To(BeTrue()) 1054 }) 1055 1056 It("queues lost STREAM frames", func() { 1057 mockSender.EXPECT().onHasStreamData(streamID) 1058 mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)) 1059 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) 1060 done := make(chan struct{}) 1061 go func() { 1062 defer GinkgoRecover() 1063 _, err := strWithTimeout.Write([]byte("foobar")) 1064 Expect(err).ToNot(HaveOccurred()) 1065 close(done) 1066 }() 1067 waitForWrite() 1068 frame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1069 Expect(ok).To(BeTrue()) 1070 Eventually(done).Should(BeClosed()) 1071 Expect(frame).ToNot(BeNil()) 1072 Expect(frame.Frame.Data).To(Equal([]byte("foobar"))) 1073 1074 // now lose the frame 1075 mockSender.EXPECT().onHasStreamData(streamID) 1076 frame.Handler.OnLost(frame.Frame) 1077 newFrame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1078 Expect(ok).To(BeTrue()) 1079 Expect(newFrame).ToNot(BeNil()) 1080 Expect(newFrame.Frame.Data).To(Equal([]byte("foobar"))) 1081 }) 1082 1083 It("doesn't queue retransmissions for a stream that was canceled", func() { 1084 mockSender.EXPECT().onHasStreamData(streamID) 1085 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) 1086 mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) 1087 done := make(chan struct{}) 1088 go func() { 1089 defer GinkgoRecover() 1090 _, err := str.Write([]byte("foobar")) 1091 Expect(err).ToNot(HaveOccurred()) 1092 close(done) 1093 }() 1094 waitForWrite() 1095 f, ok, _ := str.popStreamFrame(100, protocol.Version1) 1096 Expect(ok).To(BeTrue()) 1097 Eventually(done).Should(BeClosed()) 1098 Expect(f).ToNot(BeNil()) 1099 gomock.InOrder( 1100 mockSender.EXPECT().queueControlFrame(gomock.Any()), 1101 mockSender.EXPECT().onStreamCompleted(streamID), 1102 ) 1103 str.CancelWrite(9876) 1104 // don't EXPECT any calls to onHasStreamData 1105 f.Handler.OnLost(f.Frame) 1106 Expect(str.retransmissionQueue).To(BeEmpty()) 1107 }) 1108 }) 1109 1110 Context("determining when a stream is completed", func() { 1111 BeforeEach(func() { 1112 mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes() 1113 mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes() 1114 }) 1115 1116 It("says when a stream is completed", func() { 1117 mockSender.EXPECT().onHasStreamData(streamID) 1118 done := make(chan struct{}) 1119 go func() { 1120 defer GinkgoRecover() 1121 _, err := strWithTimeout.Write(make([]byte, 100)) 1122 Expect(err).ToNot(HaveOccurred()) 1123 close(done) 1124 }() 1125 waitForWrite() 1126 1127 // get a bunch of small frames (max. 20 bytes) 1128 var frames []ackhandler.StreamFrame 1129 for { 1130 frame, ok, hasMoreData := str.popStreamFrame(20, protocol.Version1) 1131 if !ok { 1132 continue 1133 } 1134 frames = append(frames, frame) 1135 if !hasMoreData { 1136 break 1137 } 1138 } 1139 Eventually(done).Should(BeClosed()) 1140 1141 // Acknowledge all frames. 1142 // We don't expect the stream to be completed, since we still need to send the FIN. 1143 for _, f := range frames { 1144 f.Handler.OnAcked(f.Frame) 1145 } 1146 1147 // Now close the stream and acknowledge the FIN. 1148 mockSender.EXPECT().onHasStreamData(streamID) 1149 Expect(str.Close()).To(Succeed()) 1150 frame, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1151 Expect(ok).To(BeTrue()) 1152 Expect(frame).ToNot(BeNil()) 1153 mockSender.EXPECT().onStreamCompleted(streamID) 1154 frame.Handler.OnAcked(frame.Frame) 1155 }) 1156 1157 It("says when a stream is completed, if Close() is called before popping the frame", func() { 1158 mockSender.EXPECT().onHasStreamData(streamID).Times(2) 1159 done := make(chan struct{}) 1160 go func() { 1161 defer GinkgoRecover() 1162 _, err := strWithTimeout.Write(make([]byte, 100)) 1163 Expect(err).ToNot(HaveOccurred()) 1164 close(done) 1165 }() 1166 waitForWrite() 1167 Eventually(done).Should(BeClosed()) 1168 Expect(str.Close()).To(Succeed()) 1169 1170 frame, ok, hasMoreData := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1171 Expect(ok).To(BeTrue()) 1172 Expect(hasMoreData).To(BeFalse()) 1173 Expect(frame).ToNot(BeNil()) 1174 Expect(frame.Frame.Fin).To(BeTrue()) 1175 1176 mockSender.EXPECT().onStreamCompleted(streamID) 1177 frame.Handler.OnAcked(frame.Frame) 1178 }) 1179 1180 It("doesn't say it's completed when there are frames waiting to be retransmitted", func() { 1181 mockSender.EXPECT().onHasStreamData(streamID) 1182 done := make(chan struct{}) 1183 go func() { 1184 defer GinkgoRecover() 1185 _, err := strWithTimeout.Write(getData(100)) 1186 Expect(err).ToNot(HaveOccurred()) 1187 mockSender.EXPECT().onHasStreamData(streamID) 1188 Expect(str.Close()).To(Succeed()) 1189 close(done) 1190 }() 1191 waitForWrite() 1192 1193 // get a bunch of small frames (max. 20 bytes) 1194 var frames []ackhandler.StreamFrame 1195 for { 1196 frame, ok, _ := str.popStreamFrame(20, protocol.Version1) 1197 if !ok { 1198 continue 1199 } 1200 frames = append(frames, frame) 1201 if frame.Frame.Fin { 1202 break 1203 } 1204 } 1205 Eventually(done).Should(BeClosed()) 1206 1207 // lose the first frame, acknowledge all others 1208 for _, f := range frames[1:] { 1209 f.Handler.OnAcked(f.Frame) 1210 } 1211 mockSender.EXPECT().onHasStreamData(streamID) 1212 frames[0].Handler.OnLost(frames[0].Frame) 1213 1214 // get the retransmission and acknowledge it 1215 ret, ok, _ := str.popStreamFrame(protocol.MaxByteCount, protocol.Version1) 1216 Expect(ok).To(BeTrue()) 1217 Expect(ret).ToNot(BeNil()) 1218 mockSender.EXPECT().onStreamCompleted(streamID) 1219 ret.Handler.OnAcked(ret.Frame) 1220 }) 1221 1222 // This test is kind of an integration test. 1223 // It writes 4 MB of data, and pops STREAM frames that sometimes are and sometimes aren't limited by flow control. 1224 // Half of these STREAM frames are then received and their content saved, while the other half is reported lost 1225 // and has to be retransmitted. 1226 It("retransmits data until everything has been acknowledged", func() { 1227 const dataLen = 1 << 22 // 4 MB 1228 mockSender.EXPECT().onHasStreamData(streamID).AnyTimes() 1229 mockFC.EXPECT().SendWindowSize().DoAndReturn(func() protocol.ByteCount { 1230 return protocol.ByteCount(mrand.Intn(500)) + 50 1231 }).AnyTimes() 1232 mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes() 1233 1234 data := make([]byte, dataLen) 1235 _, err := rand.Read(data) 1236 Expect(err).ToNot(HaveOccurred()) 1237 done := make(chan struct{}) 1238 go func() { 1239 defer GinkgoRecover() 1240 defer close(done) 1241 _, err := str.Write(data) 1242 Expect(err).ToNot(HaveOccurred()) 1243 str.Close() 1244 }() 1245 1246 var completed bool 1247 mockSender.EXPECT().onStreamCompleted(streamID).Do(func(protocol.StreamID) { completed = true }) 1248 1249 received := make([]byte, dataLen) 1250 for { 1251 if completed { 1252 break 1253 } 1254 f, ok, _ := str.popStreamFrame(protocol.ByteCount(mrand.Intn(300)+100), protocol.Version1) 1255 if !ok { 1256 continue 1257 } 1258 sf := f.Frame 1259 // 50%: acknowledge the frame and save the data 1260 // 50%: lose the frame 1261 if mrand.Intn(100) < 50 { 1262 copy(received[sf.Offset:sf.Offset+sf.DataLen()], sf.Data) 1263 f.Handler.OnAcked(f.Frame) 1264 } else { 1265 f.Handler.OnLost(f.Frame) 1266 } 1267 } 1268 Expect(received).To(Equal(data)) 1269 }) 1270 }) 1271 })