github.com/quic-go/quic-go@v0.44.0/internal/flowcontrol/stream_flow_controller_test.go (about)

     1  package flowcontrol
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/quic-go/quic-go/internal/protocol"
     7  	"github.com/quic-go/quic-go/internal/qerr"
     8  	"github.com/quic-go/quic-go/internal/utils"
     9  
    10  	. "github.com/onsi/ginkgo/v2"
    11  	. "github.com/onsi/gomega"
    12  )
    13  
    14  var _ = Describe("Stream Flow controller", func() {
    15  	var (
    16  		controller         *streamFlowController
    17  		queuedWindowUpdate bool
    18  	)
    19  
    20  	BeforeEach(func() {
    21  		queuedWindowUpdate = false
    22  		rttStats := &utils.RTTStats{}
    23  		controller = &streamFlowController{
    24  			streamID: 10,
    25  			connection: NewConnectionFlowController(
    26  				1000,
    27  				1000,
    28  				func() {},
    29  				func(protocol.ByteCount) bool { return true },
    30  				rttStats,
    31  				utils.DefaultLogger,
    32  			).(*connectionFlowController),
    33  		}
    34  		controller.maxReceiveWindowSize = 10000
    35  		controller.rttStats = rttStats
    36  		controller.logger = utils.DefaultLogger
    37  		controller.queueWindowUpdate = func() { queuedWindowUpdate = true }
    38  	})
    39  
    40  	Context("Constructor", func() {
    41  		rttStats := &utils.RTTStats{}
    42  		const receiveWindow protocol.ByteCount = 2000
    43  		const maxReceiveWindow protocol.ByteCount = 3000
    44  		const sendWindow protocol.ByteCount = 4000
    45  
    46  		It("sets the send and receive windows", func() {
    47  			cc := NewConnectionFlowController(0, 0, nil, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger)
    48  			fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, nil, rttStats, utils.DefaultLogger).(*streamFlowController)
    49  			Expect(fc.streamID).To(Equal(protocol.StreamID(5)))
    50  			Expect(fc.receiveWindow).To(Equal(receiveWindow))
    51  			Expect(fc.maxReceiveWindowSize).To(Equal(maxReceiveWindow))
    52  			Expect(fc.sendWindow).To(Equal(sendWindow))
    53  		})
    54  
    55  		It("queues window updates with the correct stream ID", func() {
    56  			var queued bool
    57  			queueWindowUpdate := func(id protocol.StreamID) {
    58  				Expect(id).To(Equal(protocol.StreamID(5)))
    59  				queued = true
    60  			}
    61  
    62  			cc := NewConnectionFlowController(receiveWindow, maxReceiveWindow, func() {}, func(protocol.ByteCount) bool { return true }, nil, utils.DefaultLogger)
    63  			fc := NewStreamFlowController(5, cc, receiveWindow, maxReceiveWindow, sendWindow, queueWindowUpdate, rttStats, utils.DefaultLogger).(*streamFlowController)
    64  			fc.AddBytesRead(receiveWindow)
    65  			Expect(queued).To(BeTrue())
    66  		})
    67  	})
    68  
    69  	Context("receiving data", func() {
    70  		Context("registering received offsets", func() {
    71  			var receiveWindow protocol.ByteCount = 10000
    72  			var receiveWindowSize protocol.ByteCount = 600
    73  
    74  			BeforeEach(func() {
    75  				controller.receiveWindow = receiveWindow
    76  				controller.receiveWindowSize = receiveWindowSize
    77  			})
    78  
    79  			It("updates the highestReceived", func() {
    80  				controller.highestReceived = 1337
    81  				Expect(controller.UpdateHighestReceived(1338, false)).To(Succeed())
    82  				Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1338)))
    83  			})
    84  
    85  			It("informs the connection flow controller about received data", func() {
    86  				controller.highestReceived = 10
    87  				controller.connection.(*connectionFlowController).highestReceived = 100
    88  				Expect(controller.UpdateHighestReceived(20, false)).To(Succeed())
    89  				Expect(controller.connection.(*connectionFlowController).highestReceived).To(Equal(protocol.ByteCount(100 + 10)))
    90  			})
    91  
    92  			It("does not decrease the highestReceived", func() {
    93  				controller.highestReceived = 1337
    94  				Expect(controller.UpdateHighestReceived(1000, false)).To(Succeed())
    95  				Expect(controller.highestReceived).To(Equal(protocol.ByteCount(1337)))
    96  			})
    97  
    98  			It("does nothing when setting the same byte offset", func() {
    99  				controller.highestReceived = 1337
   100  				Expect(controller.UpdateHighestReceived(1337, false)).To(Succeed())
   101  			})
   102  
   103  			It("does not give a flow control violation when using the window completely", func() {
   104  				controller.connection.(*connectionFlowController).receiveWindow = receiveWindow
   105  				Expect(controller.UpdateHighestReceived(receiveWindow, false)).To(Succeed())
   106  			})
   107  
   108  			It("detects a flow control violation", func() {
   109  				Expect(controller.UpdateHighestReceived(receiveWindow+1, false)).To(MatchError(&qerr.TransportError{
   110  					ErrorCode:    qerr.FlowControlError,
   111  					ErrorMessage: "received 10001 bytes on stream 10, allowed 10000 bytes",
   112  				}))
   113  			})
   114  
   115  			It("accepts a final offset higher than the highest received", func() {
   116  				Expect(controller.UpdateHighestReceived(100, false)).To(Succeed())
   117  				Expect(controller.UpdateHighestReceived(101, true)).To(Succeed())
   118  				Expect(controller.highestReceived).To(Equal(protocol.ByteCount(101)))
   119  			})
   120  
   121  			It("errors when receiving a final offset smaller than the highest offset received so far", func() {
   122  				controller.UpdateHighestReceived(100, false)
   123  				Expect(controller.UpdateHighestReceived(50, true)).To(MatchError(&qerr.TransportError{
   124  					ErrorCode:    qerr.FinalSizeError,
   125  					ErrorMessage: "received final offset 50 for stream 10, but already received offset 100 before",
   126  				}))
   127  			})
   128  
   129  			It("accepts delayed data after receiving a final offset", func() {
   130  				Expect(controller.UpdateHighestReceived(300, true)).To(Succeed())
   131  				Expect(controller.UpdateHighestReceived(250, false)).To(Succeed())
   132  			})
   133  
   134  			It("errors when receiving a higher offset after receiving a final offset", func() {
   135  				Expect(controller.UpdateHighestReceived(200, true)).To(Succeed())
   136  				Expect(controller.UpdateHighestReceived(250, false)).To(MatchError(&qerr.TransportError{
   137  					ErrorCode:    qerr.FinalSizeError,
   138  					ErrorMessage: "received offset 250 for stream 10, but final offset was already received at 200",
   139  				}))
   140  			})
   141  
   142  			It("accepts duplicate final offsets", func() {
   143  				Expect(controller.UpdateHighestReceived(200, true)).To(Succeed())
   144  				Expect(controller.UpdateHighestReceived(200, true)).To(Succeed())
   145  				Expect(controller.highestReceived).To(Equal(protocol.ByteCount(200)))
   146  			})
   147  
   148  			It("errors when receiving inconsistent final offsets", func() {
   149  				Expect(controller.UpdateHighestReceived(200, true)).To(Succeed())
   150  				Expect(controller.UpdateHighestReceived(201, true)).To(MatchError(&qerr.TransportError{
   151  					ErrorCode:    qerr.FinalSizeError,
   152  					ErrorMessage: "received inconsistent final offset for stream 10 (old: 200, new: 201 bytes)",
   153  				}))
   154  			})
   155  
   156  			It("tells the connection flow controller when a stream is abandoned", func() {
   157  				controller.AddBytesRead(5)
   158  				Expect(controller.UpdateHighestReceived(100, true)).To(Succeed())
   159  				controller.Abandon()
   160  				Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(100)))
   161  			})
   162  
   163  			It("tolerates repeated calls to Abandon", func() {
   164  				controller.AddBytesRead(5)
   165  				Expect(controller.UpdateHighestReceived(100, true)).To(Succeed())
   166  				controller.Abandon()
   167  				controller.Abandon()
   168  				controller.Abandon()
   169  				Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(100)))
   170  			})
   171  		})
   172  
   173  		It("saves when data is read", func() {
   174  			controller.AddBytesRead(200)
   175  			Expect(controller.bytesRead).To(Equal(protocol.ByteCount(200)))
   176  			Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(200)))
   177  		})
   178  
   179  		Context("generating window updates", func() {
   180  			var oldWindowSize protocol.ByteCount
   181  
   182  			// update the congestion such that it returns a given value for the smoothed RTT
   183  			setRtt := func(t time.Duration) {
   184  				controller.rttStats.UpdateRTT(t, 0, time.Now())
   185  				Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked
   186  			}
   187  
   188  			BeforeEach(func() {
   189  				controller.receiveWindow = 100
   190  				controller.receiveWindowSize = 60
   191  				controller.bytesRead = 100 - 60
   192  				controller.connection.(*connectionFlowController).receiveWindow = 100
   193  				controller.connection.(*connectionFlowController).receiveWindowSize = 120
   194  				oldWindowSize = controller.receiveWindowSize
   195  			})
   196  
   197  			It("queues window updates", func() {
   198  				controller.AddBytesRead(1)
   199  				Expect(queuedWindowUpdate).To(BeFalse())
   200  				controller.AddBytesRead(29)
   201  				Expect(queuedWindowUpdate).To(BeTrue())
   202  				Expect(controller.GetWindowUpdate()).ToNot(BeZero())
   203  				queuedWindowUpdate = false
   204  				controller.AddBytesRead(1)
   205  				Expect(queuedWindowUpdate).To(BeFalse())
   206  			})
   207  
   208  			It("tells the connection flow controller when the window was auto-tuned", func() {
   209  				var allowed protocol.ByteCount
   210  				controller.connection.(*connectionFlowController).allowWindowIncrease = func(size protocol.ByteCount) bool {
   211  					allowed = size
   212  					return true
   213  				}
   214  				oldOffset := controller.bytesRead
   215  				setRtt(scaleDuration(20 * time.Millisecond))
   216  				controller.epochStartOffset = oldOffset
   217  				controller.epochStartTime = time.Now().Add(-time.Millisecond)
   218  				controller.AddBytesRead(55)
   219  				offset := controller.GetWindowUpdate()
   220  				Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize))
   221  				Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize))
   222  				Expect(allowed).To(Equal(oldWindowSize))
   223  				Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(protocol.ByteCount(float64(controller.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier)))
   224  			})
   225  
   226  			It("doesn't increase the connection flow control window if it's not allowed", func() {
   227  				oldOffset := controller.bytesRead
   228  				oldConnectionSize := controller.connection.(*connectionFlowController).receiveWindowSize
   229  				controller.connection.(*connectionFlowController).allowWindowIncrease = func(protocol.ByteCount) bool { return false }
   230  				setRtt(scaleDuration(20 * time.Millisecond))
   231  				controller.epochStartOffset = oldOffset
   232  				controller.epochStartTime = time.Now().Add(-time.Millisecond)
   233  				controller.AddBytesRead(55)
   234  				offset := controller.GetWindowUpdate()
   235  				Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize))
   236  				Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize))
   237  				Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(oldConnectionSize))
   238  			})
   239  
   240  			It("sends a connection-level window update when a large stream is abandoned", func() {
   241  				Expect(controller.UpdateHighestReceived(90, true)).To(Succeed())
   242  				Expect(controller.connection.GetWindowUpdate()).To(BeZero())
   243  				controller.Abandon()
   244  				Expect(controller.connection.GetWindowUpdate()).ToNot(BeZero())
   245  			})
   246  
   247  			It("doesn't increase the window after a final offset was already received", func() {
   248  				Expect(controller.UpdateHighestReceived(90, true)).To(Succeed())
   249  				controller.AddBytesRead(30)
   250  				Expect(queuedWindowUpdate).To(BeFalse())
   251  				offset := controller.GetWindowUpdate()
   252  				Expect(offset).To(BeZero())
   253  			})
   254  		})
   255  	})
   256  
   257  	Context("sending data", func() {
   258  		It("gets the size of the send window", func() {
   259  			controller.connection.UpdateSendWindow(1000)
   260  			controller.UpdateSendWindow(15)
   261  			controller.AddBytesSent(5)
   262  			Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(10)))
   263  		})
   264  
   265  		It("makes sure that it doesn't overflow the connection-level window", func() {
   266  			controller.connection.UpdateSendWindow(12)
   267  			controller.UpdateSendWindow(20)
   268  			controller.AddBytesSent(10)
   269  			Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(2)))
   270  		})
   271  
   272  		It("doesn't say that it's blocked, if only the connection is blocked", func() {
   273  			controller.connection.UpdateSendWindow(50)
   274  			controller.UpdateSendWindow(100)
   275  			controller.AddBytesSent(50)
   276  			blocked, _ := controller.connection.IsNewlyBlocked()
   277  			Expect(blocked).To(BeTrue())
   278  			Expect(controller.IsNewlyBlocked()).To(BeFalse())
   279  		})
   280  	})
   281  })