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