add a quic.Config option to configure the idle timeout

This commit is contained in:
Marten Seemann 2017-08-22 20:24:19 +07:00
parent e398d5409a
commit 71e82677e1
12 changed files with 64 additions and 30 deletions

View file

@ -8,6 +8,7 @@
- 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
- Add a `quic.Config` option to configure the handshake timeout
- Add a `quic.Config` option to configure the idle timeout
- Add a `quic.Config` option to configure keep-alive
- Implement `net.Conn`-style deadlines for streams
- Remove the `tls.Config` from the `quic.Config`. The `tls.Config` must now be passed to the `Dial` and `Listen` functions as a separate parameter. See the [Godoc](https://godoc.org/github.com/lucas-clemente/quic-go) for details.

View file

@ -153,6 +153,10 @@ func populateClientConfig(config *Config) *Config {
if config.HandshakeTimeout != 0 {
handshakeTimeout = config.HandshakeTimeout
}
idleTimeout := protocol.DefaultIdleTimeout
if config.IdleTimeout != 0 {
idleTimeout = config.IdleTimeout
}
maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
if maxReceiveStreamFlowControlWindow == 0 {
@ -166,6 +170,7 @@ func populateClientConfig(config *Config) *Config {
return &Config{
Versions: versions,
HandshakeTimeout: handshakeTimeout,
IdleTimeout: idleTimeout,
RequestConnectionIDTruncation: config.RequestConnectionIDTruncation,
MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,

View file

@ -184,10 +184,12 @@ var _ = Describe("Client", func() {
It("setups with the right values", func() {
config := &Config{
HandshakeTimeout: 1337 * time.Minute,
IdleTimeout: 42 * time.Hour,
RequestConnectionIDTruncation: true,
}
c := populateClientConfig(config)
Expect(c.HandshakeTimeout).To(Equal(1337 * time.Minute))
Expect(c.IdleTimeout).To(Equal(42 * time.Hour))
Expect(c.RequestConnectionIDTruncation).To(BeTrue())
})
@ -195,6 +197,7 @@ var _ = Describe("Client", func() {
c := populateClientConfig(&Config{})
Expect(c.Versions).To(Equal(protocol.SupportedVersions))
Expect(c.HandshakeTimeout).To(Equal(protocol.DefaultHandshakeTimeout))
Expect(c.IdleTimeout).To(Equal(protocol.DefaultIdleTimeout))
Expect(c.RequestConnectionIDTruncation).To(BeFalse())
})

View file

@ -64,8 +64,11 @@ var (
// NewConnectionParamatersManager creates a new connection parameters manager
func NewConnectionParamatersManager(
pers protocol.Perspective, v protocol.VersionNumber,
maxReceiveStreamFlowControlWindow protocol.ByteCount, maxReceiveConnectionFlowControlWindow protocol.ByteCount,
pers protocol.Perspective,
v protocol.VersionNumber,
maxReceiveStreamFlowControlWindow protocol.ByteCount,
maxReceiveConnectionFlowControlWindow protocol.ByteCount,
idleTimeout time.Duration,
) ConnectionParametersManager {
h := &connectionParametersManager{
perspective: pers,
@ -78,12 +81,11 @@ func NewConnectionParamatersManager(
maxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,
}
h.idleConnectionStateLifetime = idleTimeout
if h.perspective == protocol.PerspectiveServer {
h.idleConnectionStateLifetime = protocol.DefaultIdleTimeout
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the client's perspective
} else {
h.idleConnectionStateLifetime = protocol.MaxIdleTimeoutClient
h.maxStreamsPerConnection = protocol.MaxStreamsPerConnection // this is the value negotiated based on what the client sent
h.maxIncomingDynamicStreamsPerConnection = protocol.MaxStreamsPerConnection // "incoming" seen from the server's perspective
}
@ -163,10 +165,7 @@ func (h *connectionParametersManager) negotiateMaxIncomingDynamicStreamsPerConne
}
func (h *connectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration {
if h.perspective == protocol.PerspectiveServer {
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutServer)
}
return utils.MinDuration(clientValue, protocol.MaxIdleTimeoutClient)
return utils.MinDuration(clientValue, h.idleConnectionStateLifetime)
}
// GetHelloMap gets all parameters needed for the Hello message

View file

@ -18,12 +18,21 @@ var _ = Describe("ConnectionsParameterManager", func() {
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
idleTimeout := 42 * time.Second
BeforeEach(func() {
cpm = NewConnectionParamatersManager(protocol.PerspectiveServer, protocol.Version36,
maxReceiveStreamFlowControlWindowServer, maxReceiveConnectionFlowControlWindowServer,
cpm = NewConnectionParamatersManager(
protocol.PerspectiveServer,
protocol.Version36,
maxReceiveStreamFlowControlWindowServer,
maxReceiveConnectionFlowControlWindowServer,
idleTimeout,
).(*connectionParametersManager)
cpmClient = NewConnectionParamatersManager(protocol.PerspectiveClient, protocol.Version36,
maxReceiveStreamFlowControlWindowClient, maxReceiveConnectionFlowControlWindowClient,
cpmClient = NewConnectionParamatersManager(
protocol.PerspectiveClient,
protocol.Version36,
maxReceiveStreamFlowControlWindowClient,
maxReceiveConnectionFlowControlWindowClient,
idleTimeout,
).(*connectionParametersManager)
})
@ -94,7 +103,7 @@ var _ = Describe("ConnectionsParameterManager", func() {
entryMap, err := cpmClient.GetHelloMap()
Expect(err).ToNot(HaveOccurred())
Expect(entryMap).To(HaveKey(TagICSL))
Expect(binary.LittleEndian.Uint32(entryMap[TagICSL])).To(BeEquivalentTo(protocol.MaxIdleTimeoutClient / time.Second))
Expect(binary.LittleEndian.Uint32(entryMap[TagICSL])).To(BeEquivalentTo(idleTimeout / time.Second))
Expect(entryMap).To(HaveKey(TagMSPC))
Expect(binary.LittleEndian.Uint32(entryMap[TagMSPC])).To(BeEquivalentTo(protocol.MaxStreamsPerConnection))
Expect(entryMap).To(HaveKey(TagMIDS))
@ -200,17 +209,15 @@ var _ = Describe("ConnectionsParameterManager", func() {
Context("idle connection state lifetime", func() {
It("has initial idle connection state lifetime", func() {
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(protocol.DefaultIdleTimeout))
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(idleTimeout))
})
It("negotiates correctly when the peer wants a longer lifetime", func() {
Expect(cpm.negotiateIdleConnectionStateLifetime(protocol.MaxIdleTimeoutServer + 10*time.Second)).To(Equal(protocol.MaxIdleTimeoutServer))
Expect(cpmClient.negotiateIdleConnectionStateLifetime(protocol.MaxIdleTimeoutClient + 10*time.Second)).To(Equal(protocol.MaxIdleTimeoutClient))
Expect(cpm.negotiateIdleConnectionStateLifetime(idleTimeout + 10*time.Second)).To(Equal(idleTimeout))
})
It("negotiates correctly when the peer wants a shorter lifetime", func() {
Expect(cpm.negotiateIdleConnectionStateLifetime(protocol.MaxIdleTimeoutServer - 1*time.Second)).To(Equal(protocol.MaxIdleTimeoutServer - 1*time.Second))
Expect(cpmClient.negotiateIdleConnectionStateLifetime(protocol.MaxIdleTimeoutClient - 1*time.Second)).To(Equal(protocol.MaxIdleTimeoutClient - 1*time.Second))
Expect(cpm.negotiateIdleConnectionStateLifetime(idleTimeout - 3*time.Second)).To(Equal(idleTimeout - 3*time.Second))
})
It("sets the negotiated lifetime", func() {
@ -229,7 +236,7 @@ var _ = Describe("ConnectionsParameterManager", func() {
}
err := cpm.SetFromMap(values)
Expect(err).To(MatchError(ErrMalformedTag))
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(protocol.DefaultIdleTimeout))
Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(idleTimeout))
})
It("gets idle connection state lifetime", func() {

View file

@ -111,8 +111,11 @@ var _ = Describe("Client Crypto Setup", func() {
version,
stream,
nil,
NewConnectionParamatersManager(protocol.PerspectiveClient, version,
NewConnectionParamatersManager(
protocol.PerspectiveClient,
version,
protocol.DefaultMaxReceiveStreamFlowControlWindowClient, protocol.DefaultMaxReceiveConnectionFlowControlWindowClient,
protocol.DefaultIdleTimeout,
),
aeadChanged,
&TransportParameters{},

View file

@ -184,8 +184,11 @@ 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,
protocol.DefaultIdleTimeout,
)
csInt, err := NewCryptoSetup(
protocol.ConnectionID(42),

View file

@ -100,6 +100,12 @@ type Config struct {
// If the timeout is exceeded, the connection is closed.
// If this value is zero, the timeout is set to 10 seconds.
HandshakeTimeout time.Duration
// IdleTimeout is the maximum duration that may pass without any incoming network activity.
// This value only applies after the handshake has completed.
// Before that, the idle timeout is set to half the duration of the HandshakeTimeout.
// If the timeout is exceeded, the connection is closed.
// If this value is zero, the timeout is set to 30 seconds.
IdleTimeout 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.

View file

@ -119,15 +119,9 @@ const CryptoParameterMaxLength = 4000
// EphermalKeyLifetime is the lifetime of the ephermal key during the handshake, see handshake.getEphermalKEX.
const EphermalKeyLifetime = time.Minute
// DefaultIdleTimeout is the default idle timeout, for the server
// DefaultIdleTimeout is the default idle timeout
const DefaultIdleTimeout = 30 * time.Second
// MaxIdleTimeoutServer is the maximum idle timeout that can be negotiated, for the server
const MaxIdleTimeoutServer = 1 * time.Minute
// MaxIdleTimeoutClient is the idle timeout that the client suggests to the server
const MaxIdleTimeoutClient = 2 * time.Minute
// DefaultHandshakeTimeout is the default timeout for a connection until the crypto handshake succeeds.
const DefaultHandshakeTimeout = 10 * time.Second

View file

@ -128,6 +128,10 @@ func populateServerConfig(config *Config) *Config {
if config.HandshakeTimeout != 0 {
handshakeTimeout = config.HandshakeTimeout
}
idleTimeout := protocol.DefaultIdleTimeout
if config.IdleTimeout != 0 {
idleTimeout = config.IdleTimeout
}
maxReceiveStreamFlowControlWindow := config.MaxReceiveStreamFlowControlWindow
if maxReceiveStreamFlowControlWindow == 0 {
@ -141,6 +145,7 @@ func populateServerConfig(config *Config) *Config {
return &Config{
Versions: versions,
HandshakeTimeout: handshakeTimeout,
IdleTimeout: idleTimeout,
AcceptSTK: vsa,
MaxReceiveStreamFlowControlWindow: maxReceiveStreamFlowControlWindow,
MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindow,

View file

@ -355,6 +355,7 @@ var _ = Describe("Server", func() {
Versions: supportedVersions,
AcceptSTK: acceptSTK,
HandshakeTimeout: 1337 * time.Hour,
IdleTimeout: 42 * time.Minute,
}
ln, err := Listen(conn, &tls.Config{}, &config)
Expect(err).ToNot(HaveOccurred())
@ -364,6 +365,7 @@ var _ = Describe("Server", func() {
Expect(server.scfg).ToNot(BeNil())
Expect(server.config.Versions).To(Equal(supportedVersions))
Expect(server.config.HandshakeTimeout).To(Equal(1337 * time.Hour))
Expect(server.config.IdleTimeout).To(Equal(42 * time.Minute))
Expect(reflect.ValueOf(server.config.AcceptSTK)).To(Equal(reflect.ValueOf(acceptSTK)))
})
@ -373,6 +375,7 @@ var _ = Describe("Server", func() {
server := ln.(*server)
Expect(server.config.Versions).To(Equal(protocol.SupportedVersions))
Expect(server.config.HandshakeTimeout).To(Equal(protocol.DefaultHandshakeTimeout))
Expect(server.config.IdleTimeout).To(Equal(protocol.DefaultIdleTimeout))
Expect(reflect.ValueOf(server.config.AcceptSTK)).To(Equal(reflect.ValueOf(defaultAcceptSTK)))
})

View file

@ -181,8 +181,13 @@ func (s *session) setup(
s.sessionCreationTime = now
s.rttStats = &congestion.RTTStats{}
s.connectionParameters = handshake.NewConnectionParamatersManager(s.perspective, s.version,
s.config.MaxReceiveStreamFlowControlWindow, s.config.MaxReceiveConnectionFlowControlWindow)
s.connectionParameters = handshake.NewConnectionParamatersManager(
s.perspective,
s.version,
s.config.MaxReceiveStreamFlowControlWindow,
s.config.MaxReceiveConnectionFlowControlWindow,
s.config.IdleTimeout,
)
s.sentPacketHandler = ackhandler.NewSentPacketHandler(s.rttStats)
s.flowControlManager = flowcontrol.NewFlowControlManager(s.connectionParameters, s.rttStats)
s.receivedPacketHandler = ackhandler.NewReceivedPacketHandler(s.version)