Files
quic-go/qlog/event.go
Marten Seemann c2131eb595 qlog: split serializiation and event definitions, remove logging abstraction (#5356)
* qlog: implement a Trace and a Writer struct

* qlog: rename Trace to FileSeq

* split qlog trace writer and QUIC qlog events into separate packages

* use the new qlog.Recorder instead of the logging.ConnectionTracer
2025-10-08 05:53:02 +02:00

790 lines
24 KiB
Go

package qlog
import (
"errors"
"fmt"
"net"
"net/netip"
"time"
"github.com/quic-go/quic-go/internal/protocol"
"github.com/quic-go/quic-go/internal/qerr"
"github.com/quic-go/quic-go/qlogwriter/jsontext"
)
func milliseconds(dur time.Duration) float64 { return float64(dur.Nanoseconds()) / 1e6 }
type encoderHelper struct {
enc *jsontext.Encoder
err error
}
func (h *encoderHelper) WriteToken(t jsontext.Token) {
if h.err != nil {
return
}
h.err = h.enc.WriteToken(t)
}
type eventDetails interface {
Name() string
Encode(*jsontext.Encoder) error
}
type event struct {
EventTime time.Time
RelativeTime time.Duration
eventDetails
}
func (e event) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("time"))
h.WriteToken(jsontext.Float(milliseconds(e.RelativeTime)))
h.WriteToken(jsontext.String("name"))
h.WriteToken(jsontext.String(e.Name()))
h.WriteToken(jsontext.String("data"))
if err := e.eventDetails.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type versions []Version
func (v versions) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginArray)
for _, e := range v {
h.WriteToken(jsontext.String(fmt.Sprintf("%x", uint32(e))))
}
h.WriteToken(jsontext.EndArray)
return h.err
}
type RawInfo struct {
Length int // full packet length, including header and AEAD authentication tag
PayloadLength int // length of the packet payload, excluding AEAD tag
}
func (i RawInfo) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("length"))
h.WriteToken(jsontext.Uint(uint64(i.Length)))
if i.PayloadLength != 0 {
h.WriteToken(jsontext.String("payload_length"))
h.WriteToken(jsontext.Uint(uint64(i.PayloadLength)))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type StartedConnection struct {
SrcAddr *net.UDPAddr
DestAddr *net.UDPAddr
SrcConnectionID ConnectionID
DestConnectionID ConnectionID
}
func (e StartedConnection) Name() string { return "transport:connection_started" }
func (e StartedConnection) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if e.SrcAddr.IP.To4() != nil {
h.WriteToken(jsontext.String("ip_version"))
h.WriteToken(jsontext.String("ipv4"))
} else {
h.WriteToken(jsontext.String("ip_version"))
h.WriteToken(jsontext.String("ipv6"))
}
h.WriteToken(jsontext.String("src_ip"))
h.WriteToken(jsontext.String(e.SrcAddr.IP.String()))
h.WriteToken(jsontext.String("src_port"))
h.WriteToken(jsontext.Int(int64(e.SrcAddr.Port)))
h.WriteToken(jsontext.String("dst_ip"))
h.WriteToken(jsontext.String(e.DestAddr.IP.String()))
h.WriteToken(jsontext.String("dst_port"))
h.WriteToken(jsontext.Int(int64(e.DestAddr.Port)))
h.WriteToken(jsontext.String("src_cid"))
h.WriteToken(jsontext.String(e.SrcConnectionID.String()))
h.WriteToken(jsontext.String("dst_cid"))
h.WriteToken(jsontext.String(e.DestConnectionID.String()))
h.WriteToken(jsontext.EndObject)
return h.err
}
type VersionInformation struct {
ClientVersions, ServerVersions []Version
ChosenVersion Version
}
func (e VersionInformation) Name() string { return "transport:version_information" }
func (e VersionInformation) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if len(e.ClientVersions) > 0 {
h.WriteToken(jsontext.String("client_versions"))
if err := versions(e.ClientVersions).Encode(enc); err != nil {
return err
}
}
if len(e.ServerVersions) > 0 {
h.WriteToken(jsontext.String("server_versions"))
if err := versions(e.ServerVersions).Encode(enc); err != nil {
return err
}
}
h.WriteToken(jsontext.String("chosen_version"))
h.WriteToken(jsontext.String(fmt.Sprintf("%x", uint32(e.ChosenVersion))))
h.WriteToken(jsontext.EndObject)
return h.err
}
type ConnectionClosed struct {
Error error
}
func (e ConnectionClosed) Name() string { return "transport:connection_closed" }
func (e ConnectionClosed) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
var (
statelessResetErr *qerr.StatelessResetError
handshakeTimeoutErr *qerr.HandshakeTimeoutError
idleTimeoutErr *qerr.IdleTimeoutError
applicationErr *qerr.ApplicationError
transportErr *qerr.TransportError
versionNegotiationErr *qerr.VersionNegotiationError
)
switch {
case errors.As(e.Error, &statelessResetErr):
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(OwnerRemote)))
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("stateless_reset"))
case errors.As(e.Error, &handshakeTimeoutErr):
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(OwnerLocal)))
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("handshake_timeout"))
case errors.As(e.Error, &idleTimeoutErr):
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(OwnerLocal)))
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("idle_timeout"))
case errors.As(e.Error, &applicationErr):
owner := OwnerLocal
if applicationErr.Remote {
owner = OwnerRemote
}
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(owner)))
h.WriteToken(jsontext.String("application_code"))
h.WriteToken(jsontext.Uint(uint64(applicationErr.ErrorCode)))
h.WriteToken(jsontext.String("reason"))
h.WriteToken(jsontext.String(applicationErr.ErrorMessage))
case errors.As(e.Error, &transportErr):
owner := OwnerLocal
if transportErr.Remote {
owner = OwnerRemote
}
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(owner)))
h.WriteToken(jsontext.String("connection_code"))
h.WriteToken(jsontext.String(transportError(transportErr.ErrorCode).String()))
h.WriteToken(jsontext.String("reason"))
h.WriteToken(jsontext.String(transportErr.ErrorMessage))
case errors.As(e.Error, &versionNegotiationErr):
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("version_mismatch"))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type PacketSent struct {
Header PacketHeader
Raw RawInfo
Frames []Frame
ECN ECN
IsCoalesced bool
Trigger string
SupportedVersions []Version
}
func (e PacketSent) Name() string { return "transport:packet_sent" }
func (e PacketSent) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("raw"))
if err := e.Raw.Encode(enc); err != nil {
return err
}
if len(e.Frames) > 0 {
h.WriteToken(jsontext.String("frames"))
if err := frames(e.Frames).Encode(enc); err != nil {
return err
}
}
if e.IsCoalesced {
h.WriteToken(jsontext.String("is_coalesced"))
h.WriteToken(jsontext.True)
}
if e.ECN != ECNUnsupported {
h.WriteToken(jsontext.String("ecn"))
h.WriteToken(jsontext.String(string(e.ECN)))
}
if e.Trigger != "" {
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(e.Trigger))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type PacketReceived struct {
Header PacketHeader
Raw RawInfo
Frames []Frame
ECN ECN
IsCoalesced bool
Trigger string
}
func (e PacketReceived) Name() string { return "transport:packet_received" }
func (e PacketReceived) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("raw"))
if err := e.Raw.Encode(enc); err != nil {
return err
}
if len(e.Frames) > 0 {
h.WriteToken(jsontext.String("frames"))
if err := frames(e.Frames).Encode(enc); err != nil {
return err
}
}
if e.IsCoalesced {
h.WriteToken(jsontext.String("is_coalesced"))
h.WriteToken(jsontext.True)
}
if e.ECN != ECNUnsupported {
h.WriteToken(jsontext.String("ecn"))
h.WriteToken(jsontext.String(string(e.ECN)))
}
if e.Trigger != "" {
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(e.Trigger))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type VersionNegotiationReceived struct {
Header PacketHeaderVersionNegotiation
SupportedVersions []Version
}
func (e VersionNegotiationReceived) Name() string { return "transport:packet_received" }
func (e VersionNegotiationReceived) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("supported_versions"))
if err := versions(e.SupportedVersions).Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type VersionNegotiationSent struct {
Header PacketHeaderVersionNegotiation
SupportedVersions []Version
}
func (e VersionNegotiationSent) Name() string { return "transport:packet_sent" }
func (e VersionNegotiationSent) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("supported_versions"))
if err := versions(e.SupportedVersions).Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type PacketBuffered struct {
Header PacketHeader
Raw RawInfo
}
func (e PacketBuffered) Name() string { return "transport:packet_buffered" }
func (e PacketBuffered) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("raw"))
if err := e.Raw.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("keys_unavailable"))
h.WriteToken(jsontext.EndObject)
return h.err
}
// PacketDropped is the transport:packet_dropped event.
type PacketDropped struct {
Header PacketHeader
Raw RawInfo
Trigger PacketDropReason
}
func (e PacketDropped) Name() string { return "transport:packet_dropped" }
func (e PacketDropped) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("raw"))
if err := e.Raw.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(string(e.Trigger)))
h.WriteToken(jsontext.EndObject)
return h.err
}
type MTUUpdated struct {
Value int
Done bool
}
func (e MTUUpdated) Name() string { return "recovery:mtu_updated" }
func (e MTUUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("mtu"))
h.WriteToken(jsontext.Uint(uint64(e.Value)))
h.WriteToken(jsontext.String("done"))
h.WriteToken(jsontext.Bool(e.Done))
h.WriteToken(jsontext.EndObject)
return h.err
}
type MetricsUpdated struct {
MinRTT *time.Duration
SmoothedRTT *time.Duration
LatestRTT *time.Duration
RTTVariance *time.Duration
CongestionWindow *int
BytesInFlight *int
PacketsInFlight *int
PTOCount *uint32
}
func (e MetricsUpdated) Name() string { return "recovery:metrics_updated" }
func (e MetricsUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if e.MinRTT != nil {
h.WriteToken(jsontext.String("min_rtt"))
h.WriteToken(jsontext.Float(milliseconds(*e.MinRTT)))
}
if e.SmoothedRTT != nil {
h.WriteToken(jsontext.String("smoothed_rtt"))
h.WriteToken(jsontext.Float(milliseconds(*e.SmoothedRTT)))
}
if e.LatestRTT != nil {
h.WriteToken(jsontext.String("latest_rtt"))
h.WriteToken(jsontext.Float(milliseconds(*e.LatestRTT)))
}
if e.RTTVariance != nil {
h.WriteToken(jsontext.String("rtt_variance"))
h.WriteToken(jsontext.Float(milliseconds(*e.RTTVariance)))
}
if e.CongestionWindow != nil {
h.WriteToken(jsontext.String("congestion_window"))
h.WriteToken(jsontext.Uint(uint64(*e.CongestionWindow)))
}
if e.BytesInFlight != nil {
h.WriteToken(jsontext.String("bytes_in_flight"))
h.WriteToken(jsontext.Uint(uint64(*e.BytesInFlight)))
}
if e.PacketsInFlight != nil {
h.WriteToken(jsontext.String("packets_in_flight"))
h.WriteToken(jsontext.Uint(uint64(*e.PacketsInFlight)))
}
if e.PTOCount != nil {
h.WriteToken(jsontext.String("pto_count"))
h.WriteToken(jsontext.Uint(uint64(*e.PTOCount)))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type PacketLost struct {
Header PacketHeader
Trigger PacketLossReason
}
func (e PacketLost) Name() string { return "recovery:packet_lost" }
func (e PacketLost) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("header"))
if err := e.Header.Encode(enc); err != nil {
return err
}
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(string(e.Trigger)))
h.WriteToken(jsontext.EndObject)
return h.err
}
type SpuriousLoss struct {
EncryptionLevel protocol.EncryptionLevel
PacketNumber protocol.PacketNumber
PacketReordering uint64
TimeReordering time.Duration
}
func (e SpuriousLoss) Name() string { return "recovery:spurious_loss" }
func (e SpuriousLoss) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("packet_number_space"))
h.WriteToken(jsontext.String(encLevelToPacketNumberSpace(e.EncryptionLevel)))
h.WriteToken(jsontext.String("packet_number"))
h.WriteToken(jsontext.Uint(uint64(e.PacketNumber)))
h.WriteToken(jsontext.String("reordering_packets"))
h.WriteToken(jsontext.Uint(e.PacketReordering))
h.WriteToken(jsontext.String("reordering_time"))
h.WriteToken(jsontext.Float(milliseconds(e.TimeReordering)))
h.WriteToken(jsontext.EndObject)
return h.err
}
type KeyUpdated struct {
Trigger KeyUpdateTrigger
KeyType KeyType
KeyPhase KeyPhase // only set for 1-RTT keys
// we don't log the keys here, so we don't need `old` and `new`.
}
func (e KeyUpdated) Name() string { return "security:key_updated" }
func (e KeyUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(string(e.Trigger)))
h.WriteToken(jsontext.String("key_type"))
h.WriteToken(jsontext.String(string(e.KeyType)))
if e.KeyType == KeyTypeClient1RTT || e.KeyType == KeyTypeServer1RTT {
h.WriteToken(jsontext.String("key_phase"))
h.WriteToken(jsontext.Uint(uint64(e.KeyPhase)))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type KeyDiscarded struct {
KeyType KeyType
KeyPhase KeyPhase // only set for 1-RTT keys
}
func (e KeyDiscarded) Name() string { return "security:key_discarded" }
func (e KeyDiscarded) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if e.KeyType != KeyTypeClient1RTT && e.KeyType != KeyTypeServer1RTT {
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String("tls"))
}
h.WriteToken(jsontext.String("key_type"))
h.WriteToken(jsontext.String(string(e.KeyType)))
if e.KeyType == KeyTypeClient1RTT || e.KeyType == KeyTypeServer1RTT {
h.WriteToken(jsontext.String("key_phase"))
h.WriteToken(jsontext.Uint(uint64(e.KeyPhase)))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type ParametersSet struct {
Restore bool
Owner Owner
SentBy protocol.Perspective
OriginalDestinationConnectionID protocol.ConnectionID
InitialSourceConnectionID protocol.ConnectionID
RetrySourceConnectionID *protocol.ConnectionID
StatelessResetToken *protocol.StatelessResetToken
DisableActiveMigration bool
MaxIdleTimeout time.Duration
MaxUDPPayloadSize protocol.ByteCount
AckDelayExponent uint8
MaxAckDelay time.Duration
ActiveConnectionIDLimit uint64
InitialMaxData protocol.ByteCount
InitialMaxStreamDataBidiLocal protocol.ByteCount
InitialMaxStreamDataBidiRemote protocol.ByteCount
InitialMaxStreamDataUni protocol.ByteCount
InitialMaxStreamsBidi int64
InitialMaxStreamsUni int64
PreferredAddress *PreferredAddress
MaxDatagramFrameSize protocol.ByteCount
EnableResetStreamAt bool
}
func (e ParametersSet) Name() string {
if e.Restore {
return "transport:parameters_restored"
}
return "transport:parameters_set"
}
func (e ParametersSet) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if !e.Restore {
h.WriteToken(jsontext.String("owner"))
h.WriteToken(jsontext.String(string(e.Owner)))
if e.SentBy == protocol.PerspectiveServer {
h.WriteToken(jsontext.String("original_destination_connection_id"))
h.WriteToken(jsontext.String(e.OriginalDestinationConnectionID.String()))
if e.StatelessResetToken != nil {
h.WriteToken(jsontext.String("stateless_reset_token"))
h.WriteToken(jsontext.String(fmt.Sprintf("%x", e.StatelessResetToken[:])))
}
if e.RetrySourceConnectionID != nil {
h.WriteToken(jsontext.String("retry_source_connection_id"))
h.WriteToken(jsontext.String((*e.RetrySourceConnectionID).String()))
}
}
h.WriteToken(jsontext.String("initial_source_connection_id"))
h.WriteToken(jsontext.String(e.InitialSourceConnectionID.String()))
}
h.WriteToken(jsontext.String("disable_active_migration"))
h.WriteToken(jsontext.Bool(e.DisableActiveMigration))
if e.MaxIdleTimeout != 0 {
h.WriteToken(jsontext.String("max_idle_timeout"))
h.WriteToken(jsontext.Float(milliseconds(e.MaxIdleTimeout)))
}
if e.MaxUDPPayloadSize != 0 {
h.WriteToken(jsontext.String("max_udp_payload_size"))
h.WriteToken(jsontext.Int(int64(e.MaxUDPPayloadSize)))
}
if e.AckDelayExponent != 0 {
h.WriteToken(jsontext.String("ack_delay_exponent"))
h.WriteToken(jsontext.Uint(uint64(e.AckDelayExponent)))
}
if e.MaxAckDelay != 0 {
h.WriteToken(jsontext.String("max_ack_delay"))
h.WriteToken(jsontext.Float(milliseconds(e.MaxAckDelay)))
}
if e.ActiveConnectionIDLimit != 0 {
h.WriteToken(jsontext.String("active_connection_id_limit"))
h.WriteToken(jsontext.Uint(e.ActiveConnectionIDLimit))
}
if e.InitialMaxData != 0 {
h.WriteToken(jsontext.String("initial_max_data"))
h.WriteToken(jsontext.Int(int64(e.InitialMaxData)))
}
if e.InitialMaxStreamDataBidiLocal != 0 {
h.WriteToken(jsontext.String("initial_max_stream_data_bidi_local"))
h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataBidiLocal)))
}
if e.InitialMaxStreamDataBidiRemote != 0 {
h.WriteToken(jsontext.String("initial_max_stream_data_bidi_remote"))
h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataBidiRemote)))
}
if e.InitialMaxStreamDataUni != 0 {
h.WriteToken(jsontext.String("initial_max_stream_data_uni"))
h.WriteToken(jsontext.Int(int64(e.InitialMaxStreamDataUni)))
}
if e.InitialMaxStreamsBidi != 0 {
h.WriteToken(jsontext.String("initial_max_streams_bidi"))
h.WriteToken(jsontext.Int(e.InitialMaxStreamsBidi))
}
if e.InitialMaxStreamsUni != 0 {
h.WriteToken(jsontext.String("initial_max_streams_uni"))
h.WriteToken(jsontext.Int(e.InitialMaxStreamsUni))
}
if e.PreferredAddress != nil {
h.WriteToken(jsontext.String("preferred_address"))
if err := e.PreferredAddress.Encode(enc); err != nil {
return err
}
}
if e.MaxDatagramFrameSize != protocol.InvalidByteCount {
h.WriteToken(jsontext.String("max_datagram_frame_size"))
h.WriteToken(jsontext.Int(int64(e.MaxDatagramFrameSize)))
}
if e.EnableResetStreamAt {
h.WriteToken(jsontext.String("reset_stream_at"))
h.WriteToken(jsontext.True)
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type PreferredAddress struct {
IPv4, IPv6 netip.AddrPort
ConnectionID protocol.ConnectionID
StatelessResetToken protocol.StatelessResetToken
}
func (a PreferredAddress) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
if a.IPv4.IsValid() {
h.WriteToken(jsontext.String("ip_v4"))
h.WriteToken(jsontext.String(a.IPv4.Addr().String()))
h.WriteToken(jsontext.String("port_v4"))
h.WriteToken(jsontext.Uint(uint64(a.IPv4.Port())))
}
if a.IPv6.IsValid() {
h.WriteToken(jsontext.String("ip_v6"))
h.WriteToken(jsontext.String(a.IPv6.Addr().String()))
h.WriteToken(jsontext.String("port_v6"))
h.WriteToken(jsontext.Uint(uint64(a.IPv6.Port())))
}
h.WriteToken(jsontext.String("connection_id"))
h.WriteToken(jsontext.String(a.ConnectionID.String()))
h.WriteToken(jsontext.String("stateless_reset_token"))
h.WriteToken(jsontext.String(fmt.Sprintf("%x", a.StatelessResetToken)))
h.WriteToken(jsontext.EndObject)
return h.err
}
type LossTimerUpdated struct {
Type LossTimerUpdateType
TimerType TimerType
EncLevel EncryptionLevel
Time time.Time
}
func (e LossTimerUpdated) Name() string { return "recovery:loss_timer_updated" }
func (e LossTimerUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("event_type"))
h.WriteToken(jsontext.String(string(e.Type)))
h.WriteToken(jsontext.String("timer_type"))
h.WriteToken(jsontext.String(string(e.TimerType)))
h.WriteToken(jsontext.String("packet_number_space"))
h.WriteToken(jsontext.String(encLevelToPacketNumberSpace(e.EncLevel)))
if e.Type == LossTimerUpdateTypeSet {
h.WriteToken(jsontext.String("delta"))
h.WriteToken(jsontext.Float(milliseconds(time.Until(e.Time))))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type eventLossTimerCanceled struct{}
func (e eventLossTimerCanceled) Name() string { return "recovery:loss_timer_updated" }
func (e eventLossTimerCanceled) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("event_type"))
h.WriteToken(jsontext.String("cancelled"))
h.WriteToken(jsontext.EndObject)
return h.err
}
type CongestionStateUpdated struct {
State CongestionState
}
func (e CongestionStateUpdated) Name() string { return "recovery:congestion_state_updated" }
func (e CongestionStateUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("new"))
h.WriteToken(jsontext.String(e.State.String()))
h.WriteToken(jsontext.EndObject)
return h.err
}
type ECNStateUpdated struct {
State ECNState
Trigger string
}
func (e ECNStateUpdated) Name() string { return "recovery:ecn_state_updated" }
func (e ECNStateUpdated) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("new"))
h.WriteToken(jsontext.String(string(e.State)))
if e.Trigger != "" {
h.WriteToken(jsontext.String("trigger"))
h.WriteToken(jsontext.String(e.Trigger))
}
h.WriteToken(jsontext.EndObject)
return h.err
}
type ALPNInformation struct {
ChosenALPN string
}
func (e ALPNInformation) Name() string { return "transport:alpn_information" }
func (e ALPNInformation) Encode(enc *jsontext.Encoder) error {
h := encoderHelper{enc: enc}
h.WriteToken(jsontext.BeginObject)
h.WriteToken(jsontext.String("chosen_alpn"))
h.WriteToken(jsontext.String(e.ChosenALPN))
h.WriteToken(jsontext.EndObject)
return h.err
}