diff --git a/Changelog.md b/Changelog.md index f009adce..666f2a77 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,7 @@ ## v0.6.0 (unreleased) +- Added `quic.Config` options for maximal flow control windows - Add a `quic.Config` option for QUIC versions - Add a `quic.Config` option to request truncation of the connection ID from a server - Add a `quic.Config` option to configure the source address validation diff --git a/client.go b/client.go index 3bf686f7..aeadee75 100644 --- a/client.go +++ b/client.go @@ -9,9 +9,9 @@ import ( "sync" "time" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" ) type client struct { @@ -123,11 +123,22 @@ func populateClientConfig(config *Config) *Config { handshakeTimeout = config.HandshakeTimeout } + maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow + if maxReceiveStreamFlowControlWindow == 0 { + maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowClient + } + maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow + if maxReceiveConnectionFlowControlWindow == 0 { + maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowClient + } + return &Config{ - TLSConfig: config.TLSConfig, - Versions: versions, - HandshakeTimeout: handshakeTimeout, - RequestConnectionIDTruncation: config.RequestConnectionIDTruncation, + TLSConfig: config.TLSConfig, + Versions: versions, + HandshakeTimeout: handshakeTimeout, + RequestConnectionIDTruncation: config.RequestConnectionIDTruncation, + MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow, + MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow, } } diff --git a/handshake/connection_parameters_manager.go b/handshake/connection_parameters_manager.go index 1127471b..1ad9a3a4 100644 --- a/handshake/connection_parameters_manager.go +++ b/handshake/connection_parameters_manager.go @@ -5,9 +5,9 @@ import ( "sync" "time" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" ) // ConnectionParametersManager negotiates and stores the connection parameters @@ -50,6 +50,8 @@ type connectionParametersManager struct { sendConnectionFlowControlWindow protocol.ByteCount receiveStreamFlowControlWindow protocol.ByteCount receiveConnectionFlowControlWindow protocol.ByteCount + maxReceiveStreamFlowControlWindow protocol.ByteCount + maxReceiveConnectionFlowControlWindow protocol.ByteCount } var _ ConnectionParametersManager = &connectionParametersManager{} @@ -61,14 +63,19 @@ var ( ) // NewConnectionParamatersManager creates a new connection parameters manager -func NewConnectionParamatersManager(pers protocol.Perspective, v protocol.VersionNumber) ConnectionParametersManager { +func NewConnectionParamatersManager( + pers protocol.Perspective, v protocol.VersionNumber, + maxReceiveStreamFlowControlWindow protocol.ByteCount, maxReceiveConnectionFlowControlWindow protocol.ByteCount, +) ConnectionParametersManager { h := &connectionParametersManager{ - perspective: pers, - version: v, - sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client - sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client - receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow, - receiveConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow, + perspective: pers, + version: v, + sendStreamFlowControlWindow: protocol.InitialStreamFlowControlWindow, // can only be changed by the client + sendConnectionFlowControlWindow: protocol.InitialConnectionFlowControlWindow, // can only be changed by the client + receiveStreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow, + receiveConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow, + maxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow, + maxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow, } if h.perspective == protocol.PerspectiveServer { @@ -207,10 +214,7 @@ func (h *connectionParametersManager) GetReceiveStreamFlowControlWindow() protoc // GetMaxReceiveStreamFlowControlWindow gets the maximum size of the stream-level flow control window for sending data func (h *connectionParametersManager) GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount { - if h.perspective == protocol.PerspectiveServer { - return protocol.MaxReceiveStreamFlowControlWindowServer - } - return protocol.MaxReceiveStreamFlowControlWindowClient + return h.maxReceiveStreamFlowControlWindow } // GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data @@ -222,10 +226,7 @@ func (h *connectionParametersManager) GetReceiveConnectionFlowControlWindow() pr // GetMaxReceiveConnectionFlowControlWindow gets the maximum size of the stream-level flow control window for sending data func (h *connectionParametersManager) GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount { - if h.perspective == protocol.PerspectiveServer { - return protocol.MaxReceiveConnectionFlowControlWindowServer - } - return protocol.MaxReceiveConnectionFlowControlWindowClient + return h.maxReceiveConnectionFlowControlWindow } // GetMaxOutgoingStreams gets the maximum number of outgoing streams per connection diff --git a/handshake/connection_parameters_manager_test.go b/handshake/connection_parameters_manager_test.go index 425a9278..99425143 100644 --- a/handshake/connection_parameters_manager_test.go +++ b/handshake/connection_parameters_manager_test.go @@ -2,6 +2,7 @@ package handshake import ( "encoding/binary" + "math" "time" "github.com/lucas-clemente/quic-go/protocol" @@ -12,10 +13,18 @@ import ( var _ = Describe("ConnectionsParameterManager", func() { var cpm *connectionParametersManager // a connectionParametersManager for a server var cpmClient *connectionParametersManager - + const MB = 1 << 20 + maxReceiveStreamFlowControlWindowServer := protocol.ByteCount(math.Floor(1.1 * MB)) // default is 1 MB + maxReceiveConnectionFlowControlWindowServer := protocol.ByteCount(math.Floor(1.5 * MB)) // default is 1.5 MB + maxReceiveStreamFlowControlWindowClient := protocol.ByteCount(math.Floor(6.4 * MB)) // default is 6 MB + maxReceiveConnectionFlowControlWindowClient := protocol.ByteCount(math.Floor(13 * MB)) // default is 15 MB BeforeEach(func() { - cpm = NewConnectionParamatersManager(protocol.PerspectiveServer, protocol.Version36).(*connectionParametersManager) - cpmClient = NewConnectionParamatersManager(protocol.PerspectiveClient, protocol.Version36).(*connectionParametersManager) + cpm = NewConnectionParamatersManager(protocol.PerspectiveServer, protocol.Version36, + maxReceiveStreamFlowControlWindowServer, maxReceiveConnectionFlowControlWindowServer, + ).(*connectionParametersManager) + cpmClient = NewConnectionParamatersManager(protocol.PerspectiveClient, protocol.Version36, + maxReceiveStreamFlowControlWindowClient, maxReceiveConnectionFlowControlWindowClient, + ).(*connectionParametersManager) }) Context("SHLO", func() { @@ -137,10 +146,10 @@ var _ = Describe("ConnectionsParameterManager", func() { }) It("has the correct maximum flow control windows", func() { - Expect(cpm.GetMaxReceiveStreamFlowControlWindow()).To(Equal(protocol.MaxReceiveStreamFlowControlWindowServer)) - Expect(cpm.GetMaxReceiveConnectionFlowControlWindow()).To(Equal(protocol.MaxReceiveConnectionFlowControlWindowServer)) - Expect(cpmClient.GetMaxReceiveStreamFlowControlWindow()).To(Equal(protocol.MaxReceiveStreamFlowControlWindowClient)) - Expect(cpmClient.GetMaxReceiveConnectionFlowControlWindow()).To(Equal(protocol.MaxReceiveConnectionFlowControlWindowClient)) + Expect(cpm.GetMaxReceiveStreamFlowControlWindow()).To(Equal(maxReceiveStreamFlowControlWindowServer)) + Expect(cpm.GetMaxReceiveConnectionFlowControlWindow()).To(Equal(maxReceiveConnectionFlowControlWindowServer)) + Expect(cpmClient.GetMaxReceiveStreamFlowControlWindow()).To(Equal(maxReceiveStreamFlowControlWindowClient)) + Expect(cpmClient.GetMaxReceiveConnectionFlowControlWindow()).To(Equal(maxReceiveConnectionFlowControlWindowClient)) }) It("sets a new stream-level flow control window for sending", func() { diff --git a/handshake/crypto_setup_client_test.go b/handshake/crypto_setup_client_test.go index 31053694..a4705957 100644 --- a/handshake/crypto_setup_client_test.go +++ b/handshake/crypto_setup_client_test.go @@ -8,9 +8,9 @@ import ( "time" "github.com/lucas-clemente/quic-go/crypto" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" ) @@ -111,7 +111,9 @@ var _ = Describe("Client Crypto Setup", func() { version, stream, nil, - NewConnectionParamatersManager(protocol.PerspectiveClient, version), + NewConnectionParamatersManager(protocol.PerspectiveClient, version, + protocol.DefaultMaxReceiveStreamFlowControlWindowClient, protocol.DefaultMaxReceiveConnectionFlowControlWindowClient, + ), aeadChanged, &TransportParameters{}, nil, diff --git a/handshake/crypto_setup_server_test.go b/handshake/crypto_setup_server_test.go index c326de50..7e2aeb88 100644 --- a/handshake/crypto_setup_server_test.go +++ b/handshake/crypto_setup_server_test.go @@ -7,9 +7,9 @@ import ( "net" "github.com/lucas-clemente/quic-go/crypto" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -184,7 +184,9 @@ var _ = Describe("Server Crypto Setup", func() { Expect(err).NotTo(HaveOccurred()) version = protocol.SupportedVersions[len(protocol.SupportedVersions)-1] supportedVersions = []protocol.VersionNumber{version, 98, 99} - cpm = NewConnectionParamatersManager(protocol.PerspectiveServer, protocol.VersionWhatever) + cpm = NewConnectionParamatersManager(protocol.PerspectiveServer, protocol.VersionWhatever, + protocol.DefaultMaxReceiveStreamFlowControlWindowServer, protocol.DefaultMaxReceiveConnectionFlowControlWindowServer, + ) csInt, err := NewCryptoSetup( protocol.ConnectionID(42), remoteAddr, diff --git a/interface.go b/interface.go index 75c55de1..20ee6cf0 100644 --- a/interface.go +++ b/interface.go @@ -76,9 +76,15 @@ type Config struct { HandshakeTimeout time.Duration // AcceptSTK determines if an STK is accepted. // It is called with stk = nil if the client didn't send an STK. - // If not set, it verifies that the address matches, and that the STK was issued within the last 24 hours + // If not set, it verifies that the address matches, and that the STK was issued within the last 24 hours. // This option is only valid for the server. AcceptSTK func(clientAddr net.Addr, stk *STK) bool + // MaxReceiveStreamFlowControlWindow is the maximum stream-level flow control window for receiving data. + // If this value is zero, it will default to 1 MB for the server and 6 MB for the client. + MaxReceiveStreamFlowControlWindow protocol.ByteCount + // MaxReceiveConnectionFlowControlWindow is the connection-level flow control window for receiving data. + // If this value is zero, it will default to 1.5 MB for the server and 15 MB for the client. + MaxReceiveConnectionFlowControlWindow protocol.ByteCount } // A Listener for incoming QUIC connections diff --git a/protocol/server_parameters.go b/protocol/server_parameters.go index 832d3879..8e632cc1 100644 --- a/protocol/server_parameters.go +++ b/protocol/server_parameters.go @@ -39,21 +39,21 @@ const ReceiveStreamFlowControlWindow ByteCount = (1 << 10) * 32 // 32 kB // This is the value that Google servers are using const ReceiveConnectionFlowControlWindow ByteCount = (1 << 10) * 48 // 48 kB -// MaxReceiveStreamFlowControlWindowServer is the maximum stream-level flow control window for receiving data +// DefaultMaxReceiveStreamFlowControlWindowServer is the default maximum stream-level flow control window for receiving data, for the server // This is the value that Google servers are using -const MaxReceiveStreamFlowControlWindowServer ByteCount = 1 * (1 << 20) // 1 MB +const DefaultMaxReceiveStreamFlowControlWindowServer ByteCount = 1 * (1 << 20) // 1 MB -// MaxReceiveConnectionFlowControlWindowServer is the connection-level flow control window for receiving data +// DefaultMaxReceiveConnectionFlowControlWindowServer is the default connection-level flow control window for receiving data, for the server // This is the value that Google servers are using -const MaxReceiveConnectionFlowControlWindowServer ByteCount = 1.5 * (1 << 20) // 1.5 MB +const DefaultMaxReceiveConnectionFlowControlWindowServer ByteCount = 1.5 * (1 << 20) // 1.5 MB -// MaxReceiveStreamFlowControlWindowClient is the maximum stream-level flow control window for receiving data, for the client +// DefaultMaxReceiveStreamFlowControlWindowClient is the default maximum stream-level flow control window for receiving data, for the client // This is the value that Chromium is using -const MaxReceiveStreamFlowControlWindowClient ByteCount = 6 * (1 << 20) // 6 MB +const DefaultMaxReceiveStreamFlowControlWindowClient ByteCount = 6 * (1 << 20) // 6 MB -// MaxReceiveConnectionFlowControlWindowClient is the connection-level flow control window for receiving data, for the server +// DefaultMaxReceiveConnectionFlowControlWindowClient is the default connection-level flow control window for receiving data, for the client // This is the value that Google servers are using -const MaxReceiveConnectionFlowControlWindowClient ByteCount = 15 * (1 << 20) // 15 MB +const DefaultMaxReceiveConnectionFlowControlWindowClient ByteCount = 15 * (1 << 20) // 15 MB // ConnectionFlowControlMultiplier determines how much larger the connection flow control windows needs to be relative to any stream's flow control window // This is the value that Chromium is using diff --git a/server.go b/server.go index c373e99a..5048df5f 100644 --- a/server.go +++ b/server.go @@ -9,9 +9,9 @@ import ( "github.com/lucas-clemente/quic-go/crypto" "github.com/lucas-clemente/quic-go/handshake" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" ) // packetHandler handles packets @@ -117,11 +117,22 @@ func populateServerConfig(config *Config) *Config { handshakeTimeout = config.HandshakeTimeout } + maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow + if maxReceiveStreamFlowControlWindow == 0 { + maxReceiveStreamFlowControlWindow = protocol.DefaultMaxReceiveStreamFlowControlWindowServer + } + maxReceiveConnectionFlowControlWindow := config.MaxReceiveConnectionFlowControlWindow + if maxReceiveConnectionFlowControlWindow == 0 { + maxReceiveConnectionFlowControlWindow = protocol.DefaultMaxReceiveConnectionFlowControlWindowServer + } + return &Config{ - TLSConfig: config.TLSConfig, - Versions: versions, - HandshakeTimeout: handshakeTimeout, - AcceptSTK: vsa, + TLSConfig: config.TLSConfig, + Versions: versions, + HandshakeTimeout: handshakeTimeout, + AcceptSTK: vsa, + MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow, + MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow, } } diff --git a/session.go b/session.go index a11bfbb8..c3859a97 100644 --- a/session.go +++ b/session.go @@ -12,9 +12,9 @@ import ( "github.com/lucas-clemente/quic-go/flowcontrol" "github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/handshake" + "github.com/lucas-clemente/quic-go/internal/utils" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" - "github.com/lucas-clemente/quic-go/internal/utils" ) type unpacker interface { @@ -172,7 +172,8 @@ func (s *session) setup( s.sessionCreationTime = now s.rttStats = &congestion.RTTStats{} - s.connectionParameters = handshake.NewConnectionParamatersManager(s.perspective, s.version) + s.connectionParameters = handshake.NewConnectionParamatersManager(s.perspective, s.version, + s.config.MaxReceiveStreamFlowControlWindow, s.config.MaxReceiveConnectionFlowControlWindow) s.sentPacketHandler = ackhandler.NewSentPacketHandler(s.rttStats) s.flowControlManager = flowcontrol.NewFlowControlManager(s.connectionParameters, s.rttStats) s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.ackAlarmChanged)