mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
195 lines
5.1 KiB
Go
195 lines
5.1 KiB
Go
package self_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/quic-go/quic-go"
|
|
quicproxy "github.com/quic-go/quic-go/integrationtests/tools/proxy"
|
|
"github.com/quic-go/quic-go/internal/protocol"
|
|
"github.com/quic-go/quic-go/logging"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestInitialPacketSize(t *testing.T) {
|
|
server := newUPDConnLocalhost(t)
|
|
client := newUPDConnLocalhost(t)
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
quic.Dial(ctx, client, server.LocalAddr(), getTLSClientConfig(), getQuicConfig(&quic.Config{
|
|
InitialPacketSize: 1337,
|
|
}))
|
|
}()
|
|
|
|
buf := make([]byte, 2000)
|
|
n, _, err := server.ReadFrom(buf)
|
|
require.NoError(t, err)
|
|
require.Equal(t, 1337, n)
|
|
|
|
cancel()
|
|
<-done
|
|
}
|
|
|
|
func TestPathMTUDiscovery(t *testing.T) {
|
|
rtt := scaleDuration(5 * time.Millisecond)
|
|
const mtu = 1400
|
|
|
|
ln, err := quic.Listen(
|
|
newUPDConnLocalhost(t),
|
|
getTLSConfig(),
|
|
getQuicConfig(&quic.Config{
|
|
InitialPacketSize: 1234,
|
|
DisablePathMTUDiscovery: true,
|
|
EnableDatagrams: true,
|
|
}),
|
|
)
|
|
require.NoError(t, err)
|
|
defer ln.Close()
|
|
|
|
serverErrChan := make(chan error, 1)
|
|
go func() {
|
|
conn, err := ln.Accept(context.Background())
|
|
if err != nil {
|
|
serverErrChan <- err
|
|
return
|
|
}
|
|
str, err := conn.AcceptStream(context.Background())
|
|
if err != nil {
|
|
serverErrChan <- err
|
|
return
|
|
}
|
|
defer str.Close()
|
|
if _, err := io.Copy(str, str); err != nil {
|
|
serverErrChan <- err
|
|
return
|
|
}
|
|
}()
|
|
|
|
var mx sync.Mutex
|
|
var maxPacketSizeServer int
|
|
var clientPacketSizes []int
|
|
proxy := &quicproxy.Proxy{
|
|
Conn: newUPDConnLocalhost(t),
|
|
ServerAddr: ln.Addr().(*net.UDPAddr),
|
|
DelayPacket: func(_ quicproxy.Direction, _ []byte) time.Duration { return rtt / 2 },
|
|
DropPacket: func(dir quicproxy.Direction, packet []byte) bool {
|
|
if len(packet) > mtu {
|
|
return true
|
|
}
|
|
mx.Lock()
|
|
defer mx.Unlock()
|
|
switch dir {
|
|
case quicproxy.DirectionIncoming:
|
|
clientPacketSizes = append(clientPacketSizes, len(packet))
|
|
case quicproxy.DirectionOutgoing:
|
|
if len(packet) > maxPacketSizeServer {
|
|
maxPacketSizeServer = len(packet)
|
|
}
|
|
}
|
|
return false
|
|
},
|
|
}
|
|
require.NoError(t, proxy.Start())
|
|
defer proxy.Close()
|
|
|
|
// Make sure to use v4-only socket here.
|
|
// We can't reliably set the DF bit on dual-stack sockets on older versions of macOS (before Sequoia).
|
|
tr := &quic.Transport{Conn: newUPDConnLocalhost(t)}
|
|
defer tr.Close()
|
|
|
|
var mtus []logging.ByteCount
|
|
conn, err := tr.Dial(
|
|
context.Background(),
|
|
proxy.LocalAddr(),
|
|
getTLSClientConfig(),
|
|
getQuicConfig(&quic.Config{
|
|
InitialPacketSize: protocol.MinInitialPacketSize,
|
|
EnableDatagrams: true,
|
|
Tracer: func(context.Context, logging.Perspective, quic.ConnectionID) *logging.ConnectionTracer {
|
|
return &logging.ConnectionTracer{
|
|
UpdatedMTU: func(mtu logging.ByteCount, _ bool) { mtus = append(mtus, mtu) },
|
|
}
|
|
},
|
|
}),
|
|
)
|
|
require.NoError(t, err)
|
|
defer conn.CloseWithError(0, "")
|
|
|
|
err = conn.SendDatagram(make([]byte, 2000))
|
|
require.Error(t, err)
|
|
var datagramErr *quic.DatagramTooLargeError
|
|
require.ErrorAs(t, err, &datagramErr)
|
|
initialMaxDatagramSize := datagramErr.MaxDatagramPayloadSize
|
|
|
|
str, err := conn.OpenStream()
|
|
require.NoError(t, err)
|
|
|
|
clientErrChan := make(chan error, 1)
|
|
go func() {
|
|
data, err := io.ReadAll(str)
|
|
if err != nil {
|
|
clientErrChan <- err
|
|
return
|
|
}
|
|
if !bytes.Equal(data, PRDataLong) {
|
|
clientErrChan <- fmt.Errorf("echoed data doesn't match: %x", data)
|
|
return
|
|
}
|
|
clientErrChan <- nil
|
|
}()
|
|
|
|
_, err = str.Write(PRDataLong)
|
|
require.NoError(t, err)
|
|
str.Close()
|
|
|
|
select {
|
|
case err := <-clientErrChan:
|
|
require.NoError(t, err)
|
|
case err := <-serverErrChan:
|
|
t.Fatalf("server error: %v", err)
|
|
case <-time.After(20 * time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
|
|
err = conn.SendDatagram(make([]byte, 2000))
|
|
require.Error(t, err)
|
|
require.ErrorAs(t, err, &datagramErr)
|
|
finalMaxDatagramSize := datagramErr.MaxDatagramPayloadSize
|
|
|
|
mx.Lock()
|
|
defer mx.Unlock()
|
|
require.NotEmpty(t, mtus)
|
|
|
|
maxPacketSizeClient := int(mtus[len(mtus)-1])
|
|
t.Logf("max client packet size: %d, MTU: %d", maxPacketSizeClient, mtu)
|
|
t.Logf("max datagram size: initial: %d, final: %d", initialMaxDatagramSize, finalMaxDatagramSize)
|
|
t.Logf("max server packet size: %d, MTU: %d", maxPacketSizeServer, mtu)
|
|
|
|
require.GreaterOrEqual(t, maxPacketSizeClient, mtu-25)
|
|
const maxDiff = 40 // this includes the 21 bytes for the short header, 16 bytes for the encryption tag, and framing overhead
|
|
require.GreaterOrEqual(t, int(initialMaxDatagramSize), protocol.MinInitialPacketSize-maxDiff)
|
|
require.GreaterOrEqual(t, int(finalMaxDatagramSize), maxPacketSizeClient-maxDiff)
|
|
// MTU discovery was disabled on the server side
|
|
require.Equal(t, 1234, maxPacketSizeServer)
|
|
|
|
var numPacketsLargerThanDiscoveredMTU int
|
|
for _, s := range clientPacketSizes {
|
|
if s > maxPacketSizeClient {
|
|
numPacketsLargerThanDiscoveredMTU++
|
|
}
|
|
}
|
|
// The client shouldn't have sent any packets larger than the MTU it discovered,
|
|
// except for at most one MTU probe packet.
|
|
require.LessOrEqual(t, numPacketsLargerThanDiscoveredMTU, 1)
|
|
}
|