send stream counts, not stream IDs, in the transport parameters

This commit is contained in:
Marten Seemann 2018-03-15 09:18:36 +01:00
parent 1fffb88553
commit b40942d39e
10 changed files with 83 additions and 74 deletions

View file

@ -193,8 +193,8 @@ func (c *client) dialTLS() error {
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow, ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
IdleTimeout: c.config.IdleTimeout, IdleTimeout: c.config.IdleTimeout,
OmitConnectionID: c.config.RequestConnectionIDOmission, OmitConnectionID: c.config.RequestConnectionIDOmission,
MaxBidiStreamID: protocol.MaxBidiStreamID(c.config.MaxIncomingStreams, protocol.PerspectiveClient), MaxBidiStreams: uint16(c.config.MaxIncomingStreams),
MaxUniStreamID: protocol.MaxUniStreamID(c.config.MaxIncomingUniStreams, protocol.PerspectiveClient), MaxUniStreams: uint16(c.config.MaxIncomingUniStreams),
} }
csc := handshake.NewCryptoStreamConn(nil) csc := handshake.NewCryptoStreamConn(nil)
extHandler := handshake.NewExtensionHandlerClient(params, c.initialVersion, c.config.Versions, c.version) extHandler := handshake.NewExtensionHandlerClient(params, c.initialVersion, c.config.Versions, c.version)

View file

@ -177,11 +177,13 @@ type Config struct {
// MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open. // MaxIncomingStreams is the maximum number of concurrent bidirectional streams that a peer is allowed to open.
// If not set, it will default to 100. // If not set, it will default to 100.
// If set to a negative value, it doesn't allow any bidirectional streams. // If set to a negative value, it doesn't allow any bidirectional streams.
// Values larger than 65535 (math.MaxUint16) are invalid.
MaxIncomingStreams int MaxIncomingStreams int
// MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open. // MaxIncomingUniStreams is the maximum number of concurrent unidirectional streams that a peer is allowed to open.
// This value doesn't have any effect in Google QUIC. // This value doesn't have any effect in Google QUIC.
// If not set, it will default to 100. // If not set, it will default to 100.
// If set to a negative value, it doesn't allow any unidirectional streams. // If set to a negative value, it doesn't allow any unidirectional streams.
// Values larger than 65535 (math.MaxUint16) are invalid.
MaxIncomingUniStreams int MaxIncomingUniStreams int
// KeepAlive defines whether this peer will periodically send PING frames to keep the connection alive. // KeepAlive defines whether this peer will periodically send PING frames to keep the connection alive.
KeepAlive bool KeepAlive bool

View file

@ -9,14 +9,14 @@ type transportParameterID uint16
const quicTLSExtensionType = 26 const quicTLSExtensionType = 26
const ( const (
initialMaxStreamDataParameterID transportParameterID = 0x0 initialMaxStreamDataParameterID transportParameterID = 0x0
initialMaxDataParameterID transportParameterID = 0x1 initialMaxDataParameterID transportParameterID = 0x1
initialMaxStreamIDBiDiParameterID transportParameterID = 0x2 initialMaxStreamsBiDiParameterID transportParameterID = 0x2
idleTimeoutParameterID transportParameterID = 0x3 idleTimeoutParameterID transportParameterID = 0x3
omitConnectionIDParameterID transportParameterID = 0x4 omitConnectionIDParameterID transportParameterID = 0x4
maxPacketSizeParameterID transportParameterID = 0x5 maxPacketSizeParameterID transportParameterID = 0x5
statelessResetTokenParameterID transportParameterID = 0x6 statelessResetTokenParameterID transportParameterID = 0x6
initialMaxStreamIDUniParameterID transportParameterID = 0x8 initialMaxStreamsUniParameterID transportParameterID = 0x8
) )
type transportParameter struct { type transportParameter struct {

View file

@ -66,11 +66,11 @@ var _ = Describe("TLS Extension Handler, for the client", func() {
BeforeEach(func() { BeforeEach(func() {
fakeBody = &tlsExtensionBody{data: []byte("foobar foobar")} fakeBody = &tlsExtensionBody{data: []byte("foobar foobar")}
parameters = map[transportParameterID][]byte{ parameters = map[transportParameterID][]byte{
initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44}, initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44},
initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55}, initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55},
initialMaxStreamIDBiDiParameterID: {0x33, 0x44, 0x55, 0x66}, initialMaxStreamsBiDiParameterID: {0x33, 0x44},
idleTimeoutParameterID: {0x13, 0x37}, idleTimeoutParameterID: {0x13, 0x37},
statelessResetTokenParameterID: bytes.Repeat([]byte{0}, 16), statelessResetTokenParameterID: bytes.Repeat([]byte{0}, 16),
} }
}) })

View file

@ -77,10 +77,10 @@ var _ = Describe("TLS Extension Handler, for the server", func() {
BeforeEach(func() { BeforeEach(func() {
fakeBody = &tlsExtensionBody{data: []byte("foobar foobar")} fakeBody = &tlsExtensionBody{data: []byte("foobar foobar")}
parameters = map[transportParameterID][]byte{ parameters = map[transportParameterID][]byte{
initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44}, initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44},
initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55}, initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55},
initialMaxStreamIDBiDiParameterID: {0x33, 0x44, 0x55, 0x66}, initialMaxStreamsBiDiParameterID: {0x33, 0x44},
idleTimeoutParameterID: {0x13, 0x37}, idleTimeoutParameterID: {0x13, 0x37},
} }
}) })

View file

@ -114,12 +114,12 @@ var _ = Describe("Transport Parameters", func() {
p := &TransportParameters{ p := &TransportParameters{
StreamFlowControlWindow: 0x1234, StreamFlowControlWindow: 0x1234,
ConnectionFlowControlWindow: 0x4321, ConnectionFlowControlWindow: 0x4321,
MaxBidiStreamID: 1337, MaxBidiStreams: 1337,
MaxUniStreamID: 7331, MaxUniStreams: 7331,
OmitConnectionID: true, OmitConnectionID: true,
IdleTimeout: 42 * time.Second, IdleTimeout: 42 * time.Second,
} }
Expect(p.String()).To(Equal("&handshake.TransportParameters{StreamFlowControlWindow: 0x1234, ConnectionFlowControlWindow: 0x4321, MaxBidiStreamID: 1337, MaxUniStreamID: 7331, OmitConnectionID: true, IdleTimeout: 42s}")) Expect(p.String()).To(Equal("&handshake.TransportParameters{StreamFlowControlWindow: 0x1234, ConnectionFlowControlWindow: 0x4321, MaxBidiStreams: 1337, MaxUniStreams: 7331, OmitConnectionID: true, IdleTimeout: 42s}"))
}) })
Context("parsing", func() { Context("parsing", func() {
@ -127,12 +127,12 @@ var _ = Describe("Transport Parameters", func() {
BeforeEach(func() { BeforeEach(func() {
parameters = map[transportParameterID][]byte{ parameters = map[transportParameterID][]byte{
initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44}, initialMaxStreamDataParameterID: {0x11, 0x22, 0x33, 0x44},
initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55}, initialMaxDataParameterID: {0x22, 0x33, 0x44, 0x55},
initialMaxStreamIDBiDiParameterID: {0x33, 0x44, 0x55, 0x66}, initialMaxStreamsBiDiParameterID: {0x33, 0x44},
initialMaxStreamIDUniParameterID: {0x44, 0x55, 0x66, 0x77}, initialMaxStreamsUniParameterID: {0x44, 0x55},
idleTimeoutParameterID: {0x13, 0x37}, idleTimeoutParameterID: {0x13, 0x37},
maxPacketSizeParameterID: {0x73, 0x31}, maxPacketSizeParameterID: {0x73, 0x31},
} }
}) })
It("reads parameters", func() { It("reads parameters", func() {
@ -140,8 +140,8 @@ var _ = Describe("Transport Parameters", func() {
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(params.StreamFlowControlWindow).To(Equal(protocol.ByteCount(0x11223344))) Expect(params.StreamFlowControlWindow).To(Equal(protocol.ByteCount(0x11223344)))
Expect(params.ConnectionFlowControlWindow).To(Equal(protocol.ByteCount(0x22334455))) Expect(params.ConnectionFlowControlWindow).To(Equal(protocol.ByteCount(0x22334455)))
Expect(params.MaxBidiStreamID).To(Equal(protocol.StreamID(0x33445566))) Expect(params.MaxBidiStreams).To(Equal(uint16(0x3344)))
Expect(params.MaxUniStreamID).To(Equal(protocol.StreamID(0x44556677))) Expect(params.MaxUniStreams).To(Equal(uint16(0x4455)))
Expect(params.IdleTimeout).To(Equal(0x1337 * time.Second)) Expect(params.IdleTimeout).To(Equal(0x1337 * time.Second))
Expect(params.OmitConnectionID).To(BeFalse()) Expect(params.OmitConnectionID).To(BeFalse())
Expect(params.MaxPacketSize).To(Equal(protocol.ByteCount(0x7331))) Expect(params.MaxPacketSize).To(Equal(protocol.ByteCount(0x7331)))
@ -194,15 +194,15 @@ var _ = Describe("Transport Parameters", func() {
}) })
It("rejects the parameters if the initial_max_stream_id_bidi has the wrong length", func() { It("rejects the parameters if the initial_max_stream_id_bidi has the wrong length", func() {
parameters[initialMaxStreamIDBiDiParameterID] = []byte{0x11, 0x22, 0x33, 0x44, 0x55} // should be 4 bytes parameters[initialMaxStreamsBiDiParameterID] = []byte{0x11, 0x22, 0x33} // should be 2 bytes
_, err := readTransportParameters(paramsMapToList(parameters)) _, err := readTransportParameters(paramsMapToList(parameters))
Expect(err).To(MatchError("wrong length for initial_max_stream_id_bidi: 5 (expected 4)")) Expect(err).To(MatchError("wrong length for initial_max_stream_id_bidi: 3 (expected 2)"))
}) })
It("rejects the parameters if the initial_max_stream_id_bidi has the wrong length", func() { It("rejects the parameters if the initial_max_stream_id_bidi has the wrong length", func() {
parameters[initialMaxStreamIDUniParameterID] = []byte{0x11, 0x22, 0x33, 0x44, 0x55} // should be 4 bytes parameters[initialMaxStreamsUniParameterID] = []byte{0x11, 0x22, 0x33} // should be 2 bytes
_, err := readTransportParameters(paramsMapToList(parameters)) _, err := readTransportParameters(paramsMapToList(parameters))
Expect(err).To(MatchError("wrong length for initial_max_stream_id_uni: 5 (expected 4)")) Expect(err).To(MatchError("wrong length for initial_max_stream_id_uni: 3 (expected 2)"))
}) })
It("rejects the parameters if the initial_idle_timeout has the wrong length", func() { It("rejects the parameters if the initial_idle_timeout has the wrong length", func() {
@ -252,8 +252,8 @@ var _ = Describe("Transport Parameters", func() {
StreamFlowControlWindow: 0xdeadbeef, StreamFlowControlWindow: 0xdeadbeef,
ConnectionFlowControlWindow: 0xdecafbad, ConnectionFlowControlWindow: 0xdecafbad,
IdleTimeout: 0xcafe * time.Second, IdleTimeout: 0xcafe * time.Second,
MaxBidiStreamID: 0xbadf000d, MaxBidiStreams: 0x1234,
MaxUniStreamID: 0xface, MaxUniStreams: 0x4321,
} }
}) })
@ -262,8 +262,8 @@ var _ = Describe("Transport Parameters", func() {
Expect(values).To(HaveLen(6)) Expect(values).To(HaveLen(6))
Expect(values).To(HaveKeyWithValue(initialMaxStreamDataParameterID, []byte{0xde, 0xad, 0xbe, 0xef})) Expect(values).To(HaveKeyWithValue(initialMaxStreamDataParameterID, []byte{0xde, 0xad, 0xbe, 0xef}))
Expect(values).To(HaveKeyWithValue(initialMaxDataParameterID, []byte{0xde, 0xca, 0xfb, 0xad})) Expect(values).To(HaveKeyWithValue(initialMaxDataParameterID, []byte{0xde, 0xca, 0xfb, 0xad}))
Expect(values).To(HaveKeyWithValue(initialMaxStreamIDBiDiParameterID, []byte{0xba, 0xdf, 0x00, 0x0d})) Expect(values).To(HaveKeyWithValue(initialMaxStreamsBiDiParameterID, []byte{0x12, 0x34}))
Expect(values).To(HaveKeyWithValue(initialMaxStreamIDUniParameterID, []byte{0x0, 0x0, 0xfa, 0xce})) Expect(values).To(HaveKeyWithValue(initialMaxStreamsUniParameterID, []byte{0x43, 0x21}))
Expect(values).To(HaveKeyWithValue(idleTimeoutParameterID, []byte{0xca, 0xfe})) Expect(values).To(HaveKeyWithValue(idleTimeoutParameterID, []byte{0xca, 0xfe}))
Expect(values).To(HaveKeyWithValue(maxPacketSizeParameterID, []byte{0x5, 0xac})) // 1452 = 0x5ac Expect(values).To(HaveKeyWithValue(maxPacketSizeParameterID, []byte{0x5, 0xac})) // 1452 = 0x5ac
}) })

View file

@ -22,9 +22,9 @@ type TransportParameters struct {
MaxPacketSize protocol.ByteCount MaxPacketSize protocol.ByteCount
MaxBidiStreamID protocol.StreamID // only used for IETF QUIC MaxUniStreams uint16 // only used for IETF QUIC
MaxUniStreamID protocol.StreamID // only used for IETF QUIC MaxBidiStreams uint16 // only used for IETF QUIC
MaxStreams uint32 // only used for gQUIC MaxStreams uint32 // only used for gQUIC
OmitConnectionID bool OmitConnectionID bool
IdleTimeout time.Duration IdleTimeout time.Duration
@ -116,18 +116,16 @@ func readTransportParameters(paramsList []transportParameter) (*TransportParamet
return nil, fmt.Errorf("wrong length for initial_max_data: %d (expected 4)", len(p.Value)) return nil, fmt.Errorf("wrong length for initial_max_data: %d (expected 4)", len(p.Value))
} }
params.ConnectionFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(p.Value)) params.ConnectionFlowControlWindow = protocol.ByteCount(binary.BigEndian.Uint32(p.Value))
case initialMaxStreamIDBiDiParameterID: case initialMaxStreamsBiDiParameterID:
if len(p.Value) != 4 { if len(p.Value) != 2 {
return nil, fmt.Errorf("wrong length for initial_max_stream_id_bidi: %d (expected 4)", len(p.Value)) return nil, fmt.Errorf("wrong length for initial_max_stream_id_bidi: %d (expected 2)", len(p.Value))
} }
// TODO(#1154): validate the stream ID params.MaxBidiStreams = binary.BigEndian.Uint16(p.Value)
params.MaxBidiStreamID = protocol.StreamID(binary.BigEndian.Uint32(p.Value)) case initialMaxStreamsUniParameterID:
case initialMaxStreamIDUniParameterID: if len(p.Value) != 2 {
if len(p.Value) != 4 { return nil, fmt.Errorf("wrong length for initial_max_stream_id_uni: %d (expected 2)", len(p.Value))
return nil, fmt.Errorf("wrong length for initial_max_stream_id_uni: %d (expected 4)", len(p.Value))
} }
// TODO(#1154): validate the stream ID params.MaxUniStreams = binary.BigEndian.Uint16(p.Value)
params.MaxUniStreamID = protocol.StreamID(binary.BigEndian.Uint32(p.Value))
case idleTimeoutParameterID: case idleTimeoutParameterID:
foundIdleTimeout = true foundIdleTimeout = true
if len(p.Value) != 2 { if len(p.Value) != 2 {
@ -164,10 +162,10 @@ func (p *TransportParameters) getTransportParameters() []transportParameter {
binary.BigEndian.PutUint32(initialMaxStreamData, uint32(p.StreamFlowControlWindow)) binary.BigEndian.PutUint32(initialMaxStreamData, uint32(p.StreamFlowControlWindow))
initialMaxData := make([]byte, 4) initialMaxData := make([]byte, 4)
binary.BigEndian.PutUint32(initialMaxData, uint32(p.ConnectionFlowControlWindow)) binary.BigEndian.PutUint32(initialMaxData, uint32(p.ConnectionFlowControlWindow))
initialMaxBidiStreamID := make([]byte, 4) initialMaxBidiStreamID := make([]byte, 2)
binary.BigEndian.PutUint32(initialMaxBidiStreamID, uint32(p.MaxBidiStreamID)) binary.BigEndian.PutUint16(initialMaxBidiStreamID, p.MaxBidiStreams)
initialMaxUniStreamID := make([]byte, 4) initialMaxUniStreamID := make([]byte, 2)
binary.BigEndian.PutUint32(initialMaxUniStreamID, uint32(p.MaxUniStreamID)) binary.BigEndian.PutUint16(initialMaxUniStreamID, p.MaxUniStreams)
idleTimeout := make([]byte, 2) idleTimeout := make([]byte, 2)
binary.BigEndian.PutUint16(idleTimeout, uint16(p.IdleTimeout/time.Second)) binary.BigEndian.PutUint16(idleTimeout, uint16(p.IdleTimeout/time.Second))
maxPacketSize := make([]byte, 2) maxPacketSize := make([]byte, 2)
@ -175,8 +173,8 @@ func (p *TransportParameters) getTransportParameters() []transportParameter {
params := []transportParameter{ params := []transportParameter{
{initialMaxStreamDataParameterID, initialMaxStreamData}, {initialMaxStreamDataParameterID, initialMaxStreamData},
{initialMaxDataParameterID, initialMaxData}, {initialMaxDataParameterID, initialMaxData},
{initialMaxStreamIDBiDiParameterID, initialMaxBidiStreamID}, {initialMaxStreamsBiDiParameterID, initialMaxBidiStreamID},
{initialMaxStreamIDUniParameterID, initialMaxUniStreamID}, {initialMaxStreamsUniParameterID, initialMaxUniStreamID},
{idleTimeoutParameterID, idleTimeout}, {idleTimeoutParameterID, idleTimeout},
{maxPacketSizeParameterID, maxPacketSize}, {maxPacketSizeParameterID, maxPacketSize},
} }
@ -189,5 +187,5 @@ func (p *TransportParameters) getTransportParameters() []transportParameter {
// String returns a string representation, intended for logging. // String returns a string representation, intended for logging.
// It should only used for IETF QUIC. // It should only used for IETF QUIC.
func (p *TransportParameters) String() string { func (p *TransportParameters) String() string {
return fmt.Sprintf("&handshake.TransportParameters{StreamFlowControlWindow: %#x, ConnectionFlowControlWindow: %#x, MaxBidiStreamID: %d, MaxUniStreamID: %d, OmitConnectionID: %t, IdleTimeout: %s}", p.StreamFlowControlWindow, p.ConnectionFlowControlWindow, p.MaxBidiStreamID, p.MaxUniStreamID, p.OmitConnectionID, p.IdleTimeout) return fmt.Sprintf("&handshake.TransportParameters{StreamFlowControlWindow: %#x, ConnectionFlowControlWindow: %#x, MaxBidiStreams: %d, MaxUniStreams: %d, OmitConnectionID: %t, IdleTimeout: %s}", p.StreamFlowControlWindow, p.ConnectionFlowControlWindow, p.MaxBidiStreams, p.MaxUniStreams, p.OmitConnectionID, p.IdleTimeout)
} }

View file

@ -71,8 +71,8 @@ func newServerTLS(
StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow, StreamFlowControlWindow: protocol.ReceiveStreamFlowControlWindow,
ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow, ConnectionFlowControlWindow: protocol.ReceiveConnectionFlowControlWindow,
IdleTimeout: config.IdleTimeout, IdleTimeout: config.IdleTimeout,
MaxBidiStreamID: protocol.MaxBidiStreamID(config.MaxIncomingStreams, protocol.PerspectiveServer), MaxBidiStreams: uint16(config.MaxIncomingStreams),
MaxUniStreamID: protocol.MaxUniStreamID(config.MaxIncomingUniStreams, protocol.PerspectiveServer), MaxUniStreams: uint16(config.MaxIncomingUniStreams),
}, },
} }
s.newMintConn = s.newMintConnImpl s.newMintConn = s.newMintConnImpl

View file

@ -206,8 +206,14 @@ func (m *streamsMap) HandleMaxStreamIDFrame(f *wire.MaxStreamIDFrame) error {
} }
func (m *streamsMap) UpdateLimits(p *handshake.TransportParameters) { func (m *streamsMap) UpdateLimits(p *handshake.TransportParameters) {
m.outgoingBidiStreams.SetMaxStream(p.MaxBidiStreamID) // Max{Uni,Bidi}StreamID returns the highest stream ID that the peer is allowed to open.
m.outgoingUniStreams.SetMaxStream(p.MaxUniStreamID) // Invert the perspective to determine the value that we are allowed to open.
peerPers := protocol.PerspectiveServer
if m.perspective == protocol.PerspectiveServer {
peerPers = protocol.PerspectiveClient
}
m.outgoingBidiStreams.SetMaxStream(protocol.MaxBidiStreamID(int(p.MaxBidiStreams), peerPers))
m.outgoingUniStreams.SetMaxStream(protocol.MaxUniStreamID(int(p.MaxUniStreams), peerPers))
} }
func (m *streamsMap) CloseWithError(err error) { func (m *streamsMap) CloseWithError(err error) {

View file

@ -3,6 +3,7 @@ package quic
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/lucas-clemente/quic-go/internal/flowcontrol" "github.com/lucas-clemente/quic-go/internal/flowcontrol"
@ -63,8 +64,8 @@ var _ = Describe("Streams Map (for IETF QUIC)", func() {
allowUnlimitedStreams := func() { allowUnlimitedStreams := func() {
m.UpdateLimits(&handshake.TransportParameters{ m.UpdateLimits(&handshake.TransportParameters{
MaxBidiStreamID: 0xffffffff, MaxBidiStreams: math.MaxUint16,
MaxUniStreamID: 0xffffffff, MaxUniStreams: math.MaxUint16,
}) })
} }
@ -266,26 +267,28 @@ var _ = Describe("Streams Map (for IETF QUIC)", func() {
mockSender.EXPECT().queueControlFrame(gomock.Any()) mockSender.EXPECT().queueControlFrame(gomock.Any())
}) })
It("processes the parameter for outgoing bidirectional streams", func() { It("processes the parameter for outgoing streams, as a server", func() {
m.perspective = protocol.PerspectiveServer
_, err := m.OpenStream() _, err := m.OpenStream()
Expect(err).To(MatchError(qerr.TooManyOpenStreams)) Expect(err).To(MatchError(qerr.TooManyOpenStreams))
m.UpdateLimits(&handshake.TransportParameters{ m.UpdateLimits(&handshake.TransportParameters{
MaxBidiStreamID: ids.firstOutgoingBidiStream, MaxBidiStreams: 5,
MaxUniStreams: 5,
}) })
str, err := m.OpenStream() Expect(m.outgoingBidiStreams.maxStream).To(Equal(protocol.StreamID(17)))
Expect(err).ToNot(HaveOccurred()) Expect(m.outgoingUniStreams.maxStream).To(Equal(protocol.StreamID(19)))
Expect(str.StreamID()).To(Equal(ids.firstOutgoingBidiStream))
}) })
It("processes the parameter for outgoing bidirectional streams", func() { It("processes the parameter for outgoing streams, as a client", func() {
m.perspective = protocol.PerspectiveClient
_, err := m.OpenUniStream() _, err := m.OpenUniStream()
Expect(err).To(MatchError(qerr.TooManyOpenStreams)) Expect(err).To(MatchError(qerr.TooManyOpenStreams))
m.UpdateLimits(&handshake.TransportParameters{ m.UpdateLimits(&handshake.TransportParameters{
MaxUniStreamID: ids.firstOutgoingUniStream, MaxBidiStreams: 5,
MaxUniStreams: 5,
}) })
str, err := m.OpenUniStream() Expect(m.outgoingBidiStreams.maxStream).To(Equal(protocol.StreamID(20)))
Expect(err).ToNot(HaveOccurred()) Expect(m.outgoingUniStreams.maxStream).To(Equal(protocol.StreamID(18)))
Expect(str.StreamID()).To(Equal(ids.firstOutgoingUniStream))
}) })
}) })