github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/flowcontrol/connection_flow_controller_test.go (about)

     1  package flowcontrol
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/apernet/quic-go/internal/protocol"
     7  	"github.com/apernet/quic-go/internal/utils"
     8  
     9  	. "github.com/onsi/ginkgo/v2"
    10  	. "github.com/onsi/gomega"
    11  )
    12  
    13  var _ = Describe("Connection Flow controller", func() {
    14  	var (
    15  		controller         *connectionFlowController
    16  		queuedWindowUpdate bool
    17  	)
    18  
    19  	// update the congestion such that it returns a given value for the smoothed RTT
    20  	setRtt := func(t time.Duration) {
    21  		controller.rttStats.UpdateRTT(t, 0, time.Now())
    22  		Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked
    23  	}
    24  
    25  	BeforeEach(func() {
    26  		queuedWindowUpdate = false
    27  		controller = &connectionFlowController{}
    28  		controller.rttStats = &utils.RTTStats{}
    29  		controller.logger = utils.DefaultLogger
    30  		controller.queueWindowUpdate = func() { queuedWindowUpdate = true }
    31  		controller.allowWindowIncrease = func(protocol.ByteCount) bool { return true }
    32  	})
    33  
    34  	Context("Constructor", func() {
    35  		rttStats := &utils.RTTStats{}
    36  
    37  		It("sets the send and receive windows", func() {
    38  			receiveWindow := protocol.ByteCount(2000)
    39  			maxReceiveWindow := protocol.ByteCount(3000)
    40  
    41  			fc := NewConnectionFlowController(
    42  				receiveWindow,
    43  				maxReceiveWindow,
    44  				nil,
    45  				func(protocol.ByteCount) bool { return true },
    46  				rttStats,
    47  				utils.DefaultLogger).(*connectionFlowController)
    48  			Expect(fc.receiveWindow).To(Equal(receiveWindow))
    49  			Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow))
    50  		})
    51  	})
    52  
    53  	Context("receive flow control", func() {
    54  		It("increases the highestReceived by a given window size", func() {
    55  			controller.highestReceived = 1337
    56  			controller.IncrementHighestReceived(123)
    57  			Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337 + 123)))
    58  		})
    59  
    60  		Context("getting window updates", func() {
    61  			BeforeEach(func() {
    62  				controller.receiveWindow = 100
    63  				controller.receiveWindowSize = 60
    64  				controller.maxReceiveWindowSize = 1000
    65  				controller.bytesRead = 100 - 60
    66  			})
    67  
    68  			It("queues window updates", func() {
    69  				controller.AddBytesRead(1)
    70  				Expect(queuedWindowUpdate).To(BeFalse())
    71  				controller.AddBytesRead(29)
    72  				Expect(queuedWindowUpdate).To(BeTrue())
    73  				Expect(controller.GetWindowUpdate()).ToNot(BeZero())
    74  				queuedWindowUpdate = false
    75  				controller.AddBytesRead(1)
    76  				Expect(queuedWindowUpdate).To(BeFalse())
    77  			})
    78  
    79  			It("gets a window update", func() {
    80  				windowSize := controller.receiveWindowSize
    81  				oldOffset := controller.bytesRead
    82  				dataRead := windowSize/2 - 1 // make sure not to trigger auto-tuning
    83  				controller.AddBytesRead(dataRead)
    84  				offset := controller.GetWindowUpdate()
    85  				Expect(offset).To(Equal(oldOffset + dataRead + 60))
    86  			})
    87  
    88  			It("auto-tunes the window", func() {
    89  				var allowed protocol.ByteCount
    90  				controller.allowWindowIncrease = func(size protocol.ByteCount) bool {
    91  					allowed = size
    92  					return true
    93  				}
    94  				oldOffset := controller.bytesRead
    95  				oldWindowSize := controller.receiveWindowSize
    96  				rtt := scaleDuration(20 * time.Millisecond)
    97  				setRtt(rtt)
    98  				controller.epochStartTime = time.Now().Add(-time.Millisecond)
    99  				controller.epochStartOffset = oldOffset
   100  				dataRead := oldWindowSize/2 + 1
   101  				controller.AddBytesRead(dataRead)
   102  				offset := controller.GetWindowUpdate()
   103  				newWindowSize := controller.receiveWindowSize
   104  				Expect(newWindowSize).To(Equal(2 * oldWindowSize))
   105  				Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize))
   106  				Expect(allowed).To(Equal(oldWindowSize))
   107  			})
   108  
   109  			It("doesn't auto-tune the window if it's not allowed", func() {
   110  				controller.allowWindowIncrease = func(protocol.ByteCount) bool { return false }
   111  				oldOffset := controller.bytesRead
   112  				oldWindowSize := controller.receiveWindowSize
   113  				rtt := scaleDuration(20 * time.Millisecond)
   114  				setRtt(rtt)
   115  				controller.epochStartTime = time.Now().Add(-time.Millisecond)
   116  				controller.epochStartOffset = oldOffset
   117  				dataRead := oldWindowSize/2 + 1
   118  				controller.AddBytesRead(dataRead)
   119  				offset := controller.GetWindowUpdate()
   120  				newWindowSize := controller.receiveWindowSize
   121  				Expect(newWindowSize).To(Equal(oldWindowSize))
   122  				Expect(offset).To(Equal(oldOffset + dataRead + newWindowSize))
   123  			})
   124  		})
   125  	})
   126  
   127  	Context("setting the minimum window size", func() {
   128  		var (
   129  			oldWindowSize     protocol.ByteCount
   130  			receiveWindow     protocol.ByteCount = 10000
   131  			receiveWindowSize protocol.ByteCount = 1000
   132  		)
   133  
   134  		BeforeEach(func() {
   135  			controller.receiveWindow = receiveWindow
   136  			controller.receiveWindowSize = receiveWindowSize
   137  			oldWindowSize = controller.receiveWindowSize
   138  			controller.maxReceiveWindowSize = 3000
   139  		})
   140  
   141  		It("sets the minimum window window size", func() {
   142  			controller.EnsureMinimumWindowSize(1800)
   143  			Expect(controller.receiveWindowSize).To(Equal(protocol.ByteCount(1800)))
   144  		})
   145  
   146  		It("doesn't reduce the window window size", func() {
   147  			controller.EnsureMinimumWindowSize(1)
   148  			Expect(controller.receiveWindowSize).To(Equal(oldWindowSize))
   149  		})
   150  
   151  		It("doesn't increase the window size beyond the maxReceiveWindowSize", func() {
   152  			max := controller.maxReceiveWindowSize
   153  			controller.EnsureMinimumWindowSize(2 * max)
   154  			Expect(controller.receiveWindowSize).To(Equal(max))
   155  		})
   156  
   157  		It("starts a new epoch after the window size was increased", func() {
   158  			controller.EnsureMinimumWindowSize(1912)
   159  			Expect(controller.epochStartTime).To(BeTemporally("~", time.Now(), 100*time.Millisecond))
   160  		})
   161  	})
   162  
   163  	Context("resetting", func() {
   164  		It("resets", func() {
   165  			const initialWindow protocol.ByteCount = 1337
   166  			controller.UpdateSendWindow(initialWindow)
   167  			controller.AddBytesSent(1000)
   168  			Expect(controller.SendWindowSize()).To(Equal(initialWindow - 1000))
   169  			Expect(controller.Reset()).To(Succeed())
   170  			Expect(controller.SendWindowSize()).To(Equal(initialWindow))
   171  		})
   172  
   173  		It("says if is blocked after resetting", func() {
   174  			const initialWindow protocol.ByteCount = 1337
   175  			controller.UpdateSendWindow(initialWindow)
   176  			controller.AddBytesSent(initialWindow)
   177  			blocked, _ := controller.IsNewlyBlocked()
   178  			Expect(blocked).To(BeTrue())
   179  			Expect(controller.Reset()).To(Succeed())
   180  			controller.AddBytesSent(initialWindow)
   181  			blocked, blockedAt := controller.IsNewlyBlocked()
   182  			Expect(blocked).To(BeTrue())
   183  			Expect(blockedAt).To(Equal(initialWindow))
   184  		})
   185  	})
   186  })