Files
golang.net/quic/stream_limits_test.go
Dmitri Shuralyov 29181b8c03 all: remove go1.25 and older build constraints
Now that the x/net module requires Go 1.25.0,
the go1.25 build constraint is always satisfied.
Simplify the code accordingly.

Change-Id: I3d6fe4a132a26918455489b998730b494f5273c4
Reviewed-on: https://go-review.googlesource.com/c/net/+/744800
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org>
Reviewed-by: Nicholas Husin <nsh@golang.org>
Reviewed-by: Nicholas Husin <husin@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
2026-02-12 07:53:52 -08:00

267 lines
8.2 KiB
Go

// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package quic
import (
"context"
"crypto/tls"
"testing"
"testing/synctest"
)
func TestStreamLimitNewStreamBlocked(t *testing.T) {
// "An endpoint that receives a frame with a stream ID exceeding the limit
// it has sent MUST treat this as a connection error of type STREAM_LIMIT_ERROR [...]"
// https://www.rfc-editor.org/rfc/rfc9000#section-4.6-3
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
ctx := canceledContext()
tc := newTestConn(t, clientSide,
permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxStreamsBidi = 0
p.initialMaxStreamsUni = 0
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
opening := runAsync(tc, func(ctx context.Context) (*Stream, error) {
return tc.conn.newLocalStream(ctx, styp)
})
if _, err := opening.result(); err != errNotDone {
t.Fatalf("new stream blocked by limit: %v, want errNotDone", err)
}
tc.writeFrames(packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 1,
})
if _, err := opening.result(); err != nil {
t.Fatalf("new stream not created after limit raised: %v", err)
}
if _, err := tc.conn.newLocalStream(ctx, styp); err == nil {
t.Fatalf("new stream blocked by raised limit: %v, want error", err)
}
})
}
func TestStreamLimitMaxStreamsDecreases(t *testing.T) {
// "MAX_STREAMS frames that do not increase the stream limit MUST be ignored."
// https://www.rfc-editor.org/rfc/rfc9000#section-4.6-4
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
ctx := canceledContext()
tc := newTestConn(t, clientSide,
permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxStreamsBidi = 0
p.initialMaxStreamsUni = 0
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
tc.writeFrames(packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 2,
})
tc.writeFrames(packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 1,
})
if _, err := tc.conn.newLocalStream(ctx, styp); err != nil {
t.Fatalf("open stream 1, limit 2, got error: %v", err)
}
if _, err := tc.conn.newLocalStream(ctx, styp); err != nil {
t.Fatalf("open stream 2, limit 2, got error: %v", err)
}
if _, err := tc.conn.newLocalStream(ctx, styp); err == nil {
t.Fatalf("open stream 3, limit 2, got error: %v", err)
}
})
}
func TestStreamLimitViolated(t *testing.T) {
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
tc := newTestConn(t, serverSide,
func(c *Config) {
if styp == bidiStream {
c.MaxBidiRemoteStreams = 10
} else {
c.MaxUniRemoteStreams = 10
}
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, 9),
})
tc.wantIdle("stream number 9 is within the limit")
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, 10),
})
tc.wantFrame("stream number 10 is beyond the limit",
packetType1RTT, debugFrameConnectionCloseTransport{
code: errStreamLimit,
},
)
})
}
func TestStreamLimitImplicitStreams(t *testing.T) {
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
tc := newTestConn(t, serverSide,
func(c *Config) {
c.MaxBidiRemoteStreams = 1 << 60
c.MaxUniRemoteStreams = 1 << 60
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
if got, want := tc.sentTransportParameters.initialMaxStreamsBidi, int64(implicitStreamLimit); got != want {
t.Errorf("sent initial_max_streams_bidi = %v, want %v", got, want)
}
if got, want := tc.sentTransportParameters.initialMaxStreamsUni, int64(implicitStreamLimit); got != want {
t.Errorf("sent initial_max_streams_uni = %v, want %v", got, want)
}
// Create stream 0.
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, 0),
})
tc.wantIdle("max streams not increased enough to send a new frame")
// Create streams [0, implicitStreamLimit).
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, implicitStreamLimit-1),
})
tc.wantFrame("max streams increases to implicit stream limit",
packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 2 * implicitStreamLimit,
})
// Create a stream past the limit.
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, 2*implicitStreamLimit),
})
tc.wantFrame("stream is past the limit",
packetType1RTT, debugFrameConnectionCloseTransport{
code: errStreamLimit,
},
)
})
}
func TestStreamLimitMaxStreamsTransportParameterTooLarge(t *testing.T) {
// "If a max_streams transport parameter [...] is received with
// a value greater than 2^60 [...] the connection MUST be closed
// immediately with a connection error of type TRANSPORT_PARAMETER_ERROR [...]"
// https://www.rfc-editor.org/rfc/rfc9000#section-4.6-2
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
tc := newTestConn(t, serverSide,
func(p *transportParameters) {
if styp == bidiStream {
p.initialMaxStreamsBidi = 1<<60 + 1
} else {
p.initialMaxStreamsUni = 1<<60 + 1
}
})
tc.writeFrames(packetTypeInitial, debugFrameCrypto{
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
})
tc.wantFrame("max streams transport parameter is too large",
packetTypeInitial, debugFrameConnectionCloseTransport{
code: errTransportParameter,
},
)
})
}
func TestStreamLimitMaxStreamsFrameTooLarge(t *testing.T) {
// "If [...] a MAX_STREAMS frame is received with a value
// greater than 2^60 [...] the connection MUST be closed immediately
// with a connection error [...] of type FRAME_ENCODING_ERROR [...]"
// https://www.rfc-editor.org/rfc/rfc9000#section-4.6-2
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
tc := newTestConn(t, serverSide)
tc.handshake()
tc.writeFrames(packetTypeInitial,
debugFrameCrypto{
data: tc.cryptoDataIn[tls.QUICEncryptionLevelInitial],
})
tc.writeFrames(packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 1<<60 + 1,
})
tc.wantFrame("MAX_STREAMS value is too large",
packetType1RTT, debugFrameConnectionCloseTransport{
code: errFrameEncoding,
},
)
})
}
func TestStreamLimitSendUpdatesMaxStreams(t *testing.T) {
testStreamTypesSynctest(t, "", func(t *testing.T, styp streamType) {
tc := newTestConn(t, serverSide, func(c *Config) {
if styp == uniStream {
c.MaxUniRemoteStreams = 4
c.MaxBidiRemoteStreams = 0
} else {
c.MaxUniRemoteStreams = 0
c.MaxBidiRemoteStreams = 4
}
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
var streams []*Stream
for i := 0; i < 4; i++ {
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, styp, int64(i)),
fin: true,
})
streams = append(streams, tc.acceptStream())
}
streams[3].Close()
if styp == bidiStream {
tc.wantFrame("stream is closed",
packetType1RTT, debugFrameStream{
id: streams[3].id,
fin: true,
data: []byte{},
})
tc.writeAckForAll()
}
tc.wantFrame("closing a stream when peer is at limit immediately extends the limit",
packetType1RTT, debugFrameMaxStreams{
streamType: styp,
max: 5,
})
})
}
func TestStreamLimitStopSendingDoesNotUpdateMaxStreams(t *testing.T) {
synctest.Test(t, testStreamLimitStopSendingDoesNotUpdateMaxStreams)
}
func testStreamLimitStopSendingDoesNotUpdateMaxStreams(t *testing.T) {
tc, s := newTestConnAndRemoteStream(t, serverSide, bidiStream, func(c *Config) {
c.MaxBidiRemoteStreams = 1
})
tc.writeFrames(packetType1RTT, debugFrameStream{
id: s.id,
fin: true,
})
s.CloseRead()
tc.writeFrames(packetType1RTT, debugFrameStopSending{
id: s.id,
})
tc.wantFrame("received STOP_SENDING, send RESET_STREAM",
packetType1RTT, debugFrameResetStream{
id: s.id,
})
tc.writeAckForAll()
tc.wantIdle("MAX_STREAMS is not extended until the user fully closes the stream")
s.CloseWrite()
tc.wantFrame("user closing the stream triggers MAX_STREAMS update",
packetType1RTT, debugFrameMaxStreams{
streamType: bidiStream,
max: 2,
})
}