package wire
import (
"errors"
"fmt"
"io"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
"github.com/quic-go/quic-go/quicvarint"
)
var errUnknownFrameType = errors .New ("unknown frame type" )
type FrameParser struct {
ackDelayExponent uint8
supportsDatagrams bool
supportsResetStreamAt bool
supportsAckFrequency bool
ackFrame *AckFrame
}
func NewFrameParser (supportsDatagrams , supportsResetStreamAt , supportsAckFrequency bool ) *FrameParser {
return &FrameParser {
supportsDatagrams : supportsDatagrams ,
supportsResetStreamAt : supportsResetStreamAt ,
supportsAckFrequency : supportsAckFrequency ,
ackFrame : &AckFrame {},
}
}
func (p *FrameParser ) ParseType (b []byte , encLevel protocol .EncryptionLevel ) (FrameType , int , error ) {
var parsed int
for len (b ) != 0 {
typ , l , err := quicvarint .Parse (b )
parsed += l
if err != nil {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
ErrorMessage : err .Error(),
}
}
b = b [l :]
if typ == 0x0 {
continue
}
ft := FrameType (typ )
valid := ft .isValidRFC9000 () ||
(p .supportsDatagrams && ft .IsDatagramFrameType ()) ||
(p .supportsResetStreamAt && ft == FrameTypeResetStreamAt ) ||
(p .supportsAckFrequency && (ft == FrameTypeAckFrequency || ft == FrameTypeImmediateAck ))
if !valid {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : typ ,
ErrorMessage : errUnknownFrameType .Error(),
}
}
if !ft .isAllowedAtEncLevel (encLevel ) {
return 0 , parsed , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : typ ,
ErrorMessage : fmt .Sprintf ("%d not allowed at encryption level %s" , ft , encLevel ),
}
}
return ft , parsed , nil
}
return 0 , parsed , io .EOF
}
func (p *FrameParser ) ParseStreamFrame (frameType FrameType , data []byte , v protocol .Version ) (*StreamFrame , int , error ) {
frame , n , err := ParseStreamFrame (data , frameType , v )
if err != nil {
return nil , n , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return frame , n , nil
}
func (p *FrameParser ) ParseAckFrame (frameType FrameType , data []byte , encLevel protocol .EncryptionLevel , v protocol .Version ) (*AckFrame , int , error ) {
ackDelayExponent := p .ackDelayExponent
if encLevel != protocol .Encryption1RTT {
ackDelayExponent = protocol .DefaultAckDelayExponent
}
p .ackFrame .Reset ()
l , err := parseAckFrame (p .ackFrame , data , frameType , ackDelayExponent , v )
if err != nil {
return nil , l , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return p .ackFrame , l , nil
}
func (p *FrameParser ) ParseDatagramFrame (frameType FrameType , data []byte , v protocol .Version ) (*DatagramFrame , int , error ) {
f , l , err := parseDatagramFrame (data , frameType , v )
if err != nil {
return nil , 0 , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return f , l , nil
}
func (p *FrameParser ) ParseLessCommonFrame (frameType FrameType , data []byte , v protocol .Version ) (Frame , int , error ) {
var frame Frame
var l int
var err error
switch frameType {
case FrameTypePing :
frame = &PingFrame {}
case FrameTypeResetStream :
frame , l , err = parseResetStreamFrame (data , false , v )
case FrameTypeStopSending :
frame , l , err = parseStopSendingFrame (data , v )
case FrameTypeCrypto :
frame , l , err = parseCryptoFrame (data , v )
case FrameTypeNewToken :
frame , l , err = parseNewTokenFrame (data , v )
case FrameTypeMaxData :
frame , l , err = parseMaxDataFrame (data , v )
case FrameTypeMaxStreamData :
frame , l , err = parseMaxStreamDataFrame (data , v )
case FrameTypeBidiMaxStreams , FrameTypeUniMaxStreams :
frame , l , err = parseMaxStreamsFrame (data , frameType , v )
case FrameTypeDataBlocked :
frame , l , err = parseDataBlockedFrame (data , v )
case FrameTypeStreamDataBlocked :
frame , l , err = parseStreamDataBlockedFrame (data , v )
case FrameTypeBidiStreamBlocked , FrameTypeUniStreamBlocked :
frame , l , err = parseStreamsBlockedFrame (data , frameType , v )
case FrameTypeNewConnectionID :
frame , l , err = parseNewConnectionIDFrame (data , v )
case FrameTypeRetireConnectionID :
frame , l , err = parseRetireConnectionIDFrame (data , v )
case FrameTypePathChallenge :
frame , l , err = parsePathChallengeFrame (data , v )
case FrameTypePathResponse :
frame , l , err = parsePathResponseFrame (data , v )
case FrameTypeConnectionClose , FrameTypeApplicationClose :
frame , l , err = parseConnectionCloseFrame (data , frameType , v )
case FrameTypeHandshakeDone :
frame = &HandshakeDoneFrame {}
case FrameTypeResetStreamAt :
frame , l , err = parseResetStreamFrame (data , true , v )
case FrameTypeAckFrequency :
frame , l , err = parseAckFrequencyFrame (data , v )
case FrameTypeImmediateAck :
frame = &ImmediateAckFrame {}
default :
err = errUnknownFrameType
}
if err != nil {
return frame , l , &qerr .TransportError {
ErrorCode : qerr .FrameEncodingError ,
FrameType : uint64 (frameType ),
ErrorMessage : err .Error(),
}
}
return frame , l , err
}
func (p *FrameParser ) SetAckDelayExponent (exp uint8 ) {
p .ackDelayExponent = exp
}
func replaceUnexpectedEOF(e error ) error {
if e == io .ErrUnexpectedEOF {
return io .EOF
}
return e
}
The pages are generated with Golds v0.8.2 . (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 .