package quic
import (
"net"
"net/netip"
"slices"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/wire"
"github.com/quic-go/quic-go/qlog"
)
func toQlogFrame(frame wire .Frame ) qlog .Frame {
switch f := frame .(type ) {
case *wire .AckFrame :
return qlog .Frame {Frame : toQlogAckFrame (f )}
case *wire .CryptoFrame :
return qlog .Frame {
Frame : &qlog .CryptoFrame {
Offset : int64 (f .Offset ),
Length : int64 (len (f .Data )),
},
}
case *wire .StreamFrame :
return qlog .Frame {
Frame : &qlog .StreamFrame {
StreamID : f .StreamID ,
Offset : int64 (f .Offset ),
Length : int64 (f .DataLen ()),
Fin : f .Fin ,
},
}
case *wire .DatagramFrame :
return qlog .Frame {
Frame : &qlog .DatagramFrame {
Length : int64 (len (f .Data )),
},
}
default :
return qlog .Frame {Frame : frame }
}
}
func toQlogAckFrame(f *wire .AckFrame ) *qlog .AckFrame {
ack := &qlog .AckFrame {
AckRanges : slices .Clone (f .AckRanges ),
DelayTime : f .DelayTime ,
ECNCE : f .ECNCE ,
ECT0 : f .ECT0 ,
ECT1 : f .ECT1 ,
}
return ack
}
func (c *Conn ) logLongHeaderPacket (p *longHeaderPacket , ecn protocol .ECN , datagramID qlog .DatagramID ) {
if c .logger .Debug () {
p .header .Log (c .logger )
if p .ack != nil {
wire .LogFrame (c .logger , p .ack , true )
}
for _ , frame := range p .frames {
wire .LogFrame (c .logger , frame .Frame , true )
}
for _ , frame := range p .streamFrames {
wire .LogFrame (c .logger , frame .Frame , true )
}
}
if c .qlogger != nil {
numFrames := len (p .frames ) + len (p .streamFrames )
if p .ack != nil {
numFrames ++
}
frames := make ([]qlog .Frame , 0 , numFrames )
if p .ack != nil {
frames = append (frames , toQlogFrame (p .ack ))
}
for _ , f := range p .frames {
frames = append (frames , toQlogFrame (f .Frame ))
}
for _ , f := range p .streamFrames {
frames = append (frames , toQlogFrame (f .Frame ))
}
c .qlogger .RecordEvent (qlog .PacketSent {
Header : qlog .PacketHeader {
PacketType : toQlogPacketType (p .header .Type ),
KeyPhaseBit : p .header .KeyPhase ,
PacketNumber : p .header .PacketNumber ,
Version : p .header .Version ,
SrcConnectionID : p .header .SrcConnectionID ,
DestConnectionID : p .header .DestConnectionID ,
},
Raw : qlog .RawInfo {
Length : int (p .length ),
PayloadLength : int (p .header .Length ),
},
DatagramID : datagramID ,
Frames : frames ,
ECN : toQlogECN (ecn ),
})
}
}
func (c *Conn ) logShortHeaderPacket (p shortHeaderPacket , ecn protocol .ECN , size protocol .ByteCount ) {
c .logShortHeaderPacketWithDatagramID (p , ecn , size , false , 0 )
}
func (c *Conn ) logShortHeaderPacketWithDatagramID (p shortHeaderPacket , ecn protocol .ECN , size protocol .ByteCount , isCoalesced bool , datagramID qlog .DatagramID ) {
if c .logger .Debug () && !isCoalesced {
c .logger .Debugf ("-> Sending packet %d (%d bytes) for connection %s, 1-RTT (ECN: %s)" , p .PacketNumber , size , c .logID , ecn )
}
if c .logger .Debug () {
wire .LogShortHeader (c .logger , p .DestConnID , p .PacketNumber , p .PacketNumberLen , p .KeyPhase )
if p .Ack != nil {
wire .LogFrame (c .logger , p .Ack , true )
}
for _ , f := range p .Frames {
wire .LogFrame (c .logger , f .Frame , true )
}
for _ , f := range p .StreamFrames {
wire .LogFrame (c .logger , f .Frame , true )
}
}
if c .qlogger != nil {
numFrames := len (p .Frames ) + len (p .StreamFrames )
if p .Ack != nil {
numFrames ++
}
fs := make ([]qlog .Frame , 0 , numFrames )
if p .Ack != nil {
fs = append (fs , toQlogFrame (p .Ack ))
}
for _ , f := range p .Frames {
fs = append (fs , toQlogFrame (f .Frame ))
}
for _ , f := range p .StreamFrames {
fs = append (fs , toQlogFrame (f .Frame ))
}
c .qlogger .RecordEvent (qlog .PacketSent {
Header : qlog .PacketHeader {
PacketType : qlog .PacketType1RTT ,
KeyPhaseBit : p .KeyPhase ,
PacketNumber : p .PacketNumber ,
Version : c .version ,
DestConnectionID : p .DestConnID ,
},
Raw : qlog .RawInfo {
Length : int (size ),
PayloadLength : int (size - wire .ShortHeaderLen (p .DestConnID , p .PacketNumberLen )),
},
DatagramID : datagramID ,
Frames : fs ,
ECN : toQlogECN (ecn ),
})
}
}
func (c *Conn ) logCoalescedPacket (packet *coalescedPacket , ecn protocol .ECN ) {
var datagramID qlog .DatagramID
if c .qlogger != nil {
datagramID = qlog .CalculateDatagramID (packet .buffer .Data )
}
if c .logger .Debug () {
if len (packet .longHdrPackets ) == 0 && packet .shortHdrPacket != nil {
c .logShortHeaderPacketWithDatagramID (
*packet .shortHdrPacket ,
ecn ,
packet .shortHdrPacket .Length ,
false ,
datagramID ,
)
return
}
if len (packet .longHdrPackets ) > 1 {
c .logger .Debugf ("-> Sending coalesced packet (%d parts, %d bytes) for connection %s" , len (packet .longHdrPackets ), packet .buffer .Len (), c .logID )
} else {
c .logger .Debugf ("-> Sending packet %d (%d bytes) for connection %s, %s" , packet .longHdrPackets [0 ].header .PacketNumber , packet .buffer .Len (), c .logID , packet .longHdrPackets [0 ].EncryptionLevel ())
}
}
for _ , p := range packet .longHdrPackets {
c .logLongHeaderPacket (p , ecn , datagramID )
}
if p := packet .shortHdrPacket ; p != nil {
c .logShortHeaderPacketWithDatagramID (*p , ecn , p .Length , true , datagramID )
}
}
func (c *Conn ) qlogTransportParameters (tp *wire .TransportParameters , sentBy protocol .Perspective , restore bool ) {
ev := qlog .ParametersSet {
Restore : restore ,
OriginalDestinationConnectionID : tp .OriginalDestinationConnectionID ,
InitialSourceConnectionID : tp .InitialSourceConnectionID ,
RetrySourceConnectionID : tp .RetrySourceConnectionID ,
StatelessResetToken : tp .StatelessResetToken ,
DisableActiveMigration : tp .DisableActiveMigration ,
MaxIdleTimeout : tp .MaxIdleTimeout ,
MaxUDPPayloadSize : tp .MaxUDPPayloadSize ,
AckDelayExponent : tp .AckDelayExponent ,
MaxAckDelay : tp .MaxAckDelay ,
ActiveConnectionIDLimit : tp .ActiveConnectionIDLimit ,
InitialMaxData : tp .InitialMaxData ,
InitialMaxStreamDataBidiLocal : tp .InitialMaxStreamDataBidiLocal ,
InitialMaxStreamDataBidiRemote : tp .InitialMaxStreamDataBidiRemote ,
InitialMaxStreamDataUni : tp .InitialMaxStreamDataUni ,
InitialMaxStreamsBidi : int64 (tp .MaxBidiStreamNum ),
InitialMaxStreamsUni : int64 (tp .MaxUniStreamNum ),
MaxDatagramFrameSize : tp .MaxDatagramFrameSize ,
EnableResetStreamAt : tp .EnableResetStreamAt ,
}
if sentBy == c .perspective {
ev .Initiator = qlog .InitiatorLocal
} else {
ev .Initiator = qlog .InitiatorRemote
}
if tp .PreferredAddress != nil {
ev .PreferredAddress = &qlog .PreferredAddress {
IPv4 : tp .PreferredAddress .IPv4 ,
IPv6 : tp .PreferredAddress .IPv6 ,
ConnectionID : tp .PreferredAddress .ConnectionID ,
StatelessResetToken : tp .PreferredAddress .StatelessResetToken ,
}
}
c .qlogger .RecordEvent (ev )
}
func toQlogECN(ecn protocol .ECN ) qlog .ECN {
switch ecn {
case protocol .ECT0 :
return qlog .ECT0
case protocol .ECT1 :
return qlog .ECT1
case protocol .ECNCE :
return qlog .ECNCE
default :
return qlog .ECNUnsupported
}
}
func toQlogPacketType(pt protocol .PacketType ) qlog .PacketType {
var qpt qlog .PacketType
switch pt {
case protocol .PacketTypeInitial :
qpt = qlog .PacketTypeInitial
case protocol .PacketTypeHandshake :
qpt = qlog .PacketTypeHandshake
case protocol .PacketType0RTT :
qpt = qlog .PacketType0RTT
case protocol .PacketTypeRetry :
qpt = qlog .PacketTypeRetry
}
return qpt
}
func toPathEndpointInfo(addr *net .UDPAddr ) qlog .PathEndpointInfo {
if addr == nil {
return qlog .PathEndpointInfo {}
}
var info qlog .PathEndpointInfo
if addr .IP == nil || addr .IP .To4 () != nil {
addrPort := netip .AddrPortFrom (netip .AddrFrom4 ([4 ]byte (addr .IP .To4 ())), uint16 (addr .Port ))
if addrPort .IsValid () {
info .IPv4 = addrPort
}
} else {
addrPort := netip .AddrPortFrom (netip .AddrFrom16 ([16 ]byte (addr .IP .To16 ())), uint16 (addr .Port ))
if addrPort .IsValid () {
info .IPv6 = addrPort
}
}
return info
}
func startedConnectionEvent(local , remote *net .UDPAddr ) qlog .StartedConnection {
var localInfo , remoteInfo qlog .PathEndpointInfo
if remote != nil {
remoteInfo = toPathEndpointInfo (remote )
}
if local != nil {
if local .IP == nil || local .IP .IsUnspecified () {
if remote != nil && remote .IP .To4 () != nil {
ap := netip .AddrPortFrom (netip .AddrFrom4 ([4 ]byte {}), uint16 (local .Port ))
if ap .IsValid () {
localInfo .IPv4 = ap
}
} else if remote != nil && remote .IP .To16 () != nil && remote .IP .To4 () == nil {
ap := netip .AddrPortFrom (netip .AddrFrom16 ([16 ]byte {}), uint16 (local .Port ))
if ap .IsValid () {
localInfo .IPv6 = ap
}
}
} else {
localInfo = toPathEndpointInfo (local )
}
}
return qlog .StartedConnection {Local : localInfo , Remote : remoteInfo }
}
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 .