mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-03 20:27:35 +03:00
By introducing a callback to the stream, which the stream calls as soon as it is completed, we can get rid of checking every single open stream if it is completed.
274 lines
6.8 KiB
Go
274 lines
6.8 KiB
Go
package quic
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/lucas-clemente/quic-go/internal/flowcontrol"
|
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
|
"github.com/lucas-clemente/quic-go/internal/utils"
|
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
|
)
|
|
|
|
type receiveStream struct {
|
|
mutex sync.Mutex
|
|
|
|
streamID protocol.StreamID
|
|
|
|
sender streamSender
|
|
|
|
frameQueue *streamFrameSorter
|
|
readPosInFrame int
|
|
readOffset protocol.ByteCount
|
|
|
|
closeForShutdownErr error
|
|
cancelReadErr error
|
|
resetRemotelyErr StreamError
|
|
|
|
closedForShutdown bool // set when CloseForShutdown() is called
|
|
finRead bool // set once we read a frame with a FinBit
|
|
canceledRead bool // set when CancelRead() is called
|
|
resetRemotely bool // set when HandleRstStreamFrame() is called
|
|
|
|
readChan chan struct{}
|
|
readDeadline time.Time
|
|
|
|
flowController flowcontrol.StreamFlowController
|
|
version protocol.VersionNumber
|
|
}
|
|
|
|
var _ ReceiveStream = &receiveStream{}
|
|
|
|
func newReceiveStream(
|
|
streamID protocol.StreamID,
|
|
sender streamSender,
|
|
flowController flowcontrol.StreamFlowController,
|
|
) *receiveStream {
|
|
return &receiveStream{
|
|
streamID: streamID,
|
|
sender: sender,
|
|
flowController: flowController,
|
|
frameQueue: newStreamFrameSorter(),
|
|
readChan: make(chan struct{}, 1),
|
|
}
|
|
}
|
|
|
|
func (s *receiveStream) StreamID() protocol.StreamID {
|
|
return s.streamID
|
|
}
|
|
|
|
// Read implements io.Reader. It is not thread safe!
|
|
func (s *receiveStream) Read(p []byte) (int, error) {
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
|
|
if s.finRead {
|
|
return 0, io.EOF
|
|
}
|
|
if s.canceledRead {
|
|
return 0, s.cancelReadErr
|
|
}
|
|
if s.resetRemotely {
|
|
return 0, s.resetRemotelyErr
|
|
}
|
|
if s.closedForShutdown {
|
|
return 0, s.closeForShutdownErr
|
|
}
|
|
|
|
bytesRead := 0
|
|
for bytesRead < len(p) {
|
|
frame := s.frameQueue.Head()
|
|
if frame == nil && bytesRead > 0 {
|
|
return bytesRead, s.closeForShutdownErr
|
|
}
|
|
|
|
for {
|
|
// Stop waiting on errors
|
|
if s.closedForShutdown {
|
|
return bytesRead, s.closeForShutdownErr
|
|
}
|
|
if s.canceledRead {
|
|
return bytesRead, s.cancelReadErr
|
|
}
|
|
if s.resetRemotely {
|
|
return bytesRead, s.resetRemotelyErr
|
|
}
|
|
|
|
deadline := s.readDeadline
|
|
if !deadline.IsZero() && !time.Now().Before(deadline) {
|
|
return bytesRead, errDeadline
|
|
}
|
|
|
|
if frame != nil {
|
|
s.readPosInFrame = int(s.readOffset - frame.Offset)
|
|
break
|
|
}
|
|
|
|
s.mutex.Unlock()
|
|
if deadline.IsZero() {
|
|
<-s.readChan
|
|
} else {
|
|
select {
|
|
case <-s.readChan:
|
|
case <-time.After(deadline.Sub(time.Now())):
|
|
}
|
|
}
|
|
s.mutex.Lock()
|
|
frame = s.frameQueue.Head()
|
|
}
|
|
|
|
if bytesRead > len(p) {
|
|
return bytesRead, fmt.Errorf("BUG: bytesRead (%d) > len(p) (%d) in stream.Read", bytesRead, len(p))
|
|
}
|
|
if s.readPosInFrame > int(frame.DataLen()) {
|
|
return bytesRead, fmt.Errorf("BUG: readPosInFrame (%d) > frame.DataLen (%d) in stream.Read", s.readPosInFrame, frame.DataLen())
|
|
}
|
|
|
|
s.mutex.Unlock()
|
|
|
|
copy(p[bytesRead:], frame.Data[s.readPosInFrame:])
|
|
m := utils.Min(len(p)-bytesRead, int(frame.DataLen())-s.readPosInFrame)
|
|
s.readPosInFrame += m
|
|
bytesRead += m
|
|
s.readOffset += protocol.ByteCount(m)
|
|
|
|
s.mutex.Lock()
|
|
// when a RST_STREAM was received, the was already informed about the final byteOffset for this stream
|
|
if !s.resetRemotely {
|
|
s.flowController.AddBytesRead(protocol.ByteCount(m))
|
|
}
|
|
// this call triggers the flow controller to increase the flow control window, if necessary
|
|
if s.flowController.HasWindowUpdate() {
|
|
s.sender.onHasWindowUpdate(s.streamID)
|
|
}
|
|
|
|
if s.readPosInFrame >= int(frame.DataLen()) {
|
|
s.frameQueue.Pop()
|
|
s.finRead = frame.FinBit
|
|
if frame.FinBit {
|
|
s.sender.onStreamCompleted(s.streamID)
|
|
return bytesRead, io.EOF
|
|
}
|
|
}
|
|
}
|
|
return bytesRead, nil
|
|
}
|
|
|
|
func (s *receiveStream) CancelRead(errorCode protocol.ApplicationErrorCode) error {
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
|
|
if s.finRead {
|
|
return nil
|
|
}
|
|
if s.canceledRead {
|
|
return nil
|
|
}
|
|
s.canceledRead = true
|
|
s.cancelReadErr = fmt.Errorf("Read on stream %d canceled with error code %d", s.streamID, errorCode)
|
|
s.signalRead()
|
|
if s.version.UsesIETFFrameFormat() {
|
|
s.sender.queueControlFrame(&wire.StopSendingFrame{
|
|
StreamID: s.streamID,
|
|
ErrorCode: errorCode,
|
|
})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *receiveStream) handleStreamFrame(frame *wire.StreamFrame) error {
|
|
maxOffset := frame.Offset + frame.DataLen()
|
|
if err := s.flowController.UpdateHighestReceived(maxOffset, frame.FinBit); err != nil {
|
|
return err
|
|
}
|
|
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
if err := s.frameQueue.Push(frame); err != nil && err != errDuplicateStreamData {
|
|
return err
|
|
}
|
|
s.signalRead()
|
|
return nil
|
|
}
|
|
|
|
func (s *receiveStream) handleRstStreamFrame(frame *wire.RstStreamFrame) error {
|
|
s.mutex.Lock()
|
|
defer s.mutex.Unlock()
|
|
|
|
if s.closedForShutdown {
|
|
return nil
|
|
}
|
|
if err := s.flowController.UpdateHighestReceived(frame.ByteOffset, true); err != nil {
|
|
return err
|
|
}
|
|
// In gQUIC, error code 0 has a special meaning.
|
|
// The peer will reliably continue transmitting, but is not interested in reading from the stream.
|
|
// We should therefore just continue reading from the stream, until we encounter the FIN bit.
|
|
if !s.version.UsesIETFFrameFormat() && frame.ErrorCode == 0 {
|
|
return nil
|
|
}
|
|
|
|
// ignore duplicate RST_STREAM frames for this stream (after checking their final offset)
|
|
if s.resetRemotely {
|
|
return nil
|
|
}
|
|
s.resetRemotely = true
|
|
s.resetRemotelyErr = streamCanceledError{
|
|
errorCode: frame.ErrorCode,
|
|
error: fmt.Errorf("Stream %d was reset with error code %d", s.streamID, frame.ErrorCode),
|
|
}
|
|
s.signalRead()
|
|
s.sender.onStreamCompleted(s.streamID)
|
|
return nil
|
|
}
|
|
|
|
func (s *receiveStream) CloseRemote(offset protocol.ByteCount) {
|
|
s.handleStreamFrame(&wire.StreamFrame{FinBit: true, Offset: offset})
|
|
}
|
|
|
|
func (s *receiveStream) onClose(offset protocol.ByteCount) {
|
|
if s.canceledRead && !s.version.UsesIETFFrameFormat() {
|
|
s.sender.queueControlFrame(&wire.RstStreamFrame{
|
|
StreamID: s.streamID,
|
|
ByteOffset: offset,
|
|
ErrorCode: 0,
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *receiveStream) SetReadDeadline(t time.Time) error {
|
|
s.mutex.Lock()
|
|
oldDeadline := s.readDeadline
|
|
s.readDeadline = t
|
|
s.mutex.Unlock()
|
|
// if the new deadline is before the currently set deadline, wake up Read()
|
|
if t.Before(oldDeadline) {
|
|
s.signalRead()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// CloseForShutdown closes a stream abruptly.
|
|
// It makes Read unblock (and return the error) immediately.
|
|
// The peer will NOT be informed about this: the stream is closed without sending a FIN or RST.
|
|
func (s *receiveStream) closeForShutdown(err error) {
|
|
s.mutex.Lock()
|
|
s.closedForShutdown = true
|
|
s.closeForShutdownErr = err
|
|
s.mutex.Unlock()
|
|
s.signalRead()
|
|
}
|
|
|
|
func (s *receiveStream) getWindowUpdate() protocol.ByteCount {
|
|
return s.flowController.GetWindowUpdate()
|
|
}
|
|
|
|
// signalRead performs a non-blocking send on the readChan
|
|
func (s *receiveStream) signalRead() {
|
|
select {
|
|
case s.readChan <- struct{}{}:
|
|
default:
|
|
}
|
|
}
|