mirror of
https://github.com/golang/net.git
synced 2026-03-31 10:27:08 +09:00
For golang/go#58547 Change-Id: I119d820824f82bfdd236c6826f960d0c934745ca Reviewed-on: https://go-review.googlesource.com/c/net/+/566295 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
281 lines
5.7 KiB
Go
281 lines
5.7 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.
|
|
|
|
//go:build go1.21
|
|
|
|
package quic
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestCryptoStreamReceive(t *testing.T) {
|
|
data := make([]byte, 1<<20)
|
|
rand.Read(data) // doesn't need to be crypto/rand, but non-deprecated and harmless
|
|
type frame struct {
|
|
start int64
|
|
end int64
|
|
want int
|
|
}
|
|
for _, test := range []struct {
|
|
name string
|
|
frames []frame
|
|
}{{
|
|
name: "linear",
|
|
frames: []frame{{
|
|
start: 0,
|
|
end: 1000,
|
|
want: 1000,
|
|
}, {
|
|
start: 1000,
|
|
end: 2000,
|
|
want: 2000,
|
|
}, {
|
|
// larger than any realistic packet can hold
|
|
start: 2000,
|
|
end: 1 << 20,
|
|
want: 1 << 20,
|
|
}},
|
|
}, {
|
|
name: "out of order",
|
|
frames: []frame{{
|
|
start: 1000,
|
|
end: 2000,
|
|
}, {
|
|
start: 2000,
|
|
end: 3000,
|
|
}, {
|
|
start: 0,
|
|
end: 1000,
|
|
want: 3000,
|
|
}},
|
|
}, {
|
|
name: "resent",
|
|
frames: []frame{{
|
|
start: 0,
|
|
end: 1000,
|
|
want: 1000,
|
|
}, {
|
|
start: 0,
|
|
end: 1000,
|
|
want: 1000,
|
|
}, {
|
|
start: 1000,
|
|
end: 2000,
|
|
want: 2000,
|
|
}, {
|
|
start: 0,
|
|
end: 1000,
|
|
want: 2000,
|
|
}, {
|
|
start: 1000,
|
|
end: 2000,
|
|
want: 2000,
|
|
}},
|
|
}, {
|
|
name: "overlapping",
|
|
frames: []frame{{
|
|
start: 0,
|
|
end: 1000,
|
|
want: 1000,
|
|
}, {
|
|
start: 3000,
|
|
end: 4000,
|
|
want: 1000,
|
|
}, {
|
|
start: 2000,
|
|
end: 3000,
|
|
want: 1000,
|
|
}, {
|
|
start: 1000,
|
|
end: 3000,
|
|
want: 4000,
|
|
}},
|
|
}, {
|
|
name: "resent consumed data",
|
|
frames: []frame{{
|
|
start: 0,
|
|
end: 1000,
|
|
want: 1000,
|
|
}, {
|
|
start: 1000,
|
|
end: 2000,
|
|
want: 2000,
|
|
}, {
|
|
start: 0,
|
|
end: 1000,
|
|
want: 2000,
|
|
}},
|
|
}} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var s cryptoStream
|
|
var got []byte
|
|
for _, f := range test.frames {
|
|
t.Logf("receive [%v,%v)", f.start, f.end)
|
|
s.handleCrypto(
|
|
f.start,
|
|
data[f.start:f.end],
|
|
func(b []byte) error {
|
|
t.Logf("got new bytes [%v,%v)", len(got), len(got)+len(b))
|
|
got = append(got, b...)
|
|
return nil
|
|
},
|
|
)
|
|
if len(got) != f.want {
|
|
t.Fatalf("have bytes [0,%v), want [0,%v)", len(got), f.want)
|
|
}
|
|
for i := range got {
|
|
if got[i] != data[i] {
|
|
t.Fatalf("byte %v of received data = %v, want %v", i, got[i], data[i])
|
|
}
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCryptoStreamSends(t *testing.T) {
|
|
data := make([]byte, 1<<20)
|
|
rand.Read(data) // doesn't need to be crypto/rand, but non-deprecated and harmless
|
|
type (
|
|
sendOp i64range[int64]
|
|
ackOp i64range[int64]
|
|
lossOp i64range[int64]
|
|
)
|
|
for _, test := range []struct {
|
|
name string
|
|
size int64
|
|
ops []any
|
|
wantSend []i64range[int64]
|
|
wantPTOSend []i64range[int64]
|
|
}{{
|
|
name: "writes with data remaining",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 1000},
|
|
sendOp{1000, 2000},
|
|
sendOp{2000, 3000},
|
|
},
|
|
wantSend: []i64range[int64]{
|
|
{3000, 4000},
|
|
},
|
|
wantPTOSend: []i64range[int64]{
|
|
{0, 4000},
|
|
},
|
|
}, {
|
|
name: "lost data is resent",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 1000},
|
|
sendOp{1000, 2000},
|
|
sendOp{2000, 3000},
|
|
sendOp{3000, 4000},
|
|
lossOp{1000, 2000},
|
|
lossOp{3000, 4000},
|
|
},
|
|
wantSend: []i64range[int64]{
|
|
{1000, 2000},
|
|
{3000, 4000},
|
|
},
|
|
wantPTOSend: []i64range[int64]{
|
|
{0, 4000},
|
|
},
|
|
}, {
|
|
name: "acked data at start of range",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 4000},
|
|
ackOp{0, 1000},
|
|
ackOp{1000, 2000},
|
|
ackOp{2000, 3000},
|
|
},
|
|
wantSend: nil,
|
|
wantPTOSend: []i64range[int64]{
|
|
{3000, 4000},
|
|
},
|
|
}, {
|
|
name: "acked data is not resent on pto",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 4000},
|
|
ackOp{1000, 2000},
|
|
},
|
|
wantSend: nil,
|
|
wantPTOSend: []i64range[int64]{
|
|
{0, 1000},
|
|
},
|
|
}, {
|
|
// This is an unusual, but possible scenario:
|
|
// Data is sent, resent, one of the two sends is acked, and the other is lost.
|
|
name: "acked and then lost data is not resent",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 4000},
|
|
sendOp{1000, 2000}, // resent, no-op
|
|
ackOp{1000, 2000},
|
|
lossOp{1000, 2000},
|
|
},
|
|
wantSend: nil,
|
|
wantPTOSend: []i64range[int64]{
|
|
{0, 1000},
|
|
},
|
|
}, {
|
|
// The opposite of the above scenario: data is marked lost, and then acked
|
|
// before being resent.
|
|
name: "lost and then acked data is not resent",
|
|
size: 4000,
|
|
ops: []any{
|
|
sendOp{0, 4000},
|
|
sendOp{1000, 2000}, // resent, no-op
|
|
lossOp{1000, 2000},
|
|
ackOp{1000, 2000},
|
|
},
|
|
wantSend: nil,
|
|
wantPTOSend: []i64range[int64]{
|
|
{0, 1000},
|
|
},
|
|
}} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
var s cryptoStream
|
|
s.write(data[:test.size])
|
|
for _, op := range test.ops {
|
|
switch op := op.(type) {
|
|
case sendOp:
|
|
t.Logf("send [%v,%v)", op.start, op.end)
|
|
b := make([]byte, op.end-op.start)
|
|
s.sendData(op.start, b)
|
|
case ackOp:
|
|
t.Logf("ack [%v,%v)", op.start, op.end)
|
|
s.ackOrLoss(op.start, op.end, packetAcked)
|
|
case lossOp:
|
|
t.Logf("loss [%v,%v)", op.start, op.end)
|
|
s.ackOrLoss(op.start, op.end, packetLost)
|
|
default:
|
|
t.Fatalf("unhandled type %T", op)
|
|
}
|
|
}
|
|
var gotSend []i64range[int64]
|
|
s.dataToSend(true, func(off, size int64) (wrote int64) {
|
|
gotSend = append(gotSend, i64range[int64]{off, off + size})
|
|
return 0
|
|
})
|
|
if !reflect.DeepEqual(gotSend, test.wantPTOSend) {
|
|
t.Fatalf("got data to send on PTO: %v, want %v", gotSend, test.wantPTOSend)
|
|
}
|
|
gotSend = nil
|
|
s.dataToSend(false, func(off, size int64) (wrote int64) {
|
|
gotSend = append(gotSend, i64range[int64]{off, off + size})
|
|
b := make([]byte, size)
|
|
s.sendData(off, b)
|
|
return int64(len(b))
|
|
})
|
|
if !reflect.DeepEqual(gotSend, test.wantSend) {
|
|
t.Fatalf("got data to send: %v, want %v", gotSend, test.wantSend)
|
|
}
|
|
})
|
|
}
|
|
}
|