package flowcontrol import ( "time" "github.com/lucas-clemente/quic-go/internal/protocol" "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" ) var _ = Describe("Connection Flow controller", func() { var ( controller *connectionFlowController queuedWindowUpdate bool ) // 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() { queuedWindowUpdate = false controller = &connectionFlowController{} controller.rttStats = &utils.RTTStats{} controller.logger = utils.DefaultLogger controller.queueWindowUpdate = func() { queuedWindowUpdate = true } controller.allowWindowIncrease = func(protocol.ByteCount) bool { return true } }) Context("Constructor", func() { rttStats := &utils.RTTStats{} It("sets the send and receive windows", func() { receiveWindow := protocol.ByteCount(2000) maxReceiveWindow := protocol.ByteCount(3000) fc := NewConnectionFlowController( receiveWindow, maxReceiveWindow, nil, func(protocol.ByteCount) bool { return true }, rttStats, utils.DefaultLogger).(*connectionFlowController) Expect(fc.receiveWindow).To(Equal(receiveWindow)) Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow)) }) }) Context("receive flow control", func() { It("increases the highestReceived by a given window size", func() { controller.highestReceived = 1337 controller.IncrementHighestReceived(123) Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337 + 123))) }) Context("getting window updates", func() { BeforeEach(func() { controller.receiveWindow = 100 controller.receiveWindowSize = 60 controller.maxReceiveWindowSize = 1000 controller.bytesRead = 100 - 60 }) It("queues window updates", func() { controller.AddBytesRead(1) Expect(queuedWindowUpdate).To(BeFalse()) controller.AddBytesRead(29) Expect(queuedWindowUpdate).To(BeTrue()) Expect(controller.GetWindowUpdate()).ToNot(BeZero()) queuedWindowUpdate = false controller.AddBytesRead(1) Expect(queuedWindowUpdate).To(BeFalse()) }) It("gets a window update", func() { windowSize := controller.receiveWindowSize oldOffset := controller.bytesRead dataRead := windowSize/2 - 1 // make sure not to trigger auto-tuning controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() Expect(offset).To(Equal(oldOffset + dataRead + 60)) }) It("auto-tunes the window", func() { var allowed protocol.ByteCount controller.allowWindowIncrease = func(size protocol.ByteCount) bool { allowed = size return true } oldOffset := controller.bytesRead oldWindowSize := controller.receiveWindowSize rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.epochStartOffset = oldOffset dataRead := oldWindowSize/2 + 1 controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(2 * oldWindowSize)) Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize)) Expect(allowed).To(Equal(oldWindowSize)) }) It("doesn't auto-tune the window if it's not allowed", func() { controller.allowWindowIncrease = func(protocol.ByteCount) bool { return false } oldOffset := controller.bytesRead oldWindowSize := controller.receiveWindowSize rtt := scaleDuration(20 * time.Millisecond) setRtt(rtt) controller.epochStartTime = time.Now().Add(-time.Millisecond) controller.epochStartOffset = oldOffset dataRead := oldWindowSize/2 + 1 controller.AddBytesRead(dataRead) offset := controller.GetWindowUpdate() newWindowSize := controller.receiveWindowSize Expect(newWindowSize).To(Equal(oldWindowSize)) Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize)) }) }) }) Context("setting the minimum window size", func() { var ( oldWindowSize protocol.ByteCount receiveWindow protocol.ByteCount = 10000 receiveWindowSize protocol.ByteCount = 1000 ) BeforeEach(func() { controller.receiveWindow = receiveWindow controller.receiveWindowSize = receiveWindowSize oldWindowSize = controller.receiveWindowSize controller.maxReceiveWindowSize = 3000 }) It("sets the minimum window window size", func() { controller.EnsureMinimumWindowSize(1800) Expect(controller.receiveWindowSize).To(Equal(protocol.ByteCount(1800))) }) It("doesn't reduce the window window size", func() { controller.EnsureMinimumWindowSize(1) Expect(controller.receiveWindowSize).To(Equal(oldWindowSize)) }) It("doesn't increase the window size beyond the maxReceiveWindowSize", func() { max := controller.maxReceiveWindowSize controller.EnsureMinimumWindowSize(2 * max) Expect(controller.receiveWindowSize).To(Equal(max)) }) It("starts a new epoch after the window size was increased", func() { controller.EnsureMinimumWindowSize(1912) Expect(controller.epochStartTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond)) }) }) Context("resetting", func() { It("resets", func() { const initialWindow protocol.ByteCount = 1337 controller.UpdateSendWindow(initialWindow) controller.AddBytesSent(1000) Expect(controller.SendWindowSize()).To(Equal(initialWindow - 1000)) Expect(controller.Reset()).To(Succeed()) Expect(controller.SendWindowSize()).To(Equal(initialWindow)) }) It("says if is blocked after resetting", func() { const initialWindow protocol.ByteCount = 1337 controller.UpdateSendWindow(initialWindow) controller.AddBytesSent(initialWindow) blocked, _ := controller.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(controller.Reset()).To(Succeed()) controller.AddBytesSent(initialWindow) blocked, blockedAt := controller.IsNewlyBlocked() Expect(blocked).To(BeTrue()) Expect(blockedAt).To(Equal(initialWindow)) }) }) })