package flowcontrol
import (
"fmt"
"github.com/quic-go/quic-go/internal/monotime"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
"github.com/quic-go/quic-go/internal/utils"
)
type streamFlowController struct {
baseFlowController
streamID protocol .StreamID
connection connectionFlowControllerI
receivedFinalOffset bool
}
var _ StreamFlowController = &streamFlowController {}
func NewStreamFlowController (
streamID protocol .StreamID ,
cfc ConnectionFlowController ,
receiveWindow protocol .ByteCount ,
maxReceiveWindow protocol .ByteCount ,
initialSendWindow protocol .ByteCount ,
rttStats *utils .RTTStats ,
logger utils .Logger ,
) StreamFlowController {
return &streamFlowController {
streamID : streamID ,
connection : cfc .(connectionFlowControllerI ),
baseFlowController : baseFlowController {
rttStats : rttStats ,
receiveWindow : receiveWindow ,
receiveWindowSize : receiveWindow ,
maxReceiveWindowSize : maxReceiveWindow ,
sendWindow : initialSendWindow ,
logger : logger ,
},
}
}
func (c *streamFlowController ) UpdateHighestReceived (offset protocol .ByteCount , final bool , now monotime .Time ) error {
if c .receivedFinalOffset {
if final && offset != c .highestReceived {
return &qerr .TransportError {
ErrorCode : qerr .FinalSizeError ,
ErrorMessage : fmt .Sprintf ("received inconsistent final offset for stream %d (old: %d, new: %d bytes)" , c .streamID , c .highestReceived , offset ),
}
}
if offset > c .highestReceived {
return &qerr .TransportError {
ErrorCode : qerr .FinalSizeError ,
ErrorMessage : fmt .Sprintf ("received offset %d for stream %d, but final offset was already received at %d" , offset , c .streamID , c .highestReceived ),
}
}
}
if final {
c .receivedFinalOffset = true
}
if offset == c .highestReceived {
return nil
}
if offset < c .highestReceived {
if final {
return &qerr .TransportError {
ErrorCode : qerr .FinalSizeError ,
ErrorMessage : fmt .Sprintf ("received final offset %d for stream %d, but already received offset %d before" , offset , c .streamID , c .highestReceived ),
}
}
return nil
}
if c .highestReceived == 0 {
c .startNewAutoTuningEpoch (now )
}
increment := offset - c .highestReceived
c .highestReceived = offset
if c .checkFlowControlViolation () {
return &qerr .TransportError {
ErrorCode : qerr .FlowControlError ,
ErrorMessage : fmt .Sprintf ("received %d bytes on stream %d, allowed %d bytes" , offset , c .streamID , c .receiveWindow ),
}
}
return c .connection .IncrementHighestReceived (increment , now )
}
func (c *streamFlowController ) AddBytesRead (n protocol .ByteCount ) (hasStreamWindowUpdate , hasConnWindowUpdate bool ) {
c .mutex .Lock ()
c .addBytesRead (n )
hasStreamWindowUpdate = c .shouldQueueWindowUpdate ()
c .mutex .Unlock ()
hasConnWindowUpdate = c .connection .AddBytesRead (n )
return
}
func (c *streamFlowController ) Abandon () {
c .mutex .Lock ()
unread := c .highestReceived - c .bytesRead
c .bytesRead = c .highestReceived
c .mutex .Unlock ()
if unread > 0 {
c .connection .AddBytesRead (unread )
}
}
func (c *streamFlowController ) AddBytesSent (n protocol .ByteCount ) {
c .baseFlowController .AddBytesSent (n )
c .connection .AddBytesSent (n )
}
func (c *streamFlowController ) SendWindowSize () protocol .ByteCount {
return min (c .baseFlowController .SendWindowSize (), c .connection .SendWindowSize ())
}
func (c *streamFlowController ) IsNewlyBlocked () bool {
blocked , _ := c .baseFlowController .IsNewlyBlocked ()
return blocked
}
func (c *streamFlowController ) shouldQueueWindowUpdate () bool {
return !c .receivedFinalOffset && c .hasWindowUpdate ()
}
func (c *streamFlowController ) GetWindowUpdate (now monotime .Time ) protocol .ByteCount {
if c .receivedFinalOffset {
return 0
}
c .mutex .Lock ()
defer c .mutex .Unlock ()
oldWindowSize := c .receiveWindowSize
offset := c .getWindowUpdate (now )
if c .receiveWindowSize > oldWindowSize {
c .logger .Debugf ("Increasing receive flow control window for stream %d to %d" , c .streamID , c .receiveWindowSize )
c .connection .EnsureMinimumWindowSize (protocol .ByteCount (float64 (c .receiveWindowSize )*protocol .ConnectionFlowControlMultiplier ), now )
}
return offset
}
The pages are generated with Golds v0.8.4 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .