package wire

import (
	
	
	
	
	
	
	
	
	

	
	
	
)

// AdditionalTransportParametersClient are additional transport parameters that will be added
// to the client's transport parameters.
// This is not intended for production use, but _only_ to increase the size of the ClientHello beyond
// the usual size of less than 1 MTU.
var AdditionalTransportParametersClient map[uint64][]byte

const transportParameterMarshalingVersion = 1

type transportParameterID uint64

const (
	originalDestinationConnectionIDParameterID transportParameterID = 0x0
	maxIdleTimeoutParameterID                  transportParameterID = 0x1
	statelessResetTokenParameterID             transportParameterID = 0x2
	maxUDPPayloadSizeParameterID               transportParameterID = 0x3
	initialMaxDataParameterID                  transportParameterID = 0x4
	initialMaxStreamDataBidiLocalParameterID   transportParameterID = 0x5
	initialMaxStreamDataBidiRemoteParameterID  transportParameterID = 0x6
	initialMaxStreamDataUniParameterID         transportParameterID = 0x7
	initialMaxStreamsBidiParameterID           transportParameterID = 0x8
	initialMaxStreamsUniParameterID            transportParameterID = 0x9
	ackDelayExponentParameterID                transportParameterID = 0xa
	maxAckDelayParameterID                     transportParameterID = 0xb
	disableActiveMigrationParameterID          transportParameterID = 0xc
	preferredAddressParameterID                transportParameterID = 0xd
	activeConnectionIDLimitParameterID         transportParameterID = 0xe
	initialSourceConnectionIDParameterID       transportParameterID = 0xf
	retrySourceConnectionIDParameterID         transportParameterID = 0x10
	// RFC 9221
	maxDatagramFrameSizeParameterID transportParameterID = 0x20
	// https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/06/
	resetStreamAtParameterID transportParameterID = 0x17f7586d2cb571
	// https://datatracker.ietf.org/doc/draft-ietf-quic-ack-frequency/11/
	minAckDelayParameterID transportParameterID = 0xff04de1b
)

// PreferredAddress is the value encoding in the preferred_address transport parameter
type PreferredAddress struct {
	IPv4, IPv6          netip.AddrPort
	ConnectionID        protocol.ConnectionID
	StatelessResetToken protocol.StatelessResetToken
}

// TransportParameters are parameters sent to the peer during the handshake
type TransportParameters struct {
	InitialMaxStreamDataBidiLocal  protocol.ByteCount
	InitialMaxStreamDataBidiRemote protocol.ByteCount
	InitialMaxStreamDataUni        protocol.ByteCount
	InitialMaxData                 protocol.ByteCount

	MaxAckDelay      time.Duration
	AckDelayExponent uint8

	DisableActiveMigration bool

	MaxUDPPayloadSize protocol.ByteCount

	MaxUniStreamNum  protocol.StreamNum
	MaxBidiStreamNum protocol.StreamNum

	MaxIdleTimeout time.Duration

	PreferredAddress *PreferredAddress

	OriginalDestinationConnectionID protocol.ConnectionID
	InitialSourceConnectionID       protocol.ConnectionID
	RetrySourceConnectionID         *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters

	StatelessResetToken     *protocol.StatelessResetToken
	ActiveConnectionIDLimit uint64

	MaxDatagramFrameSize protocol.ByteCount // RFC 9221
	EnableResetStreamAt  bool               // https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/06/
	MinAckDelay          *time.Duration
}

// Unmarshal the transport parameters
func ( *TransportParameters) ( []byte,  protocol.Perspective) error {
	if  := .unmarshal(, , false);  != nil {
		return &qerr.TransportError{
			ErrorCode:    qerr.TransportParameterError,
			ErrorMessage: .Error(),
		}
	}
	return nil
}

func ( *TransportParameters) ( []byte,  protocol.Perspective,  bool) error {
	// needed to check that every parameter is only sent at most once
	 := make([]transportParameterID, 0, 32)

	var (
		 bool
		       bool
	)

	.AckDelayExponent = protocol.DefaultAckDelayExponent
	.MaxAckDelay = protocol.DefaultMaxAckDelay
	.MaxDatagramFrameSize = protocol.InvalidByteCount
	.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit

	for len() > 0 {
		, ,  := quicvarint.Parse()
		if  != nil {
			return 
		}
		 := transportParameterID()
		 = [:]
		, ,  := quicvarint.Parse()
		if  != nil {
			return 
		}
		 = [:]
		if uint64(len()) <  {
			return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", len(), )
		}
		 = append(, )
		switch  {
		case maxIdleTimeoutParameterID,
			maxUDPPayloadSizeParameterID,
			initialMaxDataParameterID,
			initialMaxStreamDataBidiLocalParameterID,
			initialMaxStreamDataBidiRemoteParameterID,
			initialMaxStreamDataUniParameterID,
			initialMaxStreamsBidiParameterID,
			initialMaxStreamsUniParameterID,
			maxAckDelayParameterID,
			maxDatagramFrameSizeParameterID,
			ackDelayExponentParameterID,
			activeConnectionIDLimitParameterID,
			minAckDelayParameterID:
			if  := .readNumericTransportParameter(, , int());  != nil {
				return 
			}
			 = [:]
		case preferredAddressParameterID:
			if  == protocol.PerspectiveClient {
				return errors.New("client sent a preferred_address")
			}
			if  := .readPreferredAddress(, int());  != nil {
				return 
			}
			 = [:]
		case disableActiveMigrationParameterID:
			if  != 0 {
				return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", )
			}
			.DisableActiveMigration = true
		case statelessResetTokenParameterID:
			if  == protocol.PerspectiveClient {
				return errors.New("client sent a stateless_reset_token")
			}
			if  != 16 {
				return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", )
			}
			var  protocol.StatelessResetToken
			if len() < len() {
				return io.EOF
			}
			copy([:], )
			 = [len():]
			.StatelessResetToken = &
		case originalDestinationConnectionIDParameterID:
			if  == protocol.PerspectiveClient {
				return errors.New("client sent an original_destination_connection_id")
			}
			if  > protocol.MaxConnIDLen {
				return protocol.ErrInvalidConnectionIDLen
			}
			.OriginalDestinationConnectionID = protocol.ParseConnectionID([:])
			 = [:]
			 = true
		case initialSourceConnectionIDParameterID:
			if  > protocol.MaxConnIDLen {
				return protocol.ErrInvalidConnectionIDLen
			}
			.InitialSourceConnectionID = protocol.ParseConnectionID([:])
			 = [:]
			 = true
		case retrySourceConnectionIDParameterID:
			if  == protocol.PerspectiveClient {
				return errors.New("client sent a retry_source_connection_id")
			}
			if  > protocol.MaxConnIDLen {
				return protocol.ErrInvalidConnectionIDLen
			}
			 := protocol.ParseConnectionID([:])
			 = [:]
			.RetrySourceConnectionID = &
		case resetStreamAtParameterID:
			if  != 0 {
				return fmt.Errorf("wrong length for reset_stream_at: %d (expected empty)", )
			}
			.EnableResetStreamAt = true
		default:
			 = [:]
		}
	}

	// min_ack_delay must be less or equal to max_ack_delay
	if .MinAckDelay != nil && *.MinAckDelay > .MaxAckDelay {
		return fmt.Errorf("min_ack_delay (%s) is greater than max_ack_delay (%s)", *.MinAckDelay, .MaxAckDelay)
	}
	if ! {
		if  == protocol.PerspectiveServer && ! {
			return errors.New("missing original_destination_connection_id")
		}
		if .MaxUDPPayloadSize == 0 {
			.MaxUDPPayloadSize = protocol.MaxByteCount
		}
		if ! {
			return errors.New("missing initial_source_connection_id")
		}
	}

	// check that every transport parameter was sent at most once
	slices.SortFunc(, func(,  transportParameterID) int {
		if  <  {
			return -1
		}
		return 1
	})
	for  := 0;  < len()-1; ++ {
		if [] == [+1] {
			return fmt.Errorf("received duplicate transport parameter %#x", [])
		}
	}

	return nil
}

func ( *TransportParameters) ( []byte,  int) error {
	 := len()
	 := &PreferredAddress{}
	if len() < 4+2+16+2+1 {
		return io.EOF
	}
	var  [4]byte
	copy([:], [:4])
	 := binary.BigEndian.Uint16([4:])
	 = [4+2:]
	if  != 0 &&  != [4]byte{} {
		.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(), )
	}
	var  [16]byte
	copy([:], [:16])
	 := binary.BigEndian.Uint16([16:])
	if  != 0 &&  != [16]byte{} {
		.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(), )
	}
	 = [16+2:]
	 := int([0])
	 = [1:]
	if  == 0 ||  > protocol.MaxConnIDLen {
		return fmt.Errorf("invalid connection ID length: %d", )
	}
	if len() < +len(.StatelessResetToken) {
		return io.EOF
	}
	.ConnectionID = protocol.ParseConnectionID([:])
	 = [:]
	copy(.StatelessResetToken[:], )
	 = [len(.StatelessResetToken):]
	if  :=  - len();  !=  {
		return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", , )
	}
	.PreferredAddress = 
	return nil
}

func ( *TransportParameters) ( []byte,  transportParameterID,  int) error {
	, ,  := quicvarint.Parse()
	if  != nil {
		return fmt.Errorf("error while reading transport parameter %d: %s", , )
	}
	if  !=  {
		return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", )
	}
	//nolint:exhaustive // This only covers the numeric transport parameters.
	switch  {
	case initialMaxStreamDataBidiLocalParameterID:
		.InitialMaxStreamDataBidiLocal = protocol.ByteCount()
	case initialMaxStreamDataBidiRemoteParameterID:
		.InitialMaxStreamDataBidiRemote = protocol.ByteCount()
	case initialMaxStreamDataUniParameterID:
		.InitialMaxStreamDataUni = protocol.ByteCount()
	case initialMaxDataParameterID:
		.InitialMaxData = protocol.ByteCount()
	case initialMaxStreamsBidiParameterID:
		.MaxBidiStreamNum = protocol.StreamNum()
		if .MaxBidiStreamNum > protocol.MaxStreamCount {
			return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", .MaxBidiStreamNum, protocol.MaxStreamCount)
		}
	case initialMaxStreamsUniParameterID:
		.MaxUniStreamNum = protocol.StreamNum()
		if .MaxUniStreamNum > protocol.MaxStreamCount {
			return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", .MaxUniStreamNum, protocol.MaxStreamCount)
		}
	case maxIdleTimeoutParameterID:
		.MaxIdleTimeout = max(protocol.MinRemoteIdleTimeout, time.Duration()*time.Millisecond)
	case maxUDPPayloadSizeParameterID:
		if  < 1200 {
			return fmt.Errorf("invalid value for max_udp_payload_size: %d (minimum 1200)", )
		}
		.MaxUDPPayloadSize = protocol.ByteCount()
	case ackDelayExponentParameterID:
		if  > protocol.MaxAckDelayExponent {
			return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", , protocol.MaxAckDelayExponent)
		}
		.AckDelayExponent = uint8()
	case maxAckDelayParameterID:
		if  > uint64(protocol.MaxMaxAckDelay/time.Millisecond) {
			return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", , protocol.MaxMaxAckDelay/time.Millisecond)
		}
		.MaxAckDelay = time.Duration() * time.Millisecond
	case activeConnectionIDLimitParameterID:
		if  < 2 {
			return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", )
		}
		.ActiveConnectionIDLimit = 
	case maxDatagramFrameSizeParameterID:
		.MaxDatagramFrameSize = protocol.ByteCount()
	case minAckDelayParameterID:
		 := time.Duration() * time.Microsecond
		if  < 0 {
			 = math.MaxInt64
		}
		.MinAckDelay = &
	default:
		return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", )
	}
	return nil
}

// Marshal the transport parameters
func ( *TransportParameters) ( protocol.Perspective) []byte {
	// Typical Transport Parameters consume around 110 bytes, depending on the exact values,
	// especially the lengths of the Connection IDs.
	// Allocate 256 bytes, so we won't have to grow the slice in any case.
	 := make([]byte, 0, 256)

	// add a greased value
	 := make([]byte, 18)
	rand.Read()
	 = quicvarint.Append(, 27+31*uint64([0]))
	 := [1] % 16
	 = quicvarint.Append(, uint64())
	 = append(, [2:2+]...)

	// initial_max_stream_data_bidi_local
	 = .marshalVarintParam(, initialMaxStreamDataBidiLocalParameterID, uint64(.InitialMaxStreamDataBidiLocal))
	// initial_max_stream_data_bidi_remote
	 = .marshalVarintParam(, initialMaxStreamDataBidiRemoteParameterID, uint64(.InitialMaxStreamDataBidiRemote))
	// initial_max_stream_data_uni
	 = .marshalVarintParam(, initialMaxStreamDataUniParameterID, uint64(.InitialMaxStreamDataUni))
	// initial_max_data
	 = .marshalVarintParam(, initialMaxDataParameterID, uint64(.InitialMaxData))
	// initial_max_bidi_streams
	 = .marshalVarintParam(, initialMaxStreamsBidiParameterID, uint64(.MaxBidiStreamNum))
	// initial_max_uni_streams
	 = .marshalVarintParam(, initialMaxStreamsUniParameterID, uint64(.MaxUniStreamNum))
	// idle_timeout
	 = .marshalVarintParam(, maxIdleTimeoutParameterID, uint64(.MaxIdleTimeout/time.Millisecond))
	// max_udp_payload_size
	if .MaxUDPPayloadSize > 0 {
		 = .marshalVarintParam(, maxUDPPayloadSizeParameterID, uint64(.MaxUDPPayloadSize))
	}
	// max_ack_delay
	// Only send it if is different from the default value.
	if .MaxAckDelay != protocol.DefaultMaxAckDelay {
		 = .marshalVarintParam(, maxAckDelayParameterID, uint64(.MaxAckDelay/time.Millisecond))
	}
	// ack_delay_exponent
	// Only send it if is different from the default value.
	if .AckDelayExponent != protocol.DefaultAckDelayExponent {
		 = .marshalVarintParam(, ackDelayExponentParameterID, uint64(.AckDelayExponent))
	}
	// disable_active_migration
	if .DisableActiveMigration {
		 = quicvarint.Append(, uint64(disableActiveMigrationParameterID))
		 = quicvarint.Append(, 0)
	}
	if  == protocol.PerspectiveServer {
		// stateless_reset_token
		if .StatelessResetToken != nil {
			 = quicvarint.Append(, uint64(statelessResetTokenParameterID))
			 = quicvarint.Append(, 16)
			 = append(, .StatelessResetToken[:]...)
		}
		// original_destination_connection_id
		 = quicvarint.Append(, uint64(originalDestinationConnectionIDParameterID))
		 = quicvarint.Append(, uint64(.OriginalDestinationConnectionID.Len()))
		 = append(, .OriginalDestinationConnectionID.Bytes()...)
		// preferred_address
		if .PreferredAddress != nil {
			 = quicvarint.Append(, uint64(preferredAddressParameterID))
			 = quicvarint.Append(, 4+2+16+2+1+uint64(.PreferredAddress.ConnectionID.Len())+16)
			if .PreferredAddress.IPv4.IsValid() {
				 := .PreferredAddress.IPv4.Addr().As4()
				 = append(, [:]...)
				 = binary.BigEndian.AppendUint16(, .PreferredAddress.IPv4.Port())
			} else {
				 = append(, make([]byte, 6)...)
			}
			if .PreferredAddress.IPv6.IsValid() {
				 := .PreferredAddress.IPv6.Addr().As16()
				 = append(, [:]...)
				 = binary.BigEndian.AppendUint16(, .PreferredAddress.IPv6.Port())
			} else {
				 = append(, make([]byte, 18)...)
			}
			 = append(, uint8(.PreferredAddress.ConnectionID.Len()))
			 = append(, .PreferredAddress.ConnectionID.Bytes()...)
			 = append(, .PreferredAddress.StatelessResetToken[:]...)
		}
	}
	// active_connection_id_limit
	if .ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit {
		 = .marshalVarintParam(, activeConnectionIDLimitParameterID, .ActiveConnectionIDLimit)
	}
	// initial_source_connection_id
	 = quicvarint.Append(, uint64(initialSourceConnectionIDParameterID))
	 = quicvarint.Append(, uint64(.InitialSourceConnectionID.Len()))
	 = append(, .InitialSourceConnectionID.Bytes()...)
	// retry_source_connection_id
	if  == protocol.PerspectiveServer && .RetrySourceConnectionID != nil {
		 = quicvarint.Append(, uint64(retrySourceConnectionIDParameterID))
		 = quicvarint.Append(, uint64(.RetrySourceConnectionID.Len()))
		 = append(, .RetrySourceConnectionID.Bytes()...)
	}
	// QUIC datagrams
	if .MaxDatagramFrameSize != protocol.InvalidByteCount {
		 = .marshalVarintParam(, maxDatagramFrameSizeParameterID, uint64(.MaxDatagramFrameSize))
	}
	// QUIC Stream Resets with Partial Delivery
	if .EnableResetStreamAt {
		 = quicvarint.Append(, uint64(resetStreamAtParameterID))
		 = quicvarint.Append(, 0)
	}
	if .MinAckDelay != nil {
		 = .marshalVarintParam(, minAckDelayParameterID, uint64(*.MinAckDelay/time.Microsecond))
	}

	if  == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 {
		for ,  := range AdditionalTransportParametersClient {
			 = quicvarint.Append(, )
			 = quicvarint.Append(, uint64(len()))
			 = append(, ...)
		}
	}

	return 
}

func ( *TransportParameters) ( []byte,  transportParameterID,  uint64) []byte {
	 = quicvarint.Append(, uint64())
	 = quicvarint.Append(, uint64(quicvarint.Len()))
	return quicvarint.Append(, )
}

// MarshalForSessionTicket marshals the transport parameters we save in the session ticket.
// When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters.
// The client will remember the transport parameters used in the last session,
// and apply those to the 0-RTT data it sends.
// Saving the transport parameters in the ticket gives the server the option to reject 0-RTT
// if the transport parameters changed.
// Since the session ticket is encrypted, the serialization format is defined by the server.
// For convenience, we use the same format that we also use for sending the transport parameters.
func ( *TransportParameters) ( []byte) []byte {
	 = quicvarint.Append(, transportParameterMarshalingVersion)

	// initial_max_stream_data_bidi_local
	 = .marshalVarintParam(, initialMaxStreamDataBidiLocalParameterID, uint64(.InitialMaxStreamDataBidiLocal))
	// initial_max_stream_data_bidi_remote
	 = .marshalVarintParam(, initialMaxStreamDataBidiRemoteParameterID, uint64(.InitialMaxStreamDataBidiRemote))
	// initial_max_stream_data_uni
	 = .marshalVarintParam(, initialMaxStreamDataUniParameterID, uint64(.InitialMaxStreamDataUni))
	// initial_max_data
	 = .marshalVarintParam(, initialMaxDataParameterID, uint64(.InitialMaxData))
	// initial_max_bidi_streams
	 = .marshalVarintParam(, initialMaxStreamsBidiParameterID, uint64(.MaxBidiStreamNum))
	// initial_max_uni_streams
	 = .marshalVarintParam(, initialMaxStreamsUniParameterID, uint64(.MaxUniStreamNum))
	// active_connection_id_limit
	 = .marshalVarintParam(, activeConnectionIDLimitParameterID, .ActiveConnectionIDLimit)
	// max_datagram_frame_size
	if .MaxDatagramFrameSize != protocol.InvalidByteCount {
		 = .marshalVarintParam(, maxDatagramFrameSizeParameterID, uint64(.MaxDatagramFrameSize))
	}
	// reset_stream_at
	if .EnableResetStreamAt {
		 = quicvarint.Append(, uint64(resetStreamAtParameterID))
		 = quicvarint.Append(, 0)
	}
	return 
}

// UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket.
func ( *TransportParameters) ( []byte) error {
	, ,  := quicvarint.Parse()
	if  != nil {
		return 
	}
	if  != transportParameterMarshalingVersion {
		return fmt.Errorf("unknown transport parameter marshaling version: %d", )
	}
	return .unmarshal([:], protocol.PerspectiveServer, true)
}

// ValidFor0RTT checks if the transport parameters match those saved in the session ticket.
func ( *TransportParameters) ( *TransportParameters) bool {
	if .MaxDatagramFrameSize != protocol.InvalidByteCount && (.MaxDatagramFrameSize == protocol.InvalidByteCount || .MaxDatagramFrameSize < .MaxDatagramFrameSize) {
		return false
	}
	return .InitialMaxStreamDataBidiLocal >= .InitialMaxStreamDataBidiLocal &&
		.InitialMaxStreamDataBidiRemote >= .InitialMaxStreamDataBidiRemote &&
		.InitialMaxStreamDataUni >= .InitialMaxStreamDataUni &&
		.InitialMaxData >= .InitialMaxData &&
		.MaxBidiStreamNum >= .MaxBidiStreamNum &&
		.MaxUniStreamNum >= .MaxUniStreamNum &&
		.ActiveConnectionIDLimit == .ActiveConnectionIDLimit
}

// ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection.
// It is only used on the client side.
func ( *TransportParameters) ( *TransportParameters) bool {
	if .MaxDatagramFrameSize != protocol.InvalidByteCount && (.MaxDatagramFrameSize == protocol.InvalidByteCount || .MaxDatagramFrameSize < .MaxDatagramFrameSize) {
		return false
	}
	return .ActiveConnectionIDLimit >= .ActiveConnectionIDLimit &&
		.InitialMaxData >= .InitialMaxData &&
		.InitialMaxStreamDataBidiLocal >= .InitialMaxStreamDataBidiLocal &&
		.InitialMaxStreamDataBidiRemote >= .InitialMaxStreamDataBidiRemote &&
		.InitialMaxStreamDataUni >= .InitialMaxStreamDataUni &&
		.MaxBidiStreamNum >= .MaxBidiStreamNum &&
		.MaxUniStreamNum >= .MaxUniStreamNum
}

// String returns a string representation, intended for logging.
func ( *TransportParameters) () string {
	 := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, "
	 := []any{.OriginalDestinationConnectionID, .InitialSourceConnectionID}
	if .RetrySourceConnectionID != nil {
		 += "RetrySourceConnectionID: %s, "
		 = append(, .RetrySourceConnectionID)
	}
	 += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d"
	 = append(, []any{.InitialMaxStreamDataBidiLocal, .InitialMaxStreamDataBidiRemote, .InitialMaxStreamDataUni, .InitialMaxData, .MaxBidiStreamNum, .MaxUniStreamNum, .MaxIdleTimeout, .AckDelayExponent, .MaxAckDelay, .ActiveConnectionIDLimit}...)
	if .StatelessResetToken != nil { // the client never sends a stateless reset token
		 += ", StatelessResetToken: %#x"
		 = append(, *.StatelessResetToken)
	}
	if .MaxDatagramFrameSize != protocol.InvalidByteCount {
		 += ", MaxDatagramFrameSize: %d"
		 = append(, .MaxDatagramFrameSize)
	}
	 += ", EnableResetStreamAt: %t"
	 = append(, .EnableResetStreamAt)
	if .MinAckDelay != nil {
		 += ", MinAckDelay: %s"
		 = append(, *.MinAckDelay)
	}
	 += "}"
	return fmt.Sprintf(, ...)
}