github.com/danielpfeifer02/quic-go-prio-packs@v0.41.0-28/internal/flowcontrol/stream_flow_controller_test.go (about) 1 package flowcontrol 2 3 import ( 4 "time" 5 6 "github.com/danielpfeifer02/quic-go-prio-packs/internal/protocol" 7 "github.com/danielpfeifer02/quic-go-prio-packs/internal/qerr" 8 "github.com/danielpfeifer02/quic-go-prio-packs/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 164 It("saves when data is read", func() { 165 controller.AddBytesRead(200) 166 Expect(controller.bytesRead).To(Equal(protocol.ByteCount(200))) 167 Expect(controller.connection.(*connectionFlowController).bytesRead).To(Equal(protocol.ByteCount(200))) 168 }) 169 170 Context("generating window updates", func() { 171 var oldWindowSize protocol.ByteCount 172 173 // update the congestion such that it returns a given value for the smoothed RTT 174 setRtt := func(t time.Duration) { 175 controller.rttStats.UpdateRTT(t, 0, time.Now()) 176 Expect(controller.rttStats.SmoothedRTT()).To(Equal(t)) // make sure it worked 177 } 178 179 BeforeEach(func() { 180 controller.receiveWindow = 100 181 controller.receiveWindowSize = 60 182 controller.bytesRead = 100 - 60 183 controller.connection.(*connectionFlowController).receiveWindow = 100 184 controller.connection.(*connectionFlowController).receiveWindowSize = 120 185 oldWindowSize = controller.receiveWindowSize 186 }) 187 188 It("queues window updates", func() { 189 controller.AddBytesRead(1) 190 Expect(queuedWindowUpdate).To(BeFalse()) 191 controller.AddBytesRead(29) 192 Expect(queuedWindowUpdate).To(BeTrue()) 193 Expect(controller.GetWindowUpdate()).ToNot(BeZero()) 194 queuedWindowUpdate = false 195 controller.AddBytesRead(1) 196 Expect(queuedWindowUpdate).To(BeFalse()) 197 }) 198 199 It("tells the connection flow controller when the window was auto-tuned", func() { 200 var allowed protocol.ByteCount 201 controller.connection.(*connectionFlowController).allowWindowIncrease = func(size protocol.ByteCount) bool { 202 allowed = size 203 return true 204 } 205 oldOffset := controller.bytesRead 206 setRtt(scaleDuration(20 * time.Millisecond)) 207 controller.epochStartOffset = oldOffset 208 controller.epochStartTime = time.Now().Add(-time.Millisecond) 209 controller.AddBytesRead(55) 210 offset := controller.GetWindowUpdate() 211 Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize)) 212 Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) 213 Expect(allowed).To(Equal(oldWindowSize)) 214 Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(protocol.ByteCount(float64(controller.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))) 215 }) 216 217 It("doesn't increase the connection flow control window if it's not allowed", func() { 218 oldOffset := controller.bytesRead 219 oldConnectionSize := controller.connection.(*connectionFlowController).receiveWindowSize 220 controller.connection.(*connectionFlowController).allowWindowIncrease = func(protocol.ByteCount) bool { return false } 221 setRtt(scaleDuration(20 * time.Millisecond)) 222 controller.epochStartOffset = oldOffset 223 controller.epochStartTime = time.Now().Add(-time.Millisecond) 224 controller.AddBytesRead(55) 225 offset := controller.GetWindowUpdate() 226 Expect(offset).To(Equal(oldOffset + 55 + 2*oldWindowSize)) 227 Expect(controller.receiveWindowSize).To(Equal(2 * oldWindowSize)) 228 Expect(controller.connection.(*connectionFlowController).receiveWindowSize).To(Equal(oldConnectionSize)) 229 }) 230 231 It("sends a connection-level window update when a large stream is abandoned", func() { 232 Expect(controller.UpdateHighestReceived(90, true)).To(Succeed()) 233 Expect(controller.connection.GetWindowUpdate()).To(BeZero()) 234 controller.Abandon() 235 Expect(controller.connection.GetWindowUpdate()).ToNot(BeZero()) 236 }) 237 238 It("doesn't increase the window after a final offset was already received", func() { 239 Expect(controller.UpdateHighestReceived(90, true)).To(Succeed()) 240 controller.AddBytesRead(30) 241 Expect(queuedWindowUpdate).To(BeFalse()) 242 offset := controller.GetWindowUpdate() 243 Expect(offset).To(BeZero()) 244 }) 245 }) 246 }) 247 248 Context("sending data", func() { 249 It("gets the size of the send window", func() { 250 controller.connection.UpdateSendWindow(1000) 251 controller.UpdateSendWindow(15) 252 controller.AddBytesSent(5) 253 Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(10))) 254 }) 255 256 It("makes sure that it doesn't overflow the connection-level window", func() { 257 controller.connection.UpdateSendWindow(12) 258 controller.UpdateSendWindow(20) 259 controller.AddBytesSent(10) 260 Expect(controller.SendWindowSize()).To(Equal(protocol.ByteCount(2))) 261 }) 262 263 It("doesn't say that it's blocked, if only the connection is blocked", func() { 264 controller.connection.UpdateSendWindow(50) 265 controller.UpdateSendWindow(100) 266 controller.AddBytesSent(50) 267 blocked, _ := controller.connection.IsNewlyBlocked() 268 Expect(blocked).To(BeTrue()) 269 Expect(controller.IsNewlyBlocked()).To(BeFalse()) 270 }) 271 }) 272 })