package flowcontrol import ( "time" "github.com/lucas-clemente/quic-go/congestion" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/qerr" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) var _ = Describe("Stream Flow controller", func() { var controller *streamFlowController BeforeEach(func() { rttStats := &congestion.RTTStats{} controller = &streamFlowController{ streamID: 10, connection: NewConnectionFlowController(1000, 1000, rttStats).(*connectionFlowController), } controller.maxReceiveWindowSize = 10000 controller.rttStats = rttStats }) Context("Constructor", func() { rttStats := &congestion.RTTStats{} It("sets the send and receive windows", func() { receiveWindow := protocol.ByteCount(2000) maxReceiveWindow := protocol.ByteCount(3000) sendWindow := protocol.ByteCount(4000) cc := NewConnectionFlowController(0, 0, nil) fc := NewStreamFlowController(5, true, cc, receiveWindow, maxReceiveWindow, sendWindow, rttStats).(*streamFlowController) Expect(fc.streamID).To(Equal(protocol.StreamID(5))) Expect(fc.receiveWindow).To(Equal(receiveWindow)) Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow)) Expect(fc.sendWindow).To(Equal(sendWindow)) Expect(fc.contributesToConnection).To(BeTrue()) }) }) Context("receiving data", func() { Context("registering received offsets", func() { var receiveWindow protocol.ByteCount = 10000 var receiveWindowSize protocol.ByteCount = 600 BeforeEach(func() { controller.receiveWindow = receiveWindow controller.receiveWindowSize = receiveWindowSize }) It("updates the highestReceived", func() { controller.highestReceived = 1337 err := controller.UpdateHighestReceived(1338, false) Expect(err).ToNot(HaveOccurred()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1338))) }) It("informs the connection flow controller about received data", func() { controller.highestReceived = 10 controller.contributesToConnection = true controller.connection.(*connectionFlowController).highestReceived = 100 err := controller.UpdateHighestReceived(20, false) Expect(err).ToNot(HaveOccurred()) Expect(controller.connection.(*connectionFlowController).highestReceived).To(Equal(protocol.ByteCount(100 + 10))) }) It("doesn't informs the connection flow controller about received data if it doesn't contribute", func() { controller.highestReceived = 10 controller.connection.(*connectionFlowController).highestReceived = 100 err := controller.UpdateHighestReceived(20, false) Expect(err).ToNot(HaveOccurred()) Expect(controller.connection.(*connectionFlowController).highestReceived).To(Equal(protocol.ByteCount(100))) }) It("does not decrease the highestReceived", func() { controller.highestReceived = 1337 err := controller.UpdateHighestReceived(1000, false) Expect(err).ToNot(HaveOccurred()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337))) }) It("does nothing when setting the same byte offset", func() { controller.highestReceived = 1337 err := controller.UpdateHighestReceived(1337, false) Expect(err).ToNot(HaveOccurred()) }) It("does not give a flow control violation when using the window completely", func() { err := controller.UpdateHighestReceived(receiveWindow, false) Expect(err).ToNot(HaveOccurred()) }) It("detects a flow control violation", func() { err := controller.UpdateHighestReceived(receiveWindow+1, false) Expect(err).To(MatchError("FlowControlReceivedTooMuchData: Received 10001 bytes on stream 10, allowed 10000 bytes")) }) It("accepts a final offset higher than the highest received", func() { controller.highestReceived = 100 err := controller.UpdateHighestReceived(101, true) Expect(err).ToNot(HaveOccurred()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(101))) }) It("errors when receiving a final offset smaller than the highest offset received so far", func() { controller.highestReceived = 100 err := controller.UpdateHighestReceived(99, true) Expect(err).To(MatchError(qerr.StreamDataAfterTermination)) }) It("accepts delayed data after receiving a final offset", func() { err := controller.UpdateHighestReceived(300, true) Expect(err).ToNot(HaveOccurred()) err = controller.UpdateHighestReceived(250, false) Expect(err).ToNot(HaveOccurred()) }) It("errors when receiving a higher offset after receiving a final offset", func() { err := controller.UpdateHighestReceived(200, true) Expect(err).ToNot(HaveOccurred()) err = controller.UpdateHighestReceived(250, false) Expect(err).To(MatchError(qerr.StreamDataAfterTermination)) }) It("accepts duplicate final offsets", func() { err := controller.UpdateHighestReceived(200, true) Expect(err).ToNot(HaveOccurred()) err = controller.UpdateHighestReceived(200, true) Expect(err).ToNot(HaveOccurred()) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(200))) }) It("errors when receiving inconsistent final offsets", func() { err := controller.UpdateHighestReceived(200, true) Expect(err).ToNot(HaveOccurred()) err = controller.UpdateHighestReceived(201, true) Expect(err).To(MatchError("StreamDataAfterTermination: Received inconsistent final offset for stream 10 (old: 200, new: 201 bytes)")) }) }) Context("registering data read", func() { It("saves when data is read, on a stream not contributing to the connection", func() { controller.AddBytesRead(100) Expect(controller.bytesRead).To(Equal(protocol.ByteCount(100))) Expect(controller.connection.(*connectionFlowController).bytesRead).To(BeZero()) }) It("saves when data is read, on a stream not contributing to the connection", func() { controller.contributesToConnection = true controller.AddBytesRead(200) Expect(controller.bytesRead).To(Equal(protocol.ByteCount(200))) Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(200))) }) }) Context("generating window updates", func() { var oldWindowSize protocol.ByteCount // update the congestion such that it returns a given value for the smoothed RTT setRtt := func(t time.Duration) { controller.rttStats.UpdateRTT(t, 0, time.Now()) Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked } BeforeEach(func() { controller.receiveWindow = 100 controller.receiveWindowSize = 60 controller.bytesRead = 100 - 60 controller.connection.(*connectionFlowController).receiveWindowSize = 120 oldWindowSize = controller.receiveWindowSize }) It("tells if it has window updates", func() { Expect(controller.HasWindowUpdate()).To(BeFalse()) controller.AddBytesRead(30) Expect(controller.HasWindowUpdate()).To(BeTrue()) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) Expect(controller.HasWindowUpdate()).To(BeFalse()) }) It("tells the connection flow controller when the window was autotuned", func() { oldOffset := controller.bytesRead controller.contributesToConnection = true setRtt(scaleDuration(20 * time.Millisecond)) controller.epochStartOffset = oldOffset controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.AddBytesRead(55) offset := controller.GetWindowUpdate() Expect(offset).To(Equal(protocol.ByteCount(oldOffset + 55 + 2*oldWindowSize))) Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(protocol.ByteCount(float64(controller.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))) }) It("doesn't tell the connection flow controller if it doesn't contribute", func() { oldOffset := controller.bytesRead controller.contributesToConnection = false setRtt(scaleDuration(20 * time.Millisecond)) controller.epochStartOffset = oldOffset controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.AddBytesRead(55) offset := controller.GetWindowUpdate() Expect(offset).ToNot(BeZero()) Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(protocol.ByteCount(2 * oldWindowSize))) // unchanged }) It("doesn't increase the window after a final offset was already received", func() { controller.AddBytesRead(30) err := controller.UpdateHighestReceived(90, true) Expect(err).ToNot(HaveOccurred()) Expect(controller.HasWindowUpdate()).To(BeFalse()) offset := controller.GetWindowUpdate() Expect(offset).To(BeZero()) }) }) }) Context("sending data", func() { It("gets the size of the send window", func() { controller.UpdateSendWindow(15) controller.AddBytesSent(5) Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(10))) }) It("doesn't care about the connection-level window, if it doesn't contribute", func() { controller.UpdateSendWindow(15) controller.connection.UpdateSendWindow(1) controller.AddBytesSent(5) Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(10))) }) It("makes sure that it doesn't overflow the connection-level window", func() { controller.contributesToConnection = true controller.connection.UpdateSendWindow(12) controller.UpdateSendWindow(20) controller.AddBytesSent(10) Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(2))) }) It("doesn't say that it's blocked, if only the connection is blocked", func() { controller.contributesToConnection = true controller.connection.UpdateSendWindow(50) controller.UpdateSendWindow(100) controller.AddBytesSent(50) blocked, _ := controller.connection.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(controller.IsBlocked()).To(BeFalse()) }) }) })