diff --git a/internal/handshake/base_connection_parameters_manager.go b/internal/handshake/base_connection_parameters_manager.go new file mode 100644 index 00000000..fc970e54 --- /dev/null +++ b/internal/handshake/base_connection_parameters_manager.go @@ -0,0 +1,149 @@ +package handshake + +import ( + "sync" + "time" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" +) + +// ConnectionParametersManager negotiates and stores the connection parameters. +// A ConnectionParametersManager can be used for a server as well as a client. +type ConnectionParametersManager interface { + GetSendStreamFlowControlWindow() protocol.ByteCount + GetSendConnectionFlowControlWindow() protocol.ByteCount + GetReceiveStreamFlowControlWindow() protocol.ByteCount + GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount + GetReceiveConnectionFlowControlWindow() protocol.ByteCount + GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount + GetMaxOutgoingStreams() uint32 + GetMaxIncomingStreams() uint32 + GetIdleConnectionStateLifetime() time.Duration + // determines if the client requests truncated ConnectionIDs. + // It always returns false for the server. + TruncateConnectionID() bool +} + +// For the server: +// 1. call SetFromMap with the values received in the CHLO. This sets the corresponding values here, subject to negotiation +// 2. call GetHelloMap to get the values to send in the SHLO +// For the client: +// 1. call GetHelloMap to get the values to send in a CHLO +// 2. call SetFromMap with the values received in the SHLO +type baseConnectionParametersManager struct { + mutex sync.RWMutex + + version protocol.VersionNumber + perspective protocol.Perspective + + flowControlNegotiated bool + + truncateConnectionID bool + maxStreamsPerConnection uint32 + maxIncomingDynamicStreamsPerConnection uint32 + idleConnectionStateLifetime time.Duration + sendStreamFlowControlWindow protocol.ByteCount + sendConnectionFlowControlWindow protocol.ByteCount + receiveStreamFlowControlWindow protocol.ByteCount + receiveConnectionFlowControlWindow protocol.ByteCount + maxReceiveStreamFlowControlWindow protocol.ByteCount + maxReceiveConnectionFlowControlWindow protocol.ByteCount +} + +func (h *baseConnectionParametersManager) init(params *TransportParameters) { + h.sendStreamFlowControlWindow = protocol.InitialStreamFlowControlWindow // can only be changed by the client + h.sendConnectionFlowControlWindow = protocol.InitialConnectionFlowControlWindow // can only be changed by the client + h.receiveStreamFlowControlWindow = protocol.ReceiveStreamFlowControlWindow + h.receiveConnectionFlowControlWindow = protocol.ReceiveConnectionFlowControlWindow + h.maxReceiveStreamFlowControlWindow = params.MaxReceiveStreamFlowControlWindow + h.maxReceiveConnectionFlowControlWindow = params.MaxReceiveConnectionFlowControlWindow + + h.idleConnectionStateLifetime = params.IdleTimeout + if h.perspective == protocol.PerspectiveServer { + 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.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 + } +} + +func (h *baseConnectionParametersManager) negotiateMaxStreamsPerConnection(clientValue uint32) uint32 { + return utils.MinUint32(clientValue, protocol.MaxStreamsPerConnection) +} + +func (h *baseConnectionParametersManager) negotiateMaxIncomingDynamicStreamsPerConnection(clientValue uint32) uint32 { + return utils.MinUint32(clientValue, protocol.MaxIncomingDynamicStreamsPerConnection) +} + +func (h *baseConnectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration { + return utils.MinDuration(clientValue, h.idleConnectionStateLifetime) +} + +// GetSendStreamFlowControlWindow gets the size of the stream-level flow control window for sending data +func (h *baseConnectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount { + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.sendStreamFlowControlWindow +} + +// GetSendConnectionFlowControlWindow gets the size of the stream-level flow control window for sending data +func (h *baseConnectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount { + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.sendConnectionFlowControlWindow +} + +func (h *baseConnectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount { + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.receiveStreamFlowControlWindow +} + +// GetMaxReceiveStreamFlowControlWindow gets the maximum size of the stream-level flow control window for sending data +func (h *baseConnectionParametersManager) GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount { + return h.maxReceiveStreamFlowControlWindow +} + +// GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data +func (h *baseConnectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount { + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.receiveConnectionFlowControlWindow +} + +func (h *baseConnectionParametersManager) GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount { + return h.maxReceiveConnectionFlowControlWindow +} + +func (h *baseConnectionParametersManager) GetMaxOutgoingStreams() uint32 { + h.mutex.RLock() + defer h.mutex.RUnlock() + + return h.maxIncomingDynamicStreamsPerConnection +} + +func (h *baseConnectionParametersManager) GetMaxIncomingStreams() uint32 { + h.mutex.RLock() + defer h.mutex.RUnlock() + + maxStreams := protocol.MaxIncomingDynamicStreamsPerConnection + return utils.MaxUint32(uint32(maxStreams)+protocol.MaxStreamsMinimumIncrement, uint32(float64(maxStreams)*protocol.MaxStreamsMultiplier)) +} + +func (h *baseConnectionParametersManager) GetIdleConnectionStateLifetime() time.Duration { + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.idleConnectionStateLifetime +} + +func (h *baseConnectionParametersManager) TruncateConnectionID() bool { + if h.perspective == protocol.PerspectiveClient { + return false + } + + h.mutex.RLock() + defer h.mutex.RUnlock() + return h.truncateConnectionID +} diff --git a/internal/handshake/connection_parameters_manager.go b/internal/handshake/connection_parameters_manager.go deleted file mode 100644 index d2098922..00000000 --- a/internal/handshake/connection_parameters_manager.go +++ /dev/null @@ -1,262 +0,0 @@ -package handshake - -import ( - "bytes" - "sync" - "time" - - "github.com/lucas-clemente/quic-go/internal/protocol" - "github.com/lucas-clemente/quic-go/internal/utils" - "github.com/lucas-clemente/quic-go/qerr" -) - -// ConnectionParametersManager negotiates and stores the connection parameters -// A ConnectionParametersManager can be used for a server as well as a client -// For the server: -// 1. call SetFromMap with the values received in the CHLO. This sets the corresponding values here, subject to negotiation -// 2. call GetHelloMap to get the values to send in the SHLO -// For the client: -// 1. call GetHelloMap to get the values to send in a CHLO -// 2. call SetFromMap with the values received in the SHLO -type ConnectionParametersManager interface { - SetFromMap(map[Tag][]byte) error - GetHelloMap() (map[Tag][]byte, error) - - GetSendStreamFlowControlWindow() protocol.ByteCount - GetSendConnectionFlowControlWindow() protocol.ByteCount - GetReceiveStreamFlowControlWindow() protocol.ByteCount - GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount - GetReceiveConnectionFlowControlWindow() protocol.ByteCount - GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount - GetMaxOutgoingStreams() uint32 - GetMaxIncomingStreams() uint32 - GetIdleConnectionStateLifetime() time.Duration - TruncateConnectionID() bool -} - -type connectionParametersManager struct { - mutex sync.RWMutex - - version protocol.VersionNumber - perspective protocol.Perspective - - flowControlNegotiated bool - - truncateConnectionID bool - maxStreamsPerConnection uint32 - maxIncomingDynamicStreamsPerConnection uint32 - idleConnectionStateLifetime time.Duration - sendStreamFlowControlWindow protocol.ByteCount - sendConnectionFlowControlWindow protocol.ByteCount - receiveStreamFlowControlWindow protocol.ByteCount - receiveConnectionFlowControlWindow protocol.ByteCount - maxReceiveStreamFlowControlWindow protocol.ByteCount - maxReceiveConnectionFlowControlWindow protocol.ByteCount -} - -var _ ConnectionParametersManager = &connectionParametersManager{} - -// ErrMalformedTag is returned when the tag value cannot be read -var ( - ErrMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value") - ErrFlowControlRenegotiationNotSupported = qerr.Error(qerr.InvalidCryptoMessageParameter, "renegotiation of flow control parameters not supported") -) - -// NewConnectionParamatersManager creates a new connection parameters manager -func NewConnectionParamatersManager( - pers protocol.Perspective, - v protocol.VersionNumber, - params *TransportParameters, -) 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, - maxReceiveStreamFlowControlWindow: params.MaxReceiveStreamFlowControlWindow, - maxReceiveConnectionFlowControlWindow: params.MaxReceiveConnectionFlowControlWindow, - } - - h.idleConnectionStateLifetime = params.IdleTimeout - if h.perspective == protocol.PerspectiveServer { - 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.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 - } - - return h -} - -// SetFromMap reads all params -func (h *connectionParametersManager) SetFromMap(params map[Tag][]byte) error { - h.mutex.Lock() - defer h.mutex.Unlock() - - if value, ok := params[TagTCID]; ok && h.perspective == protocol.PerspectiveServer { - clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.truncateConnectionID = (clientValue == 0) - } - if value, ok := params[TagMSPC]; ok { - clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.maxStreamsPerConnection = h.negotiateMaxStreamsPerConnection(clientValue) - } - if value, ok := params[TagMIDS]; ok { - clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.maxIncomingDynamicStreamsPerConnection = h.negotiateMaxIncomingDynamicStreamsPerConnection(clientValue) - } - if value, ok := params[TagICSL]; ok { - clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second) - } - if value, ok := params[TagSFCW]; ok { - if h.flowControlNegotiated { - return ErrFlowControlRenegotiationNotSupported - } - sendStreamFlowControlWindow, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.sendStreamFlowControlWindow = protocol.ByteCount(sendStreamFlowControlWindow) - } - if value, ok := params[TagCFCW]; ok { - if h.flowControlNegotiated { - return ErrFlowControlRenegotiationNotSupported - } - sendConnectionFlowControlWindow, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) - if err != nil { - return ErrMalformedTag - } - h.sendConnectionFlowControlWindow = protocol.ByteCount(sendConnectionFlowControlWindow) - } - - _, containsSFCW := params[TagSFCW] - _, containsCFCW := params[TagCFCW] - if containsCFCW || containsSFCW { - h.flowControlNegotiated = true - } - - return nil -} - -func (h *connectionParametersManager) negotiateMaxStreamsPerConnection(clientValue uint32) uint32 { - return utils.MinUint32(clientValue, protocol.MaxStreamsPerConnection) -} - -func (h *connectionParametersManager) negotiateMaxIncomingDynamicStreamsPerConnection(clientValue uint32) uint32 { - return utils.MinUint32(clientValue, protocol.MaxIncomingDynamicStreamsPerConnection) -} - -func (h *connectionParametersManager) negotiateIdleConnectionStateLifetime(clientValue time.Duration) time.Duration { - return utils.MinDuration(clientValue, h.idleConnectionStateLifetime) -} - -// GetHelloMap gets all parameters needed for the Hello message -func (h *connectionParametersManager) GetHelloMap() (map[Tag][]byte, error) { - sfcw := bytes.NewBuffer([]byte{}) - utils.LittleEndian.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow())) - cfcw := bytes.NewBuffer([]byte{}) - utils.LittleEndian.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow())) - mspc := bytes.NewBuffer([]byte{}) - utils.LittleEndian.WriteUint32(mspc, h.maxStreamsPerConnection) - mids := bytes.NewBuffer([]byte{}) - utils.LittleEndian.WriteUint32(mids, protocol.MaxIncomingDynamicStreamsPerConnection) - icsl := bytes.NewBuffer([]byte{}) - utils.LittleEndian.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(), - }, nil -} - -// GetSendStreamFlowControlWindow gets the size of the stream-level flow control window for sending data -func (h *connectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.sendStreamFlowControlWindow -} - -// GetSendConnectionFlowControlWindow gets the size of the stream-level flow control window for sending data -func (h *connectionParametersManager) GetSendConnectionFlowControlWindow() protocol.ByteCount { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.sendConnectionFlowControlWindow -} - -// GetReceiveStreamFlowControlWindow gets the size of the stream-level flow control window for receiving data -func (h *connectionParametersManager) GetReceiveStreamFlowControlWindow() protocol.ByteCount { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.receiveStreamFlowControlWindow -} - -// GetMaxReceiveStreamFlowControlWindow gets the maximum size of the stream-level flow control window for sending data -func (h *connectionParametersManager) GetMaxReceiveStreamFlowControlWindow() protocol.ByteCount { - return h.maxReceiveStreamFlowControlWindow -} - -// GetReceiveConnectionFlowControlWindow gets the size of the stream-level flow control window for receiving data -func (h *connectionParametersManager) GetReceiveConnectionFlowControlWindow() protocol.ByteCount { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.receiveConnectionFlowControlWindow -} - -// GetMaxReceiveConnectionFlowControlWindow gets the maximum size of the stream-level flow control window for sending data -func (h *connectionParametersManager) GetMaxReceiveConnectionFlowControlWindow() protocol.ByteCount { - return h.maxReceiveConnectionFlowControlWindow -} - -// GetMaxOutgoingStreams gets the maximum number of outgoing streams per connection -func (h *connectionParametersManager) GetMaxOutgoingStreams() uint32 { - h.mutex.RLock() - defer h.mutex.RUnlock() - - return h.maxIncomingDynamicStreamsPerConnection -} - -// GetMaxIncomingStreams get the maximum number of incoming streams per connection -func (h *connectionParametersManager) GetMaxIncomingStreams() uint32 { - h.mutex.RLock() - defer h.mutex.RUnlock() - - maxStreams := protocol.MaxIncomingDynamicStreamsPerConnection - return utils.MaxUint32(uint32(maxStreams)+protocol.MaxStreamsMinimumIncrement, uint32(float64(maxStreams)*protocol.MaxStreamsMultiplier)) -} - -// GetIdleConnectionStateLifetime gets the idle timeout -func (h *connectionParametersManager) GetIdleConnectionStateLifetime() time.Duration { - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.idleConnectionStateLifetime -} - -// TruncateConnectionID determines if the client requests truncated ConnectionIDs -func (h *connectionParametersManager) TruncateConnectionID() bool { - if h.perspective == protocol.PerspectiveClient { - return false - } - - h.mutex.RLock() - defer h.mutex.RUnlock() - return h.truncateConnectionID -} diff --git a/internal/handshake/crypto_setup_client.go b/internal/handshake/crypto_setup_client.go index d1274075..dfb7f897 100644 --- a/internal/handshake/crypto_setup_client.go +++ b/internal/handshake/crypto_setup_client.go @@ -52,7 +52,7 @@ type cryptoSetupClient struct { aeadChanged chan<- protocol.EncryptionLevel requestConnIDTruncation bool - connectionParameters ConnectionParametersManager + connectionParameters *gquicConnectionParametersManager } var _ CryptoSetup = &cryptoSetupClient{} @@ -73,7 +73,7 @@ func NewCryptoSetupClient( aeadChanged chan<- protocol.EncryptionLevel, negotiatedVersions []protocol.VersionNumber, ) (CryptoSetup, ConnectionParametersManager, error) { - cpm := NewConnectionParamatersManager(protocol.PerspectiveClient, version, params) + cpm := newGQUICConnectionParamatersManager(protocol.PerspectiveClient, version, params) return &cryptoSetupClient{ hostname: hostname, connID: connID, diff --git a/internal/handshake/crypto_setup_server.go b/internal/handshake/crypto_setup_server.go index b62e593c..c3238052 100644 --- a/internal/handshake/crypto_setup_server.go +++ b/internal/handshake/crypto_setup_server.go @@ -47,7 +47,7 @@ type cryptoSetupServer struct { cryptoStream io.ReadWriter - connectionParameters ConnectionParametersManager + connectionParameters *gquicConnectionParametersManager mutex sync.RWMutex } @@ -79,7 +79,7 @@ func NewCryptoSetup( return nil, nil, err } - cpm := NewConnectionParamatersManager(protocol.PerspectiveServer, version, params) + cpm := newGQUICConnectionParamatersManager(protocol.PerspectiveServer, version, params) return &cryptoSetupServer{ connID: connID, remoteAddr: remoteAddr, diff --git a/internal/handshake/crypto_setup_tls.go b/internal/handshake/crypto_setup_tls.go index c306e67e..0601443b 100644 --- a/internal/handshake/crypto_setup_tls.go +++ b/internal/handshake/crypto_setup_tls.go @@ -47,13 +47,14 @@ func NewCryptoSetupTLS( } mintConf.ServerName = hostname + // TODO: implement connection paramaters negotiation for TLS return &cryptoSetupTLS{ perspective: perspective, mintConf: mintConf, nullAEAD: crypto.NewNullAEAD(perspective, version), keyDerivation: crypto.DeriveAESKeys, aeadChanged: aeadChanged, - }, NewConnectionParamatersManager(perspective, version, &TransportParameters{IdleTimeout: protocol.DefaultIdleTimeout}), nil + }, newGQUICConnectionParamatersManager(perspective, version, &TransportParameters{IdleTimeout: protocol.DefaultIdleTimeout}), nil } func (h *cryptoSetupTLS) HandleCryptoStream(cryptoStream io.ReadWriter) error { diff --git a/internal/handshake/gquic_connection_parameters_manager.go b/internal/handshake/gquic_connection_parameters_manager.go new file mode 100644 index 00000000..68757075 --- /dev/null +++ b/internal/handshake/gquic_connection_parameters_manager.go @@ -0,0 +1,116 @@ +package handshake + +import ( + "bytes" + "time" + + "github.com/lucas-clemente/quic-go/internal/protocol" + "github.com/lucas-clemente/quic-go/internal/utils" + "github.com/lucas-clemente/quic-go/qerr" +) + +var _ ConnectionParametersManager = &baseConnectionParametersManager{} + +// errMalformedTag is returned when the tag value cannot be read +var ( + errMalformedTag = qerr.Error(qerr.InvalidCryptoMessageParameter, "malformed Tag value") + errFlowControlRenegotiationNotSupported = qerr.Error(qerr.InvalidCryptoMessageParameter, "renegotiation of flow control parameters not supported") +) + +type gquicConnectionParametersManager struct { + baseConnectionParametersManager +} + +// newConnectionParamatersManager creates a new connection parameters manager +func newGQUICConnectionParamatersManager(pers protocol.Perspective, v protocol.VersionNumber, params *TransportParameters) *gquicConnectionParametersManager { + h := &gquicConnectionParametersManager{} + h.perspective = pers + h.version = v + h.init(params) + return h +} + +// SetFromMap reads all params. +func (h *gquicConnectionParametersManager) SetFromMap(params map[Tag][]byte) error { + h.mutex.Lock() + defer h.mutex.Unlock() + + if value, ok := params[TagTCID]; ok && h.perspective == protocol.PerspectiveServer { + clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.truncateConnectionID = (clientValue == 0) + } + if value, ok := params[TagMSPC]; ok { + clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.maxStreamsPerConnection = h.negotiateMaxStreamsPerConnection(clientValue) + } + if value, ok := params[TagMIDS]; ok { + clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.maxIncomingDynamicStreamsPerConnection = h.negotiateMaxIncomingDynamicStreamsPerConnection(clientValue) + } + if value, ok := params[TagICSL]; ok { + clientValue, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.idleConnectionStateLifetime = h.negotiateIdleConnectionStateLifetime(time.Duration(clientValue) * time.Second) + } + if value, ok := params[TagSFCW]; ok { + if h.flowControlNegotiated { + return errFlowControlRenegotiationNotSupported + } + sendStreamFlowControlWindow, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.sendStreamFlowControlWindow = protocol.ByteCount(sendStreamFlowControlWindow) + } + if value, ok := params[TagCFCW]; ok { + if h.flowControlNegotiated { + return errFlowControlRenegotiationNotSupported + } + sendConnectionFlowControlWindow, err := utils.LittleEndian.ReadUint32(bytes.NewBuffer(value)) + if err != nil { + return errMalformedTag + } + h.sendConnectionFlowControlWindow = protocol.ByteCount(sendConnectionFlowControlWindow) + } + + _, containsSFCW := params[TagSFCW] + _, containsCFCW := params[TagCFCW] + if containsCFCW || containsSFCW { + h.flowControlNegotiated = true + } + + return nil +} + +// GetHelloMap gets all parameters needed for the Hello message. +func (h *gquicConnectionParametersManager) GetHelloMap() (map[Tag][]byte, error) { + sfcw := bytes.NewBuffer([]byte{}) + utils.LittleEndian.WriteUint32(sfcw, uint32(h.GetReceiveStreamFlowControlWindow())) + cfcw := bytes.NewBuffer([]byte{}) + utils.LittleEndian.WriteUint32(cfcw, uint32(h.GetReceiveConnectionFlowControlWindow())) + mspc := bytes.NewBuffer([]byte{}) + utils.LittleEndian.WriteUint32(mspc, h.maxStreamsPerConnection) + mids := bytes.NewBuffer([]byte{}) + utils.LittleEndian.WriteUint32(mids, protocol.MaxIncomingDynamicStreamsPerConnection) + icsl := bytes.NewBuffer([]byte{}) + utils.LittleEndian.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(), + }, nil +} diff --git a/internal/handshake/connection_parameters_manager_test.go b/internal/handshake/gquic_connection_parameters_manager_test.go similarity index 94% rename from internal/handshake/connection_parameters_manager_test.go rename to internal/handshake/gquic_connection_parameters_manager_test.go index d7deec28..d83291e4 100644 --- a/internal/handshake/connection_parameters_manager_test.go +++ b/internal/handshake/gquic_connection_parameters_manager_test.go @@ -11,8 +11,8 @@ import ( ) var _ = Describe("ConnectionsParameterManager", func() { - var cpm *connectionParametersManager // a connectionParametersManager for a server - var cpmClient *connectionParametersManager + var cpm *gquicConnectionParametersManager // a connectionParametersManager for a server + var cpmClient *gquicConnectionParametersManager 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 @@ -20,7 +20,7 @@ var _ = Describe("ConnectionsParameterManager", func() { maxReceiveConnectionFlowControlWindowClient := protocol.ByteCount(math.Floor(13 * MB)) // default is 15 MB idleTimeout := 42 * time.Second BeforeEach(func() { - cpm = NewConnectionParamatersManager( + cpm = newGQUICConnectionParamatersManager( protocol.PerspectiveServer, protocol.VersionWhatever, &TransportParameters{ @@ -28,8 +28,8 @@ var _ = Describe("ConnectionsParameterManager", func() { MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindowServer, IdleTimeout: idleTimeout, }, - ).(*connectionParametersManager) - cpmClient = NewConnectionParamatersManager( + ) + cpmClient = newGQUICConnectionParamatersManager( protocol.PerspectiveClient, protocol.VersionWhatever, &TransportParameters{ @@ -37,7 +37,7 @@ var _ = Describe("ConnectionsParameterManager", func() { MaxReceiveConnectionFlowControlWindow: maxReceiveConnectionFlowControlWindowClient, IdleTimeout: idleTimeout, }, - ).(*connectionParametersManager) + ) }) Context("SHLO", func() { @@ -139,7 +139,7 @@ var _ = Describe("ConnectionsParameterManager", func() { It("errors when given an invalid value", func() { values := map[Tag][]byte{TagTCID: {2, 0, 0}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) }) }) @@ -175,7 +175,7 @@ var _ = Describe("ConnectionsParameterManager", func() { It("does not change the stream-level flow control window when given an invalid value", func() { values := map[Tag][]byte{TagSFCW: {0xDE, 0xAD, 0xBE}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) Expect(cpm.GetSendStreamFlowControlWindow()).To(Equal(protocol.InitialStreamFlowControlWindow)) }) @@ -189,7 +189,7 @@ var _ = Describe("ConnectionsParameterManager", func() { It("does not change the connection-level flow control window when given an invalid value", func() { values := map[Tag][]byte{TagCFCW: {0xDE, 0xAD, 0xBE}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) Expect(cpm.GetSendStreamFlowControlWindow()).To(Equal(protocol.InitialConnectionFlowControlWindow)) }) @@ -205,7 +205,7 @@ var _ = Describe("ConnectionsParameterManager", func() { TagSFCW: {0x13, 0x37, 0x13, 0x37}, } err = cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrFlowControlRenegotiationNotSupported)) + Expect(err).To(MatchError(errFlowControlRenegotiationNotSupported)) Expect(cpm.GetSendStreamFlowControlWindow()).To(Equal(protocol.ByteCount(0xEFBEADDE))) Expect(cpm.GetSendConnectionFlowControlWindow()).To(Equal(protocol.ByteCount(0xEFBEADDE))) }) @@ -239,7 +239,7 @@ var _ = Describe("ConnectionsParameterManager", func() { TagSFCW: {0xDE, 0xAD, 0xBE}, // 1 byte too short } err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) Expect(cpm.GetIdleConnectionStateLifetime()).To(Equal(idleTimeout)) }) @@ -252,7 +252,7 @@ var _ = Describe("ConnectionsParameterManager", func() { It("errors when given an invalid value", func() { values := map[Tag][]byte{TagICSL: {2, 0, 0}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) }) }) @@ -260,13 +260,13 @@ var _ = Describe("ConnectionsParameterManager", func() { It("errors when given an invalid max streams per connection value", func() { values := map[Tag][]byte{TagMSPC: {2, 0, 0}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) }) It("errors when given an invalid max dynamic incoming streams per connection value", func() { values := map[Tag][]byte{TagMIDS: {2, 0, 0}} // 1 byte too short err := cpm.SetFromMap(values) - Expect(err).To(MatchError(ErrMalformedTag)) + Expect(err).To(MatchError(errMalformedTag)) }) Context("outgoing connections", func() { diff --git a/internal/mocks/cpm.go b/internal/mocks/cpm.go index 2efb6cc0..0492abee 100644 --- a/internal/mocks/cpm.go +++ b/internal/mocks/cpm.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: ../handshake/connection_parameters_manager.go +// Source: ../handshake/base_connection_parameters_manager.go package mocks @@ -8,7 +8,6 @@ import ( time "time" gomock "github.com/golang/mock/gomock" - "github.com/lucas-clemente/quic-go/internal/handshake" protocol "github.com/lucas-clemente/quic-go/internal/protocol" ) @@ -35,31 +34,6 @@ func (_m *MockConnectionParametersManager) EXPECT() *MockConnectionParametersMan return _m.recorder } -// SetFromMap mocks base method -func (_m *MockConnectionParametersManager) SetFromMap(_param0 map[handshake.Tag][]byte) error { - ret := _m.ctrl.Call(_m, "SetFromMap", _param0) - ret0, _ := ret[0].(error) - return ret0 -} - -// SetFromMap indicates an expected call of SetFromMap -func (_mr *MockConnectionParametersManagerMockRecorder) SetFromMap(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "SetFromMap", reflect.TypeOf((*MockConnectionParametersManager)(nil).SetFromMap), arg0) -} - -// GetHelloMap mocks base method -func (_m *MockConnectionParametersManager) GetHelloMap() (map[handshake.Tag][]byte, error) { - ret := _m.ctrl.Call(_m, "GetHelloMap") - ret0, _ := ret[0].(map[handshake.Tag][]byte) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// GetHelloMap indicates an expected call of GetHelloMap -func (_mr *MockConnectionParametersManagerMockRecorder) GetHelloMap() *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GetHelloMap", reflect.TypeOf((*MockConnectionParametersManager)(nil).GetHelloMap)) -} - // GetSendStreamFlowControlWindow mocks base method func (_m *MockConnectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount { ret := _m.ctrl.Call(_m, "GetSendStreamFlowControlWindow") diff --git a/internal/mocks/gen.go b/internal/mocks/gen.go index 3cea6fa4..0b40addc 100644 --- a/internal/mocks/gen.go +++ b/internal/mocks/gen.go @@ -4,5 +4,5 @@ package mocks // so we have to use sed to correct for that //go:generate sh -c "mockgen -package mocks_fc -source ../flowcontrol/interface.go | sed \"s/\\[\\]WindowUpdate/[]flowcontrol.WindowUpdate/g\" > mocks_fc/flow_control_manager.go" -//go:generate sh -c "mockgen -package mocks -source ../handshake/connection_parameters_manager.go | sed \"s/\\[Tag\\]/[handshake.Tag]/g\" > cpm.go" +//go:generate sh -c "mockgen -package mocks -source ../handshake/base_connection_parameters_manager.go > cpm.go" //go:generate sh -c "goimports -w ." diff --git a/session_test.go b/session_test.go index 05ceb3a1..a43d0786 100644 --- a/session_test.go +++ b/session_test.go @@ -143,15 +143,9 @@ func areSessionsRunning() bool { return strings.Contains(b.String(), "quic-go.(*session).run") } -type mockConnectionParametersManager struct { -} +type mockConnectionParametersManager struct{} -func (m *mockConnectionParametersManager) SetFromMap(map[handshake.Tag][]byte) error { - panic("not implement") -} -func (m *mockConnectionParametersManager) GetHelloMap() (map[handshake.Tag][]byte, error) { - panic("not implement") -} +var _ handshake.ConnectionParametersManager = &mockConnectionParametersManager{} func (m *mockConnectionParametersManager) GetSendStreamFlowControlWindow() protocol.ByteCount { return protocol.InitialStreamFlowControlWindow