blob: 28559b46953a46145b969287e65064b85205bcc1 [file] [log] [blame]
// 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.
//go:build go1.21
package quic
import (
"testing"
)
func TestConnInflowReturnOnRead(t *testing.T) {
ctx := canceledContext()
tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
c.MaxConnReadBufferSize = 64
})
tc.writeFrames(packetType1RTT, debugFrameStream{
id: s.id,
data: make([]byte, 64),
})
const readSize = 8
if n, err := s.ReadContext(ctx, make([]byte, readSize)); n != readSize || err != nil {
t.Fatalf("s.Read() = %v, %v; want %v, nil", n, err, readSize)
}
tc.wantFrame("available window increases, send a MAX_DATA",
packetType1RTT, debugFrameMaxData{
max: 64 + readSize,
})
if n, err := s.ReadContext(ctx, make([]byte, 64)); n != 64-readSize || err != nil {
t.Fatalf("s.Read() = %v, %v; want %v, nil", n, err, 64-readSize)
}
tc.wantFrame("available window increases, send a MAX_DATA",
packetType1RTT, debugFrameMaxData{
max: 128,
})
}
func TestConnInflowReturnOnClose(t *testing.T) {
tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
c.MaxConnReadBufferSize = 64
})
tc.ignoreFrame(frameTypeStopSending)
tc.writeFrames(packetType1RTT, debugFrameStream{
id: s.id,
data: make([]byte, 64),
})
s.CloseRead()
tc.wantFrame("closing stream updates connection-level flow control",
packetType1RTT, debugFrameMaxData{
max: 128,
})
}
func TestConnInflowReturnOnReset(t *testing.T) {
tc, s := newTestConnAndRemoteStream(t, serverSide, uniStream, func(c *Config) {
c.MaxConnReadBufferSize = 64
})
tc.ignoreFrame(frameTypeStopSending)
tc.writeFrames(packetType1RTT, debugFrameStream{
id: s.id,
data: make([]byte, 32),
})
tc.writeFrames(packetType1RTT, debugFrameResetStream{
id: s.id,
finalSize: 64,
})
s.CloseRead()
tc.wantFrame("receiving stream reseet updates connection-level flow control",
packetType1RTT, debugFrameMaxData{
max: 128,
})
}
func TestConnInflowStreamViolation(t *testing.T) {
tc := newTestConn(t, serverSide, func(c *Config) {
c.MaxConnReadBufferSize = 100
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
// Total MAX_DATA consumed: 50
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, bidiStream, 0),
data: make([]byte, 50),
})
// Total MAX_DATA consumed: 80
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, uniStream, 0),
off: 20,
data: make([]byte, 10),
})
// Total MAX_DATA consumed: 100
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, bidiStream, 0),
off: 70,
fin: true,
})
// This stream has already consumed quota for these bytes.
// Total MAX_DATA consumed: 100
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, uniStream, 0),
data: make([]byte, 20),
})
tc.wantIdle("peer has consumed all MAX_DATA quota")
// Total MAX_DATA consumed: 101
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, bidiStream, 2),
data: make([]byte, 1),
})
tc.wantFrame("peer violates MAX_DATA limit",
packetType1RTT, debugFrameConnectionCloseTransport{
code: errFlowControl,
})
}
func TestConnInflowResetViolation(t *testing.T) {
tc := newTestConn(t, serverSide, func(c *Config) {
c.MaxConnReadBufferSize = 100
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
tc.writeFrames(packetType1RTT, debugFrameStream{
id: newStreamID(clientSide, bidiStream, 0),
data: make([]byte, 100),
})
tc.wantIdle("peer has consumed all MAX_DATA quota")
tc.writeFrames(packetType1RTT, debugFrameResetStream{
id: newStreamID(clientSide, uniStream, 0),
finalSize: 0,
})
tc.wantIdle("stream reset does not consume MAX_DATA quota, no error")
tc.writeFrames(packetType1RTT, debugFrameResetStream{
id: newStreamID(clientSide, uniStream, 1),
finalSize: 1,
})
tc.wantFrame("RESET_STREAM final size violates MAX_DATA limit",
packetType1RTT, debugFrameConnectionCloseTransport{
code: errFlowControl,
})
}
func TestConnInflowMultipleStreams(t *testing.T) {
ctx := canceledContext()
tc := newTestConn(t, serverSide, func(c *Config) {
c.MaxConnReadBufferSize = 128
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
var streams []*Stream
for _, id := range []streamID{
newStreamID(clientSide, uniStream, 0),
newStreamID(clientSide, uniStream, 1),
newStreamID(clientSide, bidiStream, 0),
newStreamID(clientSide, bidiStream, 1),
} {
tc.writeFrames(packetType1RTT, debugFrameStream{
id: id,
data: make([]byte, 32),
})
s, err := tc.conn.AcceptStream(ctx)
if err != nil {
t.Fatalf("AcceptStream() = %v", err)
}
streams = append(streams, s)
if n, err := s.ReadContext(ctx, make([]byte, 1)); err != nil || n != 1 {
t.Fatalf("s.Read() = %v, %v; want 1, nil", n, err)
}
}
tc.wantIdle("streams have read data, but not enough to update MAX_DATA")
if n, err := streams[0].ReadContext(ctx, make([]byte, 32)); err != nil || n != 31 {
t.Fatalf("s.Read() = %v, %v; want 31, nil", n, err)
}
tc.wantFrame("read enough data to trigger a MAX_DATA update",
packetType1RTT, debugFrameMaxData{
max: 128 + 32 + 1 + 1 + 1,
})
streams[2].CloseRead()
tc.wantFrame("closed stream triggers another MAX_DATA update",
packetType1RTT, debugFrameMaxData{
max: 128 + 32 + 1 + 32 + 1,
})
}
func TestConnOutflowBlocked(t *testing.T) {
tc, s := newTestConnAndLocalStream(t, clientSide, uniStream,
permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxData = 10
})
tc.ignoreFrame(frameTypeAck)
data := makeTestData(32)
n, err := s.Write(data)
if n != len(data) || err != nil {
t.Fatalf("s.Write() = %v, %v; want %v, nil", n, err, len(data))
}
tc.wantFrame("stream writes data up to MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s.id,
data: data[:10],
})
tc.wantIdle("stream is blocked by MAX_DATA limit")
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 20,
})
tc.wantFrame("stream writes data up to new MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s.id,
off: 10,
data: data[10:20],
})
tc.wantIdle("stream is blocked by new MAX_DATA limit")
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 100,
})
tc.wantFrame("stream writes remaining data",
packetType1RTT, debugFrameStream{
id: s.id,
off: 20,
data: data[20:],
})
}
func TestConnOutflowMaxDataDecreases(t *testing.T) {
tc, s := newTestConnAndLocalStream(t, clientSide, uniStream,
permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxData = 10
})
tc.ignoreFrame(frameTypeAck)
// Decrease in MAX_DATA is ignored.
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 5,
})
data := makeTestData(32)
n, err := s.Write(data)
if n != len(data) || err != nil {
t.Fatalf("s.Write() = %v, %v; want %v, nil", n, err, len(data))
}
tc.wantFrame("stream writes data up to MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s.id,
data: data[:10],
})
}
func TestConnOutflowMaxDataRoundRobin(t *testing.T) {
ctx := canceledContext()
tc := newTestConn(t, clientSide, permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxData = 0
})
tc.handshake()
tc.ignoreFrame(frameTypeAck)
s1, err := tc.conn.newLocalStream(ctx, uniStream)
if err != nil {
t.Fatalf("conn.newLocalStream(%v) = %v", uniStream, err)
}
s2, err := tc.conn.newLocalStream(ctx, uniStream)
if err != nil {
t.Fatalf("conn.newLocalStream(%v) = %v", uniStream, err)
}
s1.Write(make([]byte, 10))
s2.Write(make([]byte, 10))
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 1,
})
tc.wantFrame("stream 1 writes data up to MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s1.id,
data: []byte{0},
})
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 2,
})
tc.wantFrame("stream 2 writes data up to MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s2.id,
data: []byte{0},
})
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 3,
})
tc.wantFrame("stream 1 writes data up to MAX_DATA limit",
packetType1RTT, debugFrameStream{
id: s1.id,
off: 1,
data: []byte{0},
})
}
func TestConnOutflowMetaAndData(t *testing.T) {
tc, s := newTestConnAndLocalStream(t, clientSide, bidiStream,
permissiveTransportParameters,
func(p *transportParameters) {
p.initialMaxData = 0
})
tc.ignoreFrame(frameTypeAck)
data := makeTestData(32)
s.Write(data)
s.CloseRead()
tc.wantFrame("CloseRead sends a STOP_SENDING, not flow controlled",
packetType1RTT, debugFrameStopSending{
id: s.id,
})
tc.writeFrames(packetType1RTT, debugFrameMaxData{
max: 100,
})
tc.wantFrame("unblocked MAX_DATA",
packetType1RTT, debugFrameStream{
id: s.id,
data: data,
})
}