github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/internal/flowcontrol/stream_flow_controller.go (about) 1 package flowcontrol 2 3 import ( 4 "fmt" 5 6 "github.com/metacubex/quic-go/internal/protocol" 7 "github.com/metacubex/quic-go/internal/qerr" 8 "github.com/metacubex/quic-go/internal/utils" 9 ) 10 11 type streamFlowController struct { 12 baseFlowController 13 14 streamID protocol.StreamID 15 16 queueWindowUpdate func() 17 18 connection connectionFlowControllerI 19 20 receivedFinalOffset bool 21 } 22 23 var _ StreamFlowController = &streamFlowController{} 24 25 // NewStreamFlowController gets a new flow controller for a stream 26 func NewStreamFlowController( 27 streamID protocol.StreamID, 28 cfc ConnectionFlowController, 29 receiveWindow protocol.ByteCount, 30 maxReceiveWindow protocol.ByteCount, 31 initialSendWindow protocol.ByteCount, 32 queueWindowUpdate func(protocol.StreamID), 33 rttStats *utils.RTTStats, 34 logger utils.Logger, 35 ) StreamFlowController { 36 return &streamFlowController{ 37 streamID: streamID, 38 connection: cfc.(connectionFlowControllerI), 39 queueWindowUpdate: func() { queueWindowUpdate(streamID) }, 40 baseFlowController: baseFlowController{ 41 rttStats: rttStats, 42 receiveWindow: receiveWindow, 43 receiveWindowSize: receiveWindow, 44 maxReceiveWindowSize: maxReceiveWindow, 45 sendWindow: initialSendWindow, 46 logger: logger, 47 }, 48 } 49 } 50 51 // UpdateHighestReceived updates the highestReceived value, if the offset is higher. 52 func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error { 53 // If the final offset for this stream is already known, check for consistency. 54 if c.receivedFinalOffset { 55 // If we receive another final offset, check that it's the same. 56 if final && offset != c.highestReceived { 57 return &qerr.TransportError{ 58 ErrorCode: qerr.FinalSizeError, 59 ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset), 60 } 61 } 62 // Check that the offset is below the final offset. 63 if offset > c.highestReceived { 64 return &qerr.TransportError{ 65 ErrorCode: qerr.FinalSizeError, 66 ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived), 67 } 68 } 69 } 70 71 if final { 72 c.receivedFinalOffset = true 73 } 74 if offset == c.highestReceived { 75 return nil 76 } 77 // A higher offset was received before. 78 // This can happen due to reordering. 79 if offset <= c.highestReceived { 80 if final { 81 return &qerr.TransportError{ 82 ErrorCode: qerr.FinalSizeError, 83 ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived), 84 } 85 } 86 return nil 87 } 88 89 increment := offset - c.highestReceived 90 c.highestReceived = offset 91 if c.checkFlowControlViolation() { 92 return &qerr.TransportError{ 93 ErrorCode: qerr.FlowControlError, 94 ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow), 95 } 96 } 97 return c.connection.IncrementHighestReceived(increment) 98 } 99 100 func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) { 101 c.mutex.Lock() 102 c.baseFlowController.addBytesRead(n) 103 shouldQueueWindowUpdate := c.shouldQueueWindowUpdate() 104 c.mutex.Unlock() 105 if shouldQueueWindowUpdate { 106 c.queueWindowUpdate() 107 } 108 c.connection.AddBytesRead(n) 109 } 110 111 func (c *streamFlowController) Abandon() { 112 c.mutex.Lock() 113 unread := c.highestReceived - c.bytesRead 114 c.bytesRead = c.highestReceived 115 c.mutex.Unlock() 116 if unread > 0 { 117 c.connection.AddBytesRead(unread) 118 } 119 } 120 121 func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) { 122 c.baseFlowController.AddBytesSent(n) 123 c.connection.AddBytesSent(n) 124 } 125 126 func (c *streamFlowController) SendWindowSize() protocol.ByteCount { 127 return utils.Min(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize()) 128 } 129 130 func (c *streamFlowController) shouldQueueWindowUpdate() bool { 131 return !c.receivedFinalOffset && c.hasWindowUpdate() 132 } 133 134 func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount { 135 // If we already received the final offset for this stream, the peer won't need any additional flow control credit. 136 if c.receivedFinalOffset { 137 return 0 138 } 139 140 // Don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler 141 c.mutex.Lock() 142 oldWindowSize := c.receiveWindowSize 143 offset := c.baseFlowController.getWindowUpdate() 144 if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size 145 c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10)) 146 c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier)) 147 } 148 c.mutex.Unlock() 149 return offset 150 }