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