mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-07 06:07:36 +03:00
separate connection parameters negotiation and gQUIC handshake parsing
This commit is contained in:
parent
95901cdee4
commit
ebb4150e63
10 changed files with 289 additions and 317 deletions
149
internal/handshake/base_connection_parameters_manager.go
Normal file
149
internal/handshake/base_connection_parameters_manager.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
116
internal/handshake/gquic_connection_parameters_manager.go
Normal file
116
internal/handshake/gquic_connection_parameters_manager.go
Normal file
|
@ -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
|
||||
}
|
|
@ -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() {
|
|
@ -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")
|
||||
|
|
|
@ -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 ."
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue