forked from quic-go/quic-go
http3: migrate the request writer tests away from Ginkgo (#5069)
This commit is contained in:
@@ -4,116 +4,116 @@ import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
mockquic "github.com/quic-go/quic-go/internal/mocks/quic"
|
||||
"testing"
|
||||
|
||||
"github.com/quic-go/qpack"
|
||||
"go.uber.org/mock/gomock"
|
||||
mockquic "github.com/quic-go/quic-go/internal/mocks/quic"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
var _ = Describe("Request Writer", func() {
|
||||
var (
|
||||
rw *requestWriter
|
||||
str *mockquic.MockStream
|
||||
strBuf *bytes.Buffer
|
||||
)
|
||||
|
||||
decode := func(str io.Reader) map[string]string {
|
||||
fp := frameParser{r: str}
|
||||
frame, err := fp.ParseNext()
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{}))
|
||||
headersFrame := frame.(*headersFrame)
|
||||
data := make([]byte, headersFrame.Length)
|
||||
_, err = io.ReadFull(str, data)
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
decoder := qpack.NewDecoder(nil)
|
||||
hfs, err := decoder.DecodeFull(data)
|
||||
ExpectWithOffset(1, err).ToNot(HaveOccurred())
|
||||
values := make(map[string]string)
|
||||
for _, hf := range hfs {
|
||||
values[hf.Name] = hf.Value
|
||||
}
|
||||
return values
|
||||
func decodeRequest(t *testing.T, str io.Reader) map[string]string {
|
||||
t.Helper()
|
||||
fp := frameParser{r: str}
|
||||
frame, err := fp.ParseNext()
|
||||
require.NoError(t, err)
|
||||
require.IsType(t, &headersFrame{}, frame)
|
||||
headersFrame := frame.(*headersFrame)
|
||||
data := make([]byte, headersFrame.Length)
|
||||
_, err = io.ReadFull(str, data)
|
||||
require.NoError(t, err)
|
||||
decoder := qpack.NewDecoder(nil)
|
||||
hfs, err := decoder.DecodeFull(data)
|
||||
require.NoError(t, err)
|
||||
values := make(map[string]string)
|
||||
for _, hf := range hfs {
|
||||
values[hf.Name] = hf.Value
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
BeforeEach(func() {
|
||||
rw = newRequestWriter()
|
||||
strBuf = &bytes.Buffer{}
|
||||
str = mockquic.NewMockStream(mockCtrl)
|
||||
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
|
||||
func TestRequestWriterGetRequestGzip(t *testing.T) {
|
||||
t.Run("gzip", func(t *testing.T) {
|
||||
testRequestWriterGzip(t, true)
|
||||
})
|
||||
t.Run("no gzip", func(t *testing.T) {
|
||||
testRequestWriterGzip(t, false)
|
||||
})
|
||||
}
|
||||
|
||||
It("writes a GET request", func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
|
||||
headerFields := decode(strBuf)
|
||||
Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":method", "GET"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":path", "/index.html?foo=bar"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":scheme", "https"))
|
||||
Expect(headerFields).ToNot(HaveKey("accept-encoding"))
|
||||
})
|
||||
func testRequestWriterGzip(t *testing.T, gzip bool) {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic-go.net/index.html?foo=bar", nil)
|
||||
require.NoError(t, err)
|
||||
req.AddCookie(&http.Cookie{Name: "foo", Value: "bar"})
|
||||
req.AddCookie(&http.Cookie{Name: "baz", Value: "lorem ipsum"})
|
||||
|
||||
It("rejects invalid host headers", func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
req.Host = "foo@bar" // @ is invalid
|
||||
Expect(rw.WriteRequestHeader(str, req, false)).To(MatchError("http3: invalid Host header"))
|
||||
})
|
||||
rw := newRequestWriter()
|
||||
strBuf := &bytes.Buffer{}
|
||||
mockCtrl := gomock.NewController(t)
|
||||
str := mockquic.NewMockStream(mockCtrl)
|
||||
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
|
||||
require.NoError(t, rw.WriteRequestHeader(str, req, gzip))
|
||||
headerFields := decodeRequest(t, strBuf)
|
||||
require.Equal(t, "quic-go.net", headerFields[":authority"])
|
||||
require.Equal(t, http.MethodGet, headerFields[":method"])
|
||||
require.Equal(t, "/index.html?foo=bar", headerFields[":path"])
|
||||
require.Equal(t, "https", headerFields[":scheme"])
|
||||
require.Equal(t, `foo=bar; baz="lorem ipsum"`, headerFields["cookie"])
|
||||
switch gzip {
|
||||
case true:
|
||||
require.Equal(t, "gzip", headerFields["accept-encoding"])
|
||||
case false:
|
||||
require.NotContains(t, headerFields, "accept-encoding")
|
||||
}
|
||||
}
|
||||
|
||||
It("sends cookies", func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
cookie1 := &http.Cookie{
|
||||
Name: "Cookie #1",
|
||||
Value: "Value #1",
|
||||
}
|
||||
cookie2 := &http.Cookie{
|
||||
Name: "Cookie #2",
|
||||
Value: "Value #2",
|
||||
}
|
||||
req.AddCookie(cookie1)
|
||||
req.AddCookie(cookie2)
|
||||
Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
|
||||
headerFields := decode(strBuf)
|
||||
Expect(headerFields).To(HaveKeyWithValue("cookie", `Cookie #1="Value #1"; Cookie #2="Value #2"`))
|
||||
})
|
||||
func TestRequestWriterInvalidHostHeader(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic-go.net/index.html?foo=bar", nil)
|
||||
require.NoError(t, err)
|
||||
req.Host = "foo@bar" // @ is invalid
|
||||
rw := newRequestWriter()
|
||||
strBuf := &bytes.Buffer{}
|
||||
mockCtrl := gomock.NewController(t)
|
||||
str := mockquic.NewMockStream(mockCtrl)
|
||||
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
|
||||
require.EqualError(t,
|
||||
rw.WriteRequestHeader(str, req, false),
|
||||
"http3: invalid Host header",
|
||||
)
|
||||
}
|
||||
|
||||
It("adds the header for gzip support", func() {
|
||||
req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rw.WriteRequestHeader(str, req, true)).To(Succeed())
|
||||
headerFields := decode(strBuf)
|
||||
Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip"))
|
||||
})
|
||||
func TestRequestWriterConnect(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodConnect, "https://quic-go.net/", nil)
|
||||
require.NoError(t, err)
|
||||
rw := newRequestWriter()
|
||||
strBuf := &bytes.Buffer{}
|
||||
mockCtrl := gomock.NewController(t)
|
||||
str := mockquic.NewMockStream(mockCtrl)
|
||||
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
|
||||
require.NoError(t, rw.WriteRequestHeader(str, req, false))
|
||||
headerFields := decodeRequest(t, strBuf)
|
||||
require.Equal(t, http.MethodConnect, headerFields[":method"])
|
||||
require.Equal(t, "quic-go.net", headerFields[":authority"])
|
||||
require.NotContains(t, headerFields, ":path")
|
||||
require.NotContains(t, headerFields, ":scheme")
|
||||
require.NotContains(t, headerFields, ":protocol")
|
||||
}
|
||||
|
||||
It("writes a CONNECT request", func() {
|
||||
req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
|
||||
headerFields := decode(strBuf)
|
||||
Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
|
||||
Expect(headerFields).ToNot(HaveKey(":path"))
|
||||
Expect(headerFields).ToNot(HaveKey(":scheme"))
|
||||
Expect(headerFields).ToNot(HaveKey(":protocol"))
|
||||
})
|
||||
|
||||
It("writes an Extended CONNECT request", func() {
|
||||
req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/foobar", nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
req.Proto = "webtransport"
|
||||
Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
|
||||
headerFields := decode(strBuf)
|
||||
Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":path", "/foobar"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":scheme", "https"))
|
||||
Expect(headerFields).To(HaveKeyWithValue(":protocol", "webtransport"))
|
||||
})
|
||||
})
|
||||
func TestRequestWriterExtendedConnect(t *testing.T) {
|
||||
req, err := http.NewRequest(http.MethodConnect, "https://quic-go.net/", nil)
|
||||
require.NoError(t, err)
|
||||
req.Proto = "webtransport"
|
||||
rw := newRequestWriter()
|
||||
strBuf := &bytes.Buffer{}
|
||||
mockCtrl := gomock.NewController(t)
|
||||
str := mockquic.NewMockStream(mockCtrl)
|
||||
str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
|
||||
require.NoError(t, rw.WriteRequestHeader(str, req, false))
|
||||
headerFields := decodeRequest(t, strBuf)
|
||||
require.Equal(t, "quic-go.net", headerFields[":authority"])
|
||||
require.Equal(t, http.MethodConnect, headerFields[":method"])
|
||||
require.Equal(t, "/", headerFields[":path"])
|
||||
require.Equal(t, "https", headerFields[":scheme"])
|
||||
require.Equal(t, "webtransport", headerFields[":protocol"])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user