forked from quic-go/quic-go
236 lines
5.6 KiB
Go
236 lines
5.6 KiB
Go
package self_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/quic-go/quic-go"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func setupDeadlineTest(t *testing.T) (serverStr, clientStr quic.Stream) {
|
|
t.Helper()
|
|
server, err := quic.Listen(newUDPConnLocalhost(t), getTLSConfig(), getQuicConfig(nil))
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { server.Close() })
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
conn, err := quic.Dial(ctx, newUDPConnLocalhost(t), server.Addr(), getTLSClientConfig(), getQuicConfig(nil))
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { conn.CloseWithError(0, "") })
|
|
clientStr, err = conn.OpenStream()
|
|
require.NoError(t, err)
|
|
_, err = clientStr.Write([]byte{0}) // need to write one byte so the server learns about the stream
|
|
require.NoError(t, err)
|
|
|
|
serverConn, err := server.Accept(ctx)
|
|
require.NoError(t, err)
|
|
t.Cleanup(func() { serverConn.CloseWithError(0, "") })
|
|
serverStr, err = serverConn.AcceptStream(ctx)
|
|
require.NoError(t, err)
|
|
|
|
_, err = serverStr.Read([]byte{0})
|
|
require.NoError(t, err)
|
|
return serverStr, clientStr
|
|
}
|
|
|
|
func TestReadDeadlineSync(t *testing.T) {
|
|
serverStr, clientStr := setupDeadlineTest(t)
|
|
|
|
const timeout = time.Millisecond
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
_, err := serverStr.Write(PRDataLong)
|
|
errChan <- err
|
|
}()
|
|
|
|
var bytesRead int
|
|
var timeoutCounter int
|
|
buf := make([]byte, 1<<10)
|
|
data := make([]byte, len(PRDataLong))
|
|
clientStr.SetReadDeadline(time.Now().Add(timeout))
|
|
for bytesRead < len(PRDataLong) {
|
|
n, err := clientStr.Read(buf)
|
|
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
|
timeoutCounter++
|
|
clientStr.SetReadDeadline(time.Now().Add(timeout))
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
copy(data[bytesRead:], buf[:n])
|
|
bytesRead += n
|
|
}
|
|
require.Equal(t, PRDataLong, data)
|
|
// make sure the test actually worked and Read actually ran into the deadline a few times
|
|
t.Logf("ran into deadline %d times", timeoutCounter)
|
|
require.GreaterOrEqual(t, timeoutCounter, 10)
|
|
select {
|
|
case err := <-errChan:
|
|
require.NoError(t, err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|
|
|
|
func TestReadDeadlineAsync(t *testing.T) {
|
|
serverStr, clientStr := setupDeadlineTest(t)
|
|
|
|
const timeout = time.Millisecond
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
_, err := serverStr.Write(PRDataLong)
|
|
errChan <- err
|
|
}()
|
|
|
|
var bytesRead int
|
|
var timeoutCounter int
|
|
buf := make([]byte, 1<<10)
|
|
data := make([]byte, len(PRDataLong))
|
|
received := make(chan struct{})
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-received:
|
|
return
|
|
default:
|
|
time.Sleep(timeout)
|
|
}
|
|
clientStr.SetReadDeadline(time.Now().Add(timeout))
|
|
}
|
|
}()
|
|
|
|
for bytesRead < len(PRDataLong) {
|
|
n, err := clientStr.Read(buf)
|
|
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
|
timeoutCounter++
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
copy(data[bytesRead:], buf[:n])
|
|
bytesRead += n
|
|
}
|
|
|
|
require.Equal(t, PRDataLong, data)
|
|
close(received)
|
|
|
|
// make sure the test actually worked and Read actually ran into the deadline a few times
|
|
t.Logf("ran into deadline %d times", timeoutCounter)
|
|
require.GreaterOrEqual(t, timeoutCounter, 10)
|
|
select {
|
|
case err := <-errChan:
|
|
require.NoError(t, err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|
|
|
|
func TestWriteDeadlineSync(t *testing.T) {
|
|
serverStr, clientStr := setupDeadlineTest(t)
|
|
|
|
const timeout = time.Millisecond
|
|
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
defer close(errChan)
|
|
data, err := io.ReadAll(serverStr)
|
|
if err != nil {
|
|
errChan <- err
|
|
}
|
|
if !bytes.Equal(PRDataLong, data) {
|
|
errChan <- fmt.Errorf("data mismatch")
|
|
}
|
|
}()
|
|
|
|
var bytesWritten int
|
|
var timeoutCounter int
|
|
clientStr.SetWriteDeadline(time.Now().Add(timeout))
|
|
for bytesWritten < len(PRDataLong) {
|
|
n, err := clientStr.Write(PRDataLong[bytesWritten:])
|
|
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
|
timeoutCounter++
|
|
clientStr.SetWriteDeadline(time.Now().Add(timeout))
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
bytesWritten += n
|
|
}
|
|
clientStr.Close()
|
|
|
|
// make sure the test actually worked and Write actually ran into the deadline a few times
|
|
t.Logf("ran into deadline %d times", timeoutCounter)
|
|
require.GreaterOrEqual(t, timeoutCounter, 10)
|
|
select {
|
|
case err := <-errChan:
|
|
require.NoError(t, err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|
|
|
|
func TestWriteDeadlineAsync(t *testing.T) {
|
|
serverStr, clientStr := setupDeadlineTest(t)
|
|
|
|
const timeout = time.Millisecond
|
|
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
defer close(errChan)
|
|
data, err := io.ReadAll(serverStr)
|
|
if err != nil {
|
|
errChan <- err
|
|
}
|
|
if !bytes.Equal(PRDataLong, data) {
|
|
errChan <- fmt.Errorf("data mismatch")
|
|
}
|
|
}()
|
|
|
|
clientStr.SetWriteDeadline(time.Now().Add(timeout))
|
|
readDone := make(chan struct{})
|
|
deadlineDone := make(chan struct{})
|
|
go func() {
|
|
defer close(deadlineDone)
|
|
for {
|
|
select {
|
|
case <-readDone:
|
|
return
|
|
default:
|
|
time.Sleep(timeout)
|
|
}
|
|
clientStr.SetWriteDeadline(time.Now().Add(timeout))
|
|
}
|
|
}()
|
|
|
|
var bytesWritten int
|
|
var timeoutCounter int
|
|
clientStr.SetWriteDeadline(time.Now().Add(timeout))
|
|
for bytesWritten < len(PRDataLong) {
|
|
n, err := clientStr.Write(PRDataLong[bytesWritten:])
|
|
if nerr, ok := err.(net.Error); ok && nerr.Timeout() {
|
|
timeoutCounter++
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
bytesWritten += n
|
|
}
|
|
clientStr.Close()
|
|
|
|
close(readDone)
|
|
|
|
// make sure the test actually worked and Write actually ran into the deadline a few times
|
|
t.Logf("ran into deadline %d times", timeoutCounter)
|
|
require.GreaterOrEqual(t, timeoutCounter, 10)
|
|
select {
|
|
case err := <-errChan:
|
|
require.NoError(t, err)
|
|
case <-time.After(time.Second):
|
|
t.Fatal("timeout")
|
|
}
|
|
}
|