package quic
import (
"slices"
"sync"
"github.com/quic-go/quic-go/internal/ackhandler"
"github.com/quic-go/quic-go/internal/flowcontrol"
"github.com/quic-go/quic-go/internal/monotime"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/utils/ringbuffer"
"github.com/quic-go/quic-go/internal/wire"
"github.com/quic-go/quic-go/quicvarint"
)
const (
maxPathResponses = 256
maxControlFrames = 16 << 10
)
const maxStreamControlFrameSize = 25
type streamFrameGetter interface {
popStreamFrame(protocol .ByteCount , protocol .Version ) (ackhandler .StreamFrame , *wire .StreamDataBlockedFrame , bool )
}
type streamControlFrameGetter interface {
getControlFrame(monotime .Time ) (_ ackhandler .Frame , ok, hasMore bool )
}
type framer struct {
mutex sync .Mutex
activeStreams map [protocol .StreamID ]streamFrameGetter
streamQueue ringbuffer .RingBuffer [protocol .StreamID ]
streamsWithControlFrames map [protocol .StreamID ]streamControlFrameGetter
controlFrameMutex sync .Mutex
controlFrames []wire .Frame
pathResponses []*wire .PathResponseFrame
connFlowController flowcontrol .ConnectionFlowController
queuedTooManyControlFrames bool
}
func newFramer(connFlowController flowcontrol .ConnectionFlowController ) *framer {
return &framer {
activeStreams : make (map [protocol .StreamID ]streamFrameGetter ),
streamsWithControlFrames : make (map [protocol .StreamID ]streamControlFrameGetter ),
connFlowController : connFlowController ,
}
}
func (f *framer ) HasData () bool {
f .mutex .Lock ()
hasData := !f .streamQueue .Empty ()
f .mutex .Unlock ()
if hasData {
return true
}
f .controlFrameMutex .Lock ()
defer f .controlFrameMutex .Unlock ()
return len (f .streamsWithControlFrames ) > 0 || len (f .controlFrames ) > 0 || len (f .pathResponses ) > 0
}
func (f *framer ) QueueControlFrame (frame wire .Frame ) {
f .controlFrameMutex .Lock ()
defer f .controlFrameMutex .Unlock ()
if pr , ok := frame .(*wire .PathResponseFrame ); ok {
if len (f .pathResponses ) >= maxPathResponses {
return
}
f .pathResponses = append (f .pathResponses , pr )
return
}
if len (f .controlFrames ) >= maxControlFrames {
f .queuedTooManyControlFrames = true
return
}
f .controlFrames = append (f .controlFrames , frame )
}
func (f *framer ) Append (
frames []ackhandler .Frame ,
streamFrames []ackhandler .StreamFrame ,
maxLen protocol .ByteCount ,
now monotime .Time ,
v protocol .Version ,
) ([]ackhandler .Frame , []ackhandler .StreamFrame , protocol .ByteCount ) {
f .controlFrameMutex .Lock ()
frames , controlFrameLen := f .appendControlFrames (frames , maxLen , now , v )
maxLen -= controlFrameLen
var lastFrame ackhandler .StreamFrame
var streamFrameLen protocol .ByteCount
f .mutex .Lock ()
numActiveStreams := f .streamQueue .Len ()
for i := 0 ; i < numActiveStreams ; i ++ {
if protocol .MinStreamFrameSize > maxLen {
break
}
sf , blocked := f .getNextStreamFrame (maxLen , v )
if sf .Frame != nil {
streamFrames = append (streamFrames , sf )
maxLen -= sf .Frame .Length (v )
lastFrame = sf
streamFrameLen += sf .Frame .Length (v )
}
if blocked != nil {
l := blocked .Length (v )
if maxLen < l {
f .controlFrames = append (f .controlFrames , blocked )
break
}
frames = append (frames , ackhandler .Frame {Frame : blocked })
maxLen -= l
controlFrameLen += l
}
}
if isBlocked , offset := f .connFlowController .IsNewlyBlocked (); isBlocked {
blocked := &wire .DataBlockedFrame {MaximumData : offset }
l := blocked .Length (v )
if maxLen >= l {
frames = append (frames , ackhandler .Frame {Frame : blocked })
controlFrameLen += l
} else {
f .controlFrames = append (f .controlFrames , blocked )
}
}
f .mutex .Unlock ()
f .controlFrameMutex .Unlock ()
if lastFrame .Frame != nil {
streamFrameLen -= lastFrame .Frame .Length (v )
lastFrame .Frame .DataLenPresent = false
streamFrameLen += lastFrame .Frame .Length (v )
}
return frames , streamFrames , controlFrameLen + streamFrameLen
}
func (f *framer ) appendControlFrames (
frames []ackhandler .Frame ,
maxLen protocol .ByteCount ,
now monotime .Time ,
v protocol .Version ,
) ([]ackhandler .Frame , protocol .ByteCount ) {
var length protocol .ByteCount
if len (f .pathResponses ) > 0 {
frame := f .pathResponses [0 ]
frameLen := frame .Length (v )
if frameLen <= maxLen {
frames = append (frames , ackhandler .Frame {Frame : frame })
length += frameLen
f .pathResponses = f .pathResponses [1 :]
}
}
for id , str := range f .streamsWithControlFrames {
start :
remainingLen := maxLen - length
if remainingLen <= maxStreamControlFrameSize {
break
}
fr , ok , hasMore := str .getControlFrame (now )
if !hasMore {
delete (f .streamsWithControlFrames , id )
}
if !ok {
continue
}
frames = append (frames , fr )
length += fr .Frame .Length (v )
if hasMore {
goto start
}
}
for len (f .controlFrames ) > 0 {
frame := f .controlFrames [len (f .controlFrames )-1 ]
frameLen := frame .Length (v )
if length +frameLen > maxLen {
break
}
frames = append (frames , ackhandler .Frame {Frame : frame })
length += frameLen
f .controlFrames = f .controlFrames [:len (f .controlFrames )-1 ]
}
return frames , length
}
func (f *framer ) QueuedTooManyControlFrames () bool {
return f .queuedTooManyControlFrames
}
func (f *framer ) AddActiveStream (id protocol .StreamID , str streamFrameGetter ) {
f .mutex .Lock ()
if _ , ok := f .activeStreams [id ]; !ok {
f .streamQueue .PushBack (id )
f .activeStreams [id ] = str
}
f .mutex .Unlock ()
}
func (f *framer ) AddStreamWithControlFrames (id protocol .StreamID , str streamControlFrameGetter ) {
f .controlFrameMutex .Lock ()
if _ , ok := f .streamsWithControlFrames [id ]; !ok {
f .streamsWithControlFrames [id ] = str
}
f .controlFrameMutex .Unlock ()
}
func (f *framer ) RemoveActiveStream (id protocol .StreamID ) {
f .mutex .Lock ()
delete (f .activeStreams , id )
f .mutex .Unlock ()
}
func (f *framer ) getNextStreamFrame (maxLen protocol .ByteCount , v protocol .Version ) (ackhandler .StreamFrame , *wire .StreamDataBlockedFrame ) {
id := f .streamQueue .PopFront ()
str , ok := f .activeStreams [id ]
if !ok {
return ackhandler .StreamFrame {}, nil
}
maxLen += protocol .ByteCount (quicvarint .Len (uint64 (maxLen )))
frame , blocked , hasMoreData := str .popStreamFrame (maxLen , v )
if hasMoreData {
f .streamQueue .PushBack (id )
} else {
delete (f .activeStreams , id )
}
return frame , blocked
}
func (f *framer ) Handle0RTTRejection () {
f .mutex .Lock ()
defer f .mutex .Unlock ()
f .controlFrameMutex .Lock ()
defer f .controlFrameMutex .Unlock ()
f .streamQueue .Clear ()
for id := range f .activeStreams {
delete (f .activeStreams , id )
}
var j int
for i , frame := range f .controlFrames {
switch frame .(type ) {
case *wire .MaxDataFrame , *wire .MaxStreamDataFrame , *wire .MaxStreamsFrame ,
*wire .DataBlockedFrame , *wire .StreamDataBlockedFrame , *wire .StreamsBlockedFrame :
continue
default :
f .controlFrames [j ] = f .controlFrames [i ]
j ++
}
}
f .controlFrames = slices .Delete (f .controlFrames , j , len (f .controlFrames ))
}
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 .