github.com/apernet/quic-go@v0.43.1-0.20240515053213-5e9e635fd9f0/framer.go (about)

     1  package quic
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  
     7  	"github.com/apernet/quic-go/internal/ackhandler"
     8  	"github.com/apernet/quic-go/internal/protocol"
     9  	"github.com/apernet/quic-go/internal/utils/ringbuffer"
    10  	"github.com/apernet/quic-go/internal/wire"
    11  	"github.com/apernet/quic-go/quicvarint"
    12  )
    13  
    14  type framer interface {
    15  	HasData() bool
    16  
    17  	QueueControlFrame(wire.Frame)
    18  	AppendControlFrames([]ackhandler.Frame, protocol.ByteCount, protocol.Version) ([]ackhandler.Frame, protocol.ByteCount)
    19  
    20  	AddActiveStream(protocol.StreamID)
    21  	AppendStreamFrames([]ackhandler.StreamFrame, protocol.ByteCount, protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount)
    22  
    23  	Handle0RTTRejection() error
    24  
    25  	// QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length.
    26  	// This is a hack.
    27  	// It is easier to implement than propagating an error return value in QueueControlFrame.
    28  	// The correct solution would be to queue frames with their respective structs.
    29  	// See https://github.com/apernet/quic-go/issues/4271 for the queueing of stream-related control frames.
    30  	QueuedTooManyControlFrames() bool
    31  }
    32  
    33  const (
    34  	maxPathResponses = 256
    35  	maxControlFrames = 16 << 10
    36  )
    37  
    38  type framerI struct {
    39  	mutex sync.Mutex
    40  
    41  	streamGetter streamGetter
    42  
    43  	activeStreams map[protocol.StreamID]struct{}
    44  	streamQueue   ringbuffer.RingBuffer[protocol.StreamID]
    45  
    46  	controlFrameMutex          sync.Mutex
    47  	controlFrames              []wire.Frame
    48  	pathResponses              []*wire.PathResponseFrame
    49  	queuedTooManyControlFrames bool
    50  }
    51  
    52  var _ framer = &framerI{}
    53  
    54  func newFramer(streamGetter streamGetter) framer {
    55  	return &framerI{
    56  		streamGetter:  streamGetter,
    57  		activeStreams: make(map[protocol.StreamID]struct{}),
    58  	}
    59  }
    60  
    61  func (f *framerI) HasData() bool {
    62  	f.mutex.Lock()
    63  	hasData := !f.streamQueue.Empty()
    64  	f.mutex.Unlock()
    65  	if hasData {
    66  		return true
    67  	}
    68  	f.controlFrameMutex.Lock()
    69  	defer f.controlFrameMutex.Unlock()
    70  	return len(f.controlFrames) > 0 || len(f.pathResponses) > 0
    71  }
    72  
    73  func (f *framerI) QueueControlFrame(frame wire.Frame) {
    74  	f.controlFrameMutex.Lock()
    75  	defer f.controlFrameMutex.Unlock()
    76  
    77  	if pr, ok := frame.(*wire.PathResponseFrame); ok {
    78  		// Only queue up to maxPathResponses PATH_RESPONSE frames.
    79  		// This limit should be high enough to never be hit in practice,
    80  		// unless the peer is doing something malicious.
    81  		if len(f.pathResponses) >= maxPathResponses {
    82  			return
    83  		}
    84  		f.pathResponses = append(f.pathResponses, pr)
    85  		return
    86  	}
    87  	// This is a hack.
    88  	if len(f.controlFrames) >= maxControlFrames {
    89  		f.queuedTooManyControlFrames = true
    90  		return
    91  	}
    92  	f.controlFrames = append(f.controlFrames, frame)
    93  }
    94  
    95  func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.Frame, protocol.ByteCount) {
    96  	f.controlFrameMutex.Lock()
    97  	defer f.controlFrameMutex.Unlock()
    98  
    99  	var length protocol.ByteCount
   100  	// add a PATH_RESPONSE first, but only pack a single PATH_RESPONSE per packet
   101  	if len(f.pathResponses) > 0 {
   102  		frame := f.pathResponses[0]
   103  		frameLen := frame.Length(v)
   104  		if frameLen <= maxLen {
   105  			frames = append(frames, ackhandler.Frame{Frame: frame})
   106  			length += frameLen
   107  			f.pathResponses = f.pathResponses[1:]
   108  		}
   109  	}
   110  
   111  	for len(f.controlFrames) > 0 {
   112  		frame := f.controlFrames[len(f.controlFrames)-1]
   113  		frameLen := frame.Length(v)
   114  		if length+frameLen > maxLen {
   115  			break
   116  		}
   117  		frames = append(frames, ackhandler.Frame{Frame: frame})
   118  		length += frameLen
   119  		f.controlFrames = f.controlFrames[:len(f.controlFrames)-1]
   120  	}
   121  	return frames, length
   122  }
   123  
   124  func (f *framerI) QueuedTooManyControlFrames() bool {
   125  	return f.queuedTooManyControlFrames
   126  }
   127  
   128  func (f *framerI) AddActiveStream(id protocol.StreamID) {
   129  	f.mutex.Lock()
   130  	if _, ok := f.activeStreams[id]; !ok {
   131  		f.streamQueue.PushBack(id)
   132  		f.activeStreams[id] = struct{}{}
   133  	}
   134  	f.mutex.Unlock()
   135  }
   136  
   137  func (f *framerI) AppendStreamFrames(frames []ackhandler.StreamFrame, maxLen protocol.ByteCount, v protocol.Version) ([]ackhandler.StreamFrame, protocol.ByteCount) {
   138  	startLen := len(frames)
   139  	var length protocol.ByteCount
   140  	f.mutex.Lock()
   141  	// pop STREAM frames, until less than MinStreamFrameSize bytes are left in the packet
   142  	numActiveStreams := f.streamQueue.Len()
   143  	for i := 0; i < numActiveStreams; i++ {
   144  		if protocol.MinStreamFrameSize+length > maxLen {
   145  			break
   146  		}
   147  		id := f.streamQueue.PopFront()
   148  		// This should never return an error. Better check it anyway.
   149  		// The stream will only be in the streamQueue, if it enqueued itself there.
   150  		str, err := f.streamGetter.GetOrOpenSendStream(id)
   151  		// The stream can be nil if it completed after it said it had data.
   152  		if str == nil || err != nil {
   153  			delete(f.activeStreams, id)
   154  			continue
   155  		}
   156  		remainingLen := maxLen - length
   157  		// For the last STREAM frame, we'll remove the DataLen field later.
   158  		// Therefore, we can pretend to have more bytes available when popping
   159  		// the STREAM frame (which will always have the DataLen set).
   160  		remainingLen += protocol.ByteCount(quicvarint.Len(uint64(remainingLen)))
   161  		frame, ok, hasMoreData := str.popStreamFrame(remainingLen, v)
   162  		if hasMoreData { // put the stream back in the queue (at the end)
   163  			f.streamQueue.PushBack(id)
   164  		} else { // no more data to send. Stream is not active
   165  			delete(f.activeStreams, id)
   166  		}
   167  		// The frame can be "nil"
   168  		// * if the receiveStream was canceled after it said it had data
   169  		// * the remaining size doesn't allow us to add another STREAM frame
   170  		if !ok {
   171  			continue
   172  		}
   173  		frames = append(frames, frame)
   174  		length += frame.Frame.Length(v)
   175  	}
   176  	f.mutex.Unlock()
   177  	if len(frames) > startLen {
   178  		l := frames[len(frames)-1].Frame.Length(v)
   179  		// account for the smaller size of the last STREAM frame
   180  		frames[len(frames)-1].Frame.DataLenPresent = false
   181  		length += frames[len(frames)-1].Frame.Length(v) - l
   182  	}
   183  	return frames, length
   184  }
   185  
   186  func (f *framerI) Handle0RTTRejection() error {
   187  	f.mutex.Lock()
   188  	defer f.mutex.Unlock()
   189  
   190  	f.controlFrameMutex.Lock()
   191  	f.streamQueue.Clear()
   192  	for id := range f.activeStreams {
   193  		delete(f.activeStreams, id)
   194  	}
   195  	var j int
   196  	for i, frame := range f.controlFrames {
   197  		switch frame.(type) {
   198  		case *wire.MaxDataFrame, *wire.MaxStreamDataFrame, *wire.MaxStreamsFrame:
   199  			return errors.New("didn't expect MAX_DATA / MAX_STREAM_DATA / MAX_STREAMS frame to be sent in 0-RTT")
   200  		case *wire.DataBlockedFrame, *wire.StreamDataBlockedFrame, *wire.StreamsBlockedFrame:
   201  			continue
   202  		default:
   203  			f.controlFrames[j] = f.controlFrames[i]
   204  			j++
   205  		}
   206  	}
   207  	f.controlFrames = f.controlFrames[:j]
   208  	f.controlFrameMutex.Unlock()
   209  	return nil
   210  }