github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/internal/flowcontrol/stream_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/qerr" 8 "github.com/apernet/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 })