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

     1  package flowcontrol
     2  
     3  import (
     4  	"fmt"
     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  
    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 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  }