github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/wire/transport_parameters.go (about) 1 package wire 2 3 import ( 4 "bytes" 5 "crypto/rand" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 "net/netip" 11 "sort" 12 "time" 13 14 "github.com/daeuniverse/quic-go/internal/protocol" 15 "github.com/daeuniverse/quic-go/internal/qerr" 16 "github.com/daeuniverse/quic-go/internal/utils" 17 "github.com/daeuniverse/quic-go/quicvarint" 18 ) 19 20 // AdditionalTransportParametersClient are additional transport parameters that will be added 21 // to the client's transport parameters. 22 // This is not intended for production use, but _only_ to increase the size of the ClientHello beyond 23 // the usual size of less than 1 MTU. 24 var AdditionalTransportParametersClient map[uint64][]byte 25 26 const transportParameterMarshalingVersion = 1 27 28 type transportParameterID uint64 29 30 const ( 31 originalDestinationConnectionIDParameterID transportParameterID = 0x0 32 maxIdleTimeoutParameterID transportParameterID = 0x1 33 statelessResetTokenParameterID transportParameterID = 0x2 34 maxUDPPayloadSizeParameterID transportParameterID = 0x3 35 initialMaxDataParameterID transportParameterID = 0x4 36 initialMaxStreamDataBidiLocalParameterID transportParameterID = 0x5 37 initialMaxStreamDataBidiRemoteParameterID transportParameterID = 0x6 38 initialMaxStreamDataUniParameterID transportParameterID = 0x7 39 initialMaxStreamsBidiParameterID transportParameterID = 0x8 40 initialMaxStreamsUniParameterID transportParameterID = 0x9 41 ackDelayExponentParameterID transportParameterID = 0xa 42 maxAckDelayParameterID transportParameterID = 0xb 43 disableActiveMigrationParameterID transportParameterID = 0xc 44 preferredAddressParameterID transportParameterID = 0xd 45 activeConnectionIDLimitParameterID transportParameterID = 0xe 46 initialSourceConnectionIDParameterID transportParameterID = 0xf 47 retrySourceConnectionIDParameterID transportParameterID = 0x10 48 // RFC 9221 49 maxDatagramFrameSizeParameterID transportParameterID = 0x20 50 ) 51 52 // PreferredAddress is the value encoding in the preferred_address transport parameter 53 type PreferredAddress struct { 54 IPv4, IPv6 netip.AddrPort 55 ConnectionID protocol.ConnectionID 56 StatelessResetToken protocol.StatelessResetToken 57 } 58 59 // TransportParameters are parameters sent to the peer during the handshake 60 type TransportParameters struct { 61 InitialMaxStreamDataBidiLocal protocol.ByteCount 62 InitialMaxStreamDataBidiRemote protocol.ByteCount 63 InitialMaxStreamDataUni protocol.ByteCount 64 InitialMaxData protocol.ByteCount 65 66 MaxAckDelay time.Duration 67 AckDelayExponent uint8 68 69 DisableActiveMigration bool 70 71 MaxUDPPayloadSize protocol.ByteCount 72 73 MaxUniStreamNum protocol.StreamNum 74 MaxBidiStreamNum protocol.StreamNum 75 76 MaxIdleTimeout time.Duration 77 78 PreferredAddress *PreferredAddress 79 80 OriginalDestinationConnectionID protocol.ConnectionID 81 InitialSourceConnectionID protocol.ConnectionID 82 RetrySourceConnectionID *protocol.ConnectionID // use a pointer here to distinguish zero-length connection IDs from missing transport parameters 83 84 StatelessResetToken *protocol.StatelessResetToken 85 ActiveConnectionIDLimit uint64 86 87 MaxDatagramFrameSize protocol.ByteCount 88 } 89 90 // Unmarshal the transport parameters 91 func (p *TransportParameters) Unmarshal(data []byte, sentBy protocol.Perspective) error { 92 if err := p.unmarshal(bytes.NewReader(data), sentBy, false); err != nil { 93 return &qerr.TransportError{ 94 ErrorCode: qerr.TransportParameterError, 95 ErrorMessage: err.Error(), 96 } 97 } 98 return nil 99 } 100 101 func (p *TransportParameters) unmarshal(r *bytes.Reader, sentBy protocol.Perspective, fromSessionTicket bool) error { 102 // needed to check that every parameter is only sent at most once 103 var parameterIDs []transportParameterID 104 105 var ( 106 readOriginalDestinationConnectionID bool 107 readInitialSourceConnectionID bool 108 readActiveConnectionIDLimit bool 109 ) 110 111 p.AckDelayExponent = protocol.DefaultAckDelayExponent 112 p.MaxAckDelay = protocol.DefaultMaxAckDelay 113 p.MaxDatagramFrameSize = protocol.InvalidByteCount 114 115 for r.Len() > 0 { 116 paramIDInt, err := quicvarint.Read(r) 117 if err != nil { 118 return err 119 } 120 paramID := transportParameterID(paramIDInt) 121 paramLen, err := quicvarint.Read(r) 122 if err != nil { 123 return err 124 } 125 if uint64(r.Len()) < paramLen { 126 return fmt.Errorf("remaining length (%d) smaller than parameter length (%d)", r.Len(), paramLen) 127 } 128 parameterIDs = append(parameterIDs, paramID) 129 switch paramID { 130 case activeConnectionIDLimitParameterID: 131 readActiveConnectionIDLimit = true 132 fallthrough 133 case maxIdleTimeoutParameterID, 134 maxUDPPayloadSizeParameterID, 135 initialMaxDataParameterID, 136 initialMaxStreamDataBidiLocalParameterID, 137 initialMaxStreamDataBidiRemoteParameterID, 138 initialMaxStreamDataUniParameterID, 139 initialMaxStreamsBidiParameterID, 140 initialMaxStreamsUniParameterID, 141 maxAckDelayParameterID, 142 maxDatagramFrameSizeParameterID, 143 ackDelayExponentParameterID: 144 if err := p.readNumericTransportParameter(r, paramID, int(paramLen)); err != nil { 145 return err 146 } 147 case preferredAddressParameterID: 148 if sentBy == protocol.PerspectiveClient { 149 return errors.New("client sent a preferred_address") 150 } 151 if err := p.readPreferredAddress(r, int(paramLen)); err != nil { 152 return err 153 } 154 case disableActiveMigrationParameterID: 155 if paramLen != 0 { 156 return fmt.Errorf("wrong length for disable_active_migration: %d (expected empty)", paramLen) 157 } 158 p.DisableActiveMigration = true 159 case statelessResetTokenParameterID: 160 if sentBy == protocol.PerspectiveClient { 161 return errors.New("client sent a stateless_reset_token") 162 } 163 if paramLen != 16 { 164 return fmt.Errorf("wrong length for stateless_reset_token: %d (expected 16)", paramLen) 165 } 166 var token protocol.StatelessResetToken 167 r.Read(token[:]) 168 p.StatelessResetToken = &token 169 case originalDestinationConnectionIDParameterID: 170 if sentBy == protocol.PerspectiveClient { 171 return errors.New("client sent an original_destination_connection_id") 172 } 173 p.OriginalDestinationConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) 174 readOriginalDestinationConnectionID = true 175 case initialSourceConnectionIDParameterID: 176 p.InitialSourceConnectionID, _ = protocol.ReadConnectionID(r, int(paramLen)) 177 readInitialSourceConnectionID = true 178 case retrySourceConnectionIDParameterID: 179 if sentBy == protocol.PerspectiveClient { 180 return errors.New("client sent a retry_source_connection_id") 181 } 182 connID, _ := protocol.ReadConnectionID(r, int(paramLen)) 183 p.RetrySourceConnectionID = &connID 184 default: 185 r.Seek(int64(paramLen), io.SeekCurrent) 186 } 187 } 188 189 if !readActiveConnectionIDLimit { 190 p.ActiveConnectionIDLimit = protocol.DefaultActiveConnectionIDLimit 191 } 192 if !fromSessionTicket { 193 if sentBy == protocol.PerspectiveServer && !readOriginalDestinationConnectionID { 194 return errors.New("missing original_destination_connection_id") 195 } 196 if p.MaxUDPPayloadSize == 0 { 197 p.MaxUDPPayloadSize = protocol.MaxByteCount 198 } 199 if !readInitialSourceConnectionID { 200 return errors.New("missing initial_source_connection_id") 201 } 202 } 203 204 // check that every transport parameter was sent at most once 205 sort.Slice(parameterIDs, func(i, j int) bool { return parameterIDs[i] < parameterIDs[j] }) 206 for i := 0; i < len(parameterIDs)-1; i++ { 207 if parameterIDs[i] == parameterIDs[i+1] { 208 return fmt.Errorf("received duplicate transport parameter %#x", parameterIDs[i]) 209 } 210 } 211 212 return nil 213 } 214 215 func (p *TransportParameters) readPreferredAddress(r *bytes.Reader, expectedLen int) error { 216 remainingLen := r.Len() 217 pa := &PreferredAddress{} 218 var ipv4 [4]byte 219 if _, err := io.ReadFull(r, ipv4[:]); err != nil { 220 return err 221 } 222 port, err := utils.BigEndian.ReadUint16(r) 223 if err != nil { 224 return err 225 } 226 pa.IPv4 = netip.AddrPortFrom(netip.AddrFrom4(ipv4), port) 227 var ipv6 [16]byte 228 if _, err := io.ReadFull(r, ipv6[:]); err != nil { 229 return err 230 } 231 port, err = utils.BigEndian.ReadUint16(r) 232 if err != nil { 233 return err 234 } 235 pa.IPv6 = netip.AddrPortFrom(netip.AddrFrom16(ipv6), port) 236 connIDLen, err := r.ReadByte() 237 if err != nil { 238 return err 239 } 240 if connIDLen == 0 || connIDLen > protocol.MaxConnIDLen { 241 return fmt.Errorf("invalid connection ID length: %d", connIDLen) 242 } 243 connID, err := protocol.ReadConnectionID(r, int(connIDLen)) 244 if err != nil { 245 return err 246 } 247 pa.ConnectionID = connID 248 if _, err := io.ReadFull(r, pa.StatelessResetToken[:]); err != nil { 249 return err 250 } 251 if bytesRead := remainingLen - r.Len(); bytesRead != expectedLen { 252 return fmt.Errorf("expected preferred_address to be %d long, read %d bytes", expectedLen, bytesRead) 253 } 254 p.PreferredAddress = pa 255 return nil 256 } 257 258 func (p *TransportParameters) readNumericTransportParameter( 259 r *bytes.Reader, 260 paramID transportParameterID, 261 expectedLen int, 262 ) error { 263 remainingLen := r.Len() 264 val, err := quicvarint.Read(r) 265 if err != nil { 266 return fmt.Errorf("error while reading transport parameter %d: %s", paramID, err) 267 } 268 if remainingLen-r.Len() != expectedLen { 269 return fmt.Errorf("inconsistent transport parameter length for transport parameter %#x", paramID) 270 } 271 //nolint:exhaustive // This only covers the numeric transport parameters. 272 switch paramID { 273 case initialMaxStreamDataBidiLocalParameterID: 274 p.InitialMaxStreamDataBidiLocal = protocol.ByteCount(val) 275 case initialMaxStreamDataBidiRemoteParameterID: 276 p.InitialMaxStreamDataBidiRemote = protocol.ByteCount(val) 277 case initialMaxStreamDataUniParameterID: 278 p.InitialMaxStreamDataUni = protocol.ByteCount(val) 279 case initialMaxDataParameterID: 280 p.InitialMaxData = protocol.ByteCount(val) 281 case initialMaxStreamsBidiParameterID: 282 p.MaxBidiStreamNum = protocol.StreamNum(val) 283 if p.MaxBidiStreamNum > protocol.MaxStreamCount { 284 return fmt.Errorf("initial_max_streams_bidi too large: %d (maximum %d)", p.MaxBidiStreamNum, protocol.MaxStreamCount) 285 } 286 case initialMaxStreamsUniParameterID: 287 p.MaxUniStreamNum = protocol.StreamNum(val) 288 if p.MaxUniStreamNum > protocol.MaxStreamCount { 289 return fmt.Errorf("initial_max_streams_uni too large: %d (maximum %d)", p.MaxUniStreamNum, protocol.MaxStreamCount) 290 } 291 case maxIdleTimeoutParameterID: 292 p.MaxIdleTimeout = max(protocol.MinRemoteIdleTimeout, time.Duration(val)*time.Millisecond) 293 case maxUDPPayloadSizeParameterID: 294 if val < 1200 { 295 return fmt.Errorf("invalid value for max_packet_size: %d (minimum 1200)", val) 296 } 297 p.MaxUDPPayloadSize = protocol.ByteCount(val) 298 case ackDelayExponentParameterID: 299 if val > protocol.MaxAckDelayExponent { 300 return fmt.Errorf("invalid value for ack_delay_exponent: %d (maximum %d)", val, protocol.MaxAckDelayExponent) 301 } 302 p.AckDelayExponent = uint8(val) 303 case maxAckDelayParameterID: 304 if val > uint64(protocol.MaxMaxAckDelay/time.Millisecond) { 305 return fmt.Errorf("invalid value for max_ack_delay: %dms (maximum %dms)", val, protocol.MaxMaxAckDelay/time.Millisecond) 306 } 307 p.MaxAckDelay = time.Duration(val) * time.Millisecond 308 case activeConnectionIDLimitParameterID: 309 if val < 2 { 310 return fmt.Errorf("invalid value for active_connection_id_limit: %d (minimum 2)", val) 311 } 312 p.ActiveConnectionIDLimit = val 313 case maxDatagramFrameSizeParameterID: 314 p.MaxDatagramFrameSize = protocol.ByteCount(val) 315 default: 316 return fmt.Errorf("TransportParameter BUG: transport parameter %d not found", paramID) 317 } 318 return nil 319 } 320 321 // Marshal the transport parameters 322 func (p *TransportParameters) Marshal(pers protocol.Perspective) []byte { 323 // Typical Transport Parameters consume around 110 bytes, depending on the exact values, 324 // especially the lengths of the Connection IDs. 325 // Allocate 256 bytes, so we won't have to grow the slice in any case. 326 b := make([]byte, 0, 256) 327 328 // add a greased value 329 random := make([]byte, 18) 330 rand.Read(random) 331 b = quicvarint.Append(b, 27+31*uint64(random[0])) 332 length := random[1] % 16 333 b = quicvarint.Append(b, uint64(length)) 334 b = append(b, random[2:2+length]...) 335 336 // initial_max_stream_data_bidi_local 337 b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) 338 // initial_max_stream_data_bidi_remote 339 b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) 340 // initial_max_stream_data_uni 341 b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) 342 // initial_max_data 343 b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) 344 // initial_max_bidi_streams 345 b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) 346 // initial_max_uni_streams 347 b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) 348 // idle_timeout 349 b = p.marshalVarintParam(b, maxIdleTimeoutParameterID, uint64(p.MaxIdleTimeout/time.Millisecond)) 350 // max_packet_size 351 b = p.marshalVarintParam(b, maxUDPPayloadSizeParameterID, uint64(protocol.MaxPacketBufferSize)) 352 // max_ack_delay 353 // Only send it if is different from the default value. 354 if p.MaxAckDelay != protocol.DefaultMaxAckDelay { 355 b = p.marshalVarintParam(b, maxAckDelayParameterID, uint64(p.MaxAckDelay/time.Millisecond)) 356 } 357 // ack_delay_exponent 358 // Only send it if is different from the default value. 359 if p.AckDelayExponent != protocol.DefaultAckDelayExponent { 360 b = p.marshalVarintParam(b, ackDelayExponentParameterID, uint64(p.AckDelayExponent)) 361 } 362 // disable_active_migration 363 if p.DisableActiveMigration { 364 b = quicvarint.Append(b, uint64(disableActiveMigrationParameterID)) 365 b = quicvarint.Append(b, 0) 366 } 367 if pers == protocol.PerspectiveServer { 368 // stateless_reset_token 369 if p.StatelessResetToken != nil { 370 b = quicvarint.Append(b, uint64(statelessResetTokenParameterID)) 371 b = quicvarint.Append(b, 16) 372 b = append(b, p.StatelessResetToken[:]...) 373 } 374 // original_destination_connection_id 375 b = quicvarint.Append(b, uint64(originalDestinationConnectionIDParameterID)) 376 b = quicvarint.Append(b, uint64(p.OriginalDestinationConnectionID.Len())) 377 b = append(b, p.OriginalDestinationConnectionID.Bytes()...) 378 // preferred_address 379 if p.PreferredAddress != nil { 380 b = quicvarint.Append(b, uint64(preferredAddressParameterID)) 381 b = quicvarint.Append(b, 4+2+16+2+1+uint64(p.PreferredAddress.ConnectionID.Len())+16) 382 ip4 := p.PreferredAddress.IPv4.Addr().As4() 383 b = append(b, ip4[:]...) 384 b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv4.Port()) 385 ip6 := p.PreferredAddress.IPv6.Addr().As16() 386 b = append(b, ip6[:]...) 387 b = binary.BigEndian.AppendUint16(b, p.PreferredAddress.IPv6.Port()) 388 b = append(b, uint8(p.PreferredAddress.ConnectionID.Len())) 389 b = append(b, p.PreferredAddress.ConnectionID.Bytes()...) 390 b = append(b, p.PreferredAddress.StatelessResetToken[:]...) 391 } 392 } 393 // active_connection_id_limit 394 if p.ActiveConnectionIDLimit != protocol.DefaultActiveConnectionIDLimit { 395 b = p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) 396 } 397 // initial_source_connection_id 398 b = quicvarint.Append(b, uint64(initialSourceConnectionIDParameterID)) 399 b = quicvarint.Append(b, uint64(p.InitialSourceConnectionID.Len())) 400 b = append(b, p.InitialSourceConnectionID.Bytes()...) 401 // retry_source_connection_id 402 if pers == protocol.PerspectiveServer && p.RetrySourceConnectionID != nil { 403 b = quicvarint.Append(b, uint64(retrySourceConnectionIDParameterID)) 404 b = quicvarint.Append(b, uint64(p.RetrySourceConnectionID.Len())) 405 b = append(b, p.RetrySourceConnectionID.Bytes()...) 406 } 407 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 408 b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) 409 } 410 411 if pers == protocol.PerspectiveClient && len(AdditionalTransportParametersClient) > 0 { 412 for k, v := range AdditionalTransportParametersClient { 413 b = quicvarint.Append(b, k) 414 b = quicvarint.Append(b, uint64(len(v))) 415 b = append(b, v...) 416 } 417 } 418 419 return b 420 } 421 422 func (p *TransportParameters) marshalVarintParam(b []byte, id transportParameterID, val uint64) []byte { 423 b = quicvarint.Append(b, uint64(id)) 424 b = quicvarint.Append(b, uint64(quicvarint.Len(val))) 425 return quicvarint.Append(b, val) 426 } 427 428 // MarshalForSessionTicket marshals the transport parameters we save in the session ticket. 429 // When sending a 0-RTT enabled TLS session tickets, we need to save the transport parameters. 430 // The client will remember the transport parameters used in the last session, 431 // and apply those to the 0-RTT data it sends. 432 // Saving the transport parameters in the ticket gives the server the option to reject 0-RTT 433 // if the transport parameters changed. 434 // Since the session ticket is encrypted, the serialization format is defined by the server. 435 // For convenience, we use the same format that we also use for sending the transport parameters. 436 func (p *TransportParameters) MarshalForSessionTicket(b []byte) []byte { 437 b = quicvarint.Append(b, transportParameterMarshalingVersion) 438 439 // initial_max_stream_data_bidi_local 440 b = p.marshalVarintParam(b, initialMaxStreamDataBidiLocalParameterID, uint64(p.InitialMaxStreamDataBidiLocal)) 441 // initial_max_stream_data_bidi_remote 442 b = p.marshalVarintParam(b, initialMaxStreamDataBidiRemoteParameterID, uint64(p.InitialMaxStreamDataBidiRemote)) 443 // initial_max_stream_data_uni 444 b = p.marshalVarintParam(b, initialMaxStreamDataUniParameterID, uint64(p.InitialMaxStreamDataUni)) 445 // initial_max_data 446 b = p.marshalVarintParam(b, initialMaxDataParameterID, uint64(p.InitialMaxData)) 447 // initial_max_bidi_streams 448 b = p.marshalVarintParam(b, initialMaxStreamsBidiParameterID, uint64(p.MaxBidiStreamNum)) 449 // initial_max_uni_streams 450 b = p.marshalVarintParam(b, initialMaxStreamsUniParameterID, uint64(p.MaxUniStreamNum)) 451 // max_datagram_frame_size 452 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 453 b = p.marshalVarintParam(b, maxDatagramFrameSizeParameterID, uint64(p.MaxDatagramFrameSize)) 454 } 455 // active_connection_id_limit 456 return p.marshalVarintParam(b, activeConnectionIDLimitParameterID, p.ActiveConnectionIDLimit) 457 } 458 459 // UnmarshalFromSessionTicket unmarshals transport parameters from a session ticket. 460 func (p *TransportParameters) UnmarshalFromSessionTicket(r *bytes.Reader) error { 461 version, err := quicvarint.Read(r) 462 if err != nil { 463 return err 464 } 465 if version != transportParameterMarshalingVersion { 466 return fmt.Errorf("unknown transport parameter marshaling version: %d", version) 467 } 468 return p.unmarshal(r, protocol.PerspectiveServer, true) 469 } 470 471 // ValidFor0RTT checks if the transport parameters match those saved in the session ticket. 472 func (p *TransportParameters) ValidFor0RTT(saved *TransportParameters) bool { 473 if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { 474 return false 475 } 476 return p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && 477 p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && 478 p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && 479 p.InitialMaxData >= saved.InitialMaxData && 480 p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && 481 p.MaxUniStreamNum >= saved.MaxUniStreamNum && 482 p.ActiveConnectionIDLimit == saved.ActiveConnectionIDLimit 483 } 484 485 // ValidForUpdate checks that the new transport parameters don't reduce limits after resuming a 0-RTT connection. 486 // It is only used on the client side. 487 func (p *TransportParameters) ValidForUpdate(saved *TransportParameters) bool { 488 if saved.MaxDatagramFrameSize != protocol.InvalidByteCount && (p.MaxDatagramFrameSize == protocol.InvalidByteCount || p.MaxDatagramFrameSize < saved.MaxDatagramFrameSize) { 489 return false 490 } 491 return p.ActiveConnectionIDLimit >= saved.ActiveConnectionIDLimit && 492 p.InitialMaxData >= saved.InitialMaxData && 493 p.InitialMaxStreamDataBidiLocal >= saved.InitialMaxStreamDataBidiLocal && 494 p.InitialMaxStreamDataBidiRemote >= saved.InitialMaxStreamDataBidiRemote && 495 p.InitialMaxStreamDataUni >= saved.InitialMaxStreamDataUni && 496 p.MaxBidiStreamNum >= saved.MaxBidiStreamNum && 497 p.MaxUniStreamNum >= saved.MaxUniStreamNum 498 } 499 500 // String returns a string representation, intended for logging. 501 func (p *TransportParameters) String() string { 502 logString := "&wire.TransportParameters{OriginalDestinationConnectionID: %s, InitialSourceConnectionID: %s, " 503 logParams := []interface{}{p.OriginalDestinationConnectionID, p.InitialSourceConnectionID} 504 if p.RetrySourceConnectionID != nil { 505 logString += "RetrySourceConnectionID: %s, " 506 logParams = append(logParams, p.RetrySourceConnectionID) 507 } 508 logString += "InitialMaxStreamDataBidiLocal: %d, InitialMaxStreamDataBidiRemote: %d, InitialMaxStreamDataUni: %d, InitialMaxData: %d, MaxBidiStreamNum: %d, MaxUniStreamNum: %d, MaxIdleTimeout: %s, AckDelayExponent: %d, MaxAckDelay: %s, ActiveConnectionIDLimit: %d" 509 logParams = append(logParams, []interface{}{p.InitialMaxStreamDataBidiLocal, p.InitialMaxStreamDataBidiRemote, p.InitialMaxStreamDataUni, p.InitialMaxData, p.MaxBidiStreamNum, p.MaxUniStreamNum, p.MaxIdleTimeout, p.AckDelayExponent, p.MaxAckDelay, p.ActiveConnectionIDLimit}...) 510 if p.StatelessResetToken != nil { // the client never sends a stateless reset token 511 logString += ", StatelessResetToken: %#x" 512 logParams = append(logParams, *p.StatelessResetToken) 513 } 514 if p.MaxDatagramFrameSize != protocol.InvalidByteCount { 515 logString += ", MaxDatagramFrameSize: %d" 516 logParams = append(logParams, p.MaxDatagramFrameSize) 517 } 518 logString += "}" 519 return fmt.Sprintf(logString, logParams...) 520 }