From c3832965d040a609a1f4ae729713f762916753cc Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Thu, 18 Aug 2016 16:13:00 +0200 Subject: [PATCH 1/3] add max incoming dynamic streams to connection parameter mgr ref #281 --- handshake/connection_parameters_manager.go | 9 +++------ handshake/connection_parameters_manager_test.go | 7 +++++++ handshake/tags.go | 2 ++ protocol/server_parameters.go | 8 +++++++- 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/handshake/connection_parameters_manager.go b/handshake/connection_parameters_manager.go index 87d42b03a..f1e775f7f 100644 --- a/handshake/connection_parameters_manager.go +++ b/handshake/connection_parameters_manager.go @@ -130,12 +130,15 @@ func (h *ConnectionParametersManager) GetSHLOMap() map[Tag][]byte { utils.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow())) mspc := bytes.NewBuffer([]byte{}) utils.WriteUint32(mspc, h.GetMaxStreamsPerConnection()) + mids := bytes.NewBuffer([]byte{}) + utils.WriteUint32(mids, protocol.MaxIncomingDynamicStreams) icsl := bytes.NewBuffer([]byte{}) utils.WriteUint32(icsl, uint32(h.GetIdleConnectionStateLifetime()/time.Second)) return map[Tag][]byte{ TagICSL: icsl.Bytes(), TagMSPC: mspc.Bytes(), + TagMIDS: mids.Bytes(), TagCFCW: cfcw.Bytes(), TagSFCW: sfcw.Bytes(), } @@ -145,7 +148,6 @@ func (h *ConnectionParametersManager) GetSHLOMap() map[Tag][]byte { func (h *ConnectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount { h.mutex.RLock() defer h.mutex.RUnlock() - return h.sendStreamFlowControlWindow } @@ -153,7 +155,6 @@ func (h *ConnectionParametersManager) GetSendStreamFlowControlWindow() protocol. func (h *ConnectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount { h.mutex.RLock() defer h.mutex.RUnlock() - return h.sendConnectionFlowControlWindow } @@ -161,7 +162,6 @@ func (h *ConnectionParametersManager) GetSendConnectionFlowControlWindow() proto func (h *ConnectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount { h.mutex.RLock() defer h.mutex.RUnlock() - return h.receiveStreamFlowControlWindow } @@ -169,7 +169,6 @@ func (h *ConnectionParametersManager) GetReceiveStreamFlowControlWindow() protoc func (h *ConnectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount { h.mutex.RLock() defer h.mutex.RUnlock() - return h.receiveConnectionFlowControlWindow } @@ -177,7 +176,6 @@ func (h *ConnectionParametersManager) GetReceiveConnectionFlowControlWindow() pr func (h *ConnectionParametersManager) GetMaxStreamsPerConnection() uint32 { h.mutex.RLock() defer h.mutex.RUnlock() - return h.maxStreamsPerConnection } @@ -185,7 +183,6 @@ func (h *ConnectionParametersManager) GetMaxStreamsPerConnection() uint32 { func (h *ConnectionParametersManager) GetIdleConnectionStateLifetime() time.Duration { h.mutex.RLock() defer h.mutex.RUnlock() - return h.idleConnectionStateLifetime } diff --git a/handshake/connection_parameters_manager_test.go b/handshake/connection_parameters_manager_test.go index e9249afde..ba3363e34 100644 --- a/handshake/connection_parameters_manager_test.go +++ b/handshake/connection_parameters_manager_test.go @@ -37,6 +37,7 @@ var _ = Describe("ConnectionsParameterManager", func() { entryMap := cpm.GetSHLOMap() Expect(entryMap).To(HaveKey(TagICSL)) Expect(entryMap).To(HaveKey(TagMSPC)) + Expect(entryMap).To(HaveKey(TagMIDS)) }) It("sets the stream-level flow control windows in SHLO", func() { @@ -66,6 +67,12 @@ var _ = Describe("ConnectionsParameterManager", func() { Expect(entryMap).To(HaveKey(TagMSPC)) Expect(entryMap[TagMSPC]).To(Equal([]byte{0xEF, 0xBE, 0xAD, 0xDE})) }) + + It("sets the maximum incoming dynamic streams per connection in SHLO", func() { + entryMap := cpm.GetSHLOMap() + Expect(entryMap).To(HaveKey(TagMIDS)) + Expect(entryMap[TagMIDS]).To(Equal([]byte{100, 0, 0, 0})) + }) }) Context("Truncated connection IDs", func() { diff --git a/handshake/tags.go b/handshake/tags.go index ed96c407e..cdcc14d5f 100644 --- a/handshake/tags.go +++ b/handshake/tags.go @@ -23,6 +23,8 @@ const ( TagCCRT Tag = 'C' + 'C'<<8 + 'R'<<16 + 'T'<<24 // TagMSPC is max streams per connection TagMSPC Tag = 'M' + 'S'<<8 + 'P'<<16 + 'C'<<24 + // TagMIDS is max incoming dyanamic streams + TagMIDS Tag = 'M' + 'I'<<8 + 'D'<<16 + 'S'<<24 // TagUAID is the user agent ID TagUAID Tag = 'U' + 'A'<<8 + 'I'<<16 + 'D'<<24 // TagTCID is truncation of the connection ID diff --git a/protocol/server_parameters.go b/protocol/server_parameters.go index 3617aedf7..b4aae4bed 100644 --- a/protocol/server_parameters.go +++ b/protocol/server_parameters.go @@ -31,9 +31,15 @@ const ReceiveConnectionFlowControlWindow ByteCount = (1 << 20) * 1.5 // 1.5 MB // MaxStreamsPerConnection is the maximum value accepted for the number of streams per connection const MaxStreamsPerConnection uint32 = 100 -// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. +// MaxIncomingDynamicStreams is the maximum value accepted for the incoming number of dynamic streams per connection +const MaxIncomingDynamicStreams uint32 = 100 + +// MaxStreamsMultiplier is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this procentual increase and the absolute increment specified by MaxStreamsMinimumIncrement is used. const MaxStreamsMultiplier = 1.1 +// MaxStreamsMinimumIncrement is the slack the client is allowed for the maximum number of streams per connection, needed e.g. when packets are out of order or dropped. The minimum of this absolute increment and the procentual increase specified by MaxStreamsMultiplier is used. +const MaxStreamsMinimumIncrement = 10 + // MaxIdleConnectionStateLifetime is the maximum value accepted for the idle connection state lifetime // TODO: set a reasonable value here const MaxIdleConnectionStateLifetime = 60 * time.Second From 993dd2cd5ff5da726e3d0aea5106b09cb019dee0 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Thu, 18 Aug 2016 16:40:30 +0200 Subject: [PATCH 2/3] improve max dynamic streams calculation in streamsMap --- streams_map.go | 25 +++++++++++++------------ streams_map_test.go | 6 +++--- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/streams_map.go b/streams_map.go index 05ce1a390..0bb2a200f 100644 --- a/streams_map.go +++ b/streams_map.go @@ -7,17 +7,15 @@ import ( "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/qerr" -) - -const ( - maxNumStreams = int(float32(protocol.MaxStreamsPerConnection) * protocol.MaxStreamsMultiplier) + "github.com/lucas-clemente/quic-go/utils" ) type streamsMap struct { - streams map[protocol.StreamID]*stream - openStreams []protocol.StreamID - mutex sync.RWMutex - newStream newStreamLambda + streams map[protocol.StreamID]*stream + openStreams []protocol.StreamID + mutex sync.RWMutex + newStream newStreamLambda + maxNumStreams int roundRobinIndex int } @@ -30,10 +28,13 @@ var ( ) func newStreamsMap(newStream newStreamLambda) *streamsMap { + maxNumStreams := utils.Max(int(float32(protocol.MaxIncomingDynamicStreams)*protocol.MaxStreamsMultiplier), int(protocol.MaxIncomingDynamicStreams)) + return &streamsMap{ - streams: map[protocol.StreamID]*stream{}, - openStreams: make([]protocol.StreamID, 0, maxNumStreams), - newStream: newStream, + streams: map[protocol.StreamID]*stream{}, + openStreams: make([]protocol.StreamID, 0, maxNumStreams), + newStream: newStream, + maxNumStreams: maxNumStreams, } } @@ -54,7 +55,7 @@ func (m *streamsMap) GetOrOpenStream(id protocol.StreamID) (*stream, error) { if ok { return s, nil } - if len(m.openStreams) == maxNumStreams { + if len(m.openStreams) == m.maxNumStreams { return nil, qerr.TooManyOpenStreams } if id%2 == 0 { diff --git a/streams_map_test.go b/streams_map_test.go index 4e04a559a..ddd772a7d 100644 --- a/streams_map_test.go +++ b/streams_map_test.go @@ -60,16 +60,16 @@ var _ = Describe("Streams Map", func() { Context("counting streams", func() { It("errors when too many streams are opened", func() { - for i := 0; i < maxNumStreams; i++ { + for i := 0; i < m.maxNumStreams; i++ { _, err := m.GetOrOpenStream(protocol.StreamID(i*2 + 1)) Expect(err).NotTo(HaveOccurred()) } - _, err := m.GetOrOpenStream(protocol.StreamID(maxNumStreams)) + _, err := m.GetOrOpenStream(protocol.StreamID(m.maxNumStreams)) Expect(err).To(MatchError(qerr.TooManyOpenStreams)) }) It("does not error when many streams are opened and closed", func() { - for i := 2; i < 10*maxNumStreams; i++ { + for i := 2; i < 10*m.maxNumStreams; i++ { _, err := m.GetOrOpenStream(protocol.StreamID(i*2 + 1)) Expect(err).NotTo(HaveOccurred()) m.RemoveStream(protocol.StreamID(i*2 + 1)) From 888a35c2fd1d78957665f8ea4d9db96500502cb7 Mon Sep 17 00:00:00 2001 From: Lucas Clemente Date: Thu, 18 Aug 2016 18:02:23 +0200 Subject: [PATCH 3/3] enable support for version 35 fixes #281 --- README.md | 2 +- h2quic/server_test.go | 2 +- protocol/version.go | 3 ++- protocol/version_test.go | 4 ++-- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index bd023610b..1af53e07f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ quic-go is an implementation of the [QUIC](https://en.wikipedia.org/wiki/QUIC) p Done: -- Basic protocol with support for QUIC version 32-34 +- Basic protocol with support for QUIC version 32-35 - HTTP/2 support - Crypto (RSA / ECDSA certificates, Curve25519 for key exchange, AES-GCM or Chacha20-Poly1305 as stream cipher) - Loss detection and retransmission (currently fast retransmission & RTO) diff --git a/h2quic/server_test.go b/h2quic/server_test.go index f1fd2a58d..c9eee5290 100644 --- a/h2quic/server_test.go +++ b/h2quic/server_test.go @@ -196,7 +196,7 @@ var _ = Describe("H2 server", func() { Context("setting http headers", func() { expected := http.Header{ - "Alt-Svc": {`quic=":443"; ma=2592000; v="34,33,32"`}, + "Alt-Svc": {`quic=":443"; ma=2592000; v="35,34,33,32"`}, "Alternate-Protocol": {`443:quic`}, } diff --git a/protocol/version.go b/protocol/version.go index 5c419d914..f41dd2062 100644 --- a/protocol/version.go +++ b/protocol/version.go @@ -14,12 +14,13 @@ const ( Version32 VersionNumber = 32 + iota Version33 Version34 + Version35 VersionWhatever = 0 // for when the version doesn't matter ) // SupportedVersions lists the versions that the server supports var SupportedVersions = []VersionNumber{ - Version32, Version33, Version34, + Version32, Version33, Version34, Version35, } // SupportedVersionsAsTags is needed for the SHLO crypto message diff --git a/protocol/version_test.go b/protocol/version_test.go index 34b71fb43..ae953620b 100644 --- a/protocol/version_test.go +++ b/protocol/version_test.go @@ -17,11 +17,11 @@ var _ = Describe("Version", func() { }) It("has proper tag list", func() { - Expect(SupportedVersionsAsTags).To(Equal([]byte("Q032Q033Q034"))) + Expect(SupportedVersionsAsTags).To(Equal([]byte("Q032Q033Q034Q035"))) }) It("has proper version list", func() { - Expect(SupportedVersionsAsString).To(Equal("34,33,32")) + Expect(SupportedVersionsAsString).To(Equal("35,34,33,32")) }) It("recognizes supported versions", func() {