1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
package http3
import (
"bytes"
"io"
"testing"
"time"
"github.com/quic-go/quic-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
)
func TestResponseBodyReading(t *testing.T) {
mockCtrl := gomock.NewController(t)
var buf bytes.Buffer
buf.Write(getDataFrame([]byte("foobar")))
str := NewMockDatagramStream(mockCtrl)
str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
reqDone := make(chan struct{})
rb := newResponseBody(&Stream{datagramStream: str}, -1, reqDone)
data, err := io.ReadAll(rb)
require.NoError(t, err)
require.Equal(t, []byte("foobar"), data)
}
func TestResponseBodyReadError(t *testing.T) {
mockCtrl := gomock.NewController(t)
str := NewMockDatagramStream(mockCtrl)
str.EXPECT().Read(gomock.Any()).Return(0, assert.AnError).Times(2)
reqDone := make(chan struct{})
rb := newResponseBody(&Stream{datagramStream: str}, -1, reqDone)
_, err := rb.Read([]byte{0})
require.ErrorIs(t, err, assert.AnError)
// repeated calls to Read should return the same error
_, err = rb.Read([]byte{0})
require.ErrorIs(t, err, assert.AnError)
select {
case <-reqDone:
default:
t.Fatal("reqDone should be closed")
}
}
func TestResponseBodyClose(t *testing.T) {
mockCtrl := gomock.NewController(t)
str := NewMockDatagramStream(mockCtrl)
str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).Times(2)
reqDone := make(chan struct{})
rb := newResponseBody(&Stream{datagramStream: str}, -1, reqDone)
require.NoError(t, rb.Close())
select {
case <-reqDone:
default:
t.Fatal("reqDone should be closed")
}
// multiple calls to Close should be a no-op
require.NoError(t, rb.Close())
}
func TestResponseBodyConcurrentClose(t *testing.T) {
mockCtrl := gomock.NewController(t)
str := NewMockDatagramStream(mockCtrl)
str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeRequestCanceled)).MaxTimes(3)
reqDone := make(chan struct{})
rb := newResponseBody(&Stream{datagramStream: str}, -1, reqDone)
for range 3 {
go rb.Close()
}
select {
case <-reqDone:
case <-time.After(time.Second):
t.Fatal("reqDone should be closed")
}
}
func TestResponseBodyLengthLimiting(t *testing.T) {
t.Run("along frame boundary", func(t *testing.T) {
testResponseBodyLengthLimiting(t, true)
})
t.Run("in the middle of a frame", func(t *testing.T) {
testResponseBodyLengthLimiting(t, false)
})
}
func testResponseBodyLengthLimiting(t *testing.T, alongFrameBoundary bool) {
var buf bytes.Buffer
buf.Write(getDataFrame([]byte("foo")))
buf.Write(getDataFrame([]byte("bar")))
l := int64(4)
if alongFrameBoundary {
l = 3
}
mockCtrl := gomock.NewController(t)
str := NewMockDatagramStream(mockCtrl)
str.EXPECT().CancelRead(quic.StreamErrorCode(ErrCodeMessageError))
str.EXPECT().CancelWrite(quic.StreamErrorCode(ErrCodeMessageError))
str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
rb := newResponseBody(&Stream{datagramStream: str}, l, make(chan struct{}))
data, err := io.ReadAll(rb)
require.Equal(t, []byte("foobar")[:l], data)
require.ErrorIs(t, err, errTooMuchData)
// check that repeated calls to Read also return the right error
n, err := rb.Read([]byte{0})
require.Zero(t, n)
require.ErrorIs(t, err, errTooMuchData)
}
|