forked from quic-go/quic-go
On the server side, the http.Request is consumed by the HTTP handler. The HTTP handler may close the body (it doesn't have to though). In any case, closing the stream is the wrong thing to do, since that closes the write side of the stream. All we want to do is cancel the stream (if the EOF hasn't been read yet).
190 lines
5.0 KiB
Go
190 lines
5.0 KiB
Go
package http3
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/lucas-clemente/quic-go"
|
|
mockquic "github.com/lucas-clemente/quic-go/internal/mocks/quic"
|
|
|
|
. "github.com/onsi/ginkgo"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
type bodyType uint8
|
|
|
|
const (
|
|
bodyTypeRequest bodyType = iota
|
|
bodyTypeResponse
|
|
)
|
|
|
|
func (t bodyType) String() string {
|
|
if t == bodyTypeRequest {
|
|
return "request"
|
|
}
|
|
return "response"
|
|
}
|
|
|
|
var _ = Describe("Body", func() {
|
|
var (
|
|
rb *body
|
|
str *mockquic.MockStream
|
|
buf *bytes.Buffer
|
|
reqDone chan struct{}
|
|
errorCbCalled bool
|
|
)
|
|
|
|
errorCb := func() { errorCbCalled = true }
|
|
|
|
getDataFrame := func(data []byte) []byte {
|
|
b := &bytes.Buffer{}
|
|
(&dataFrame{Length: uint64(len(data))}).Write(b)
|
|
b.Write(data)
|
|
return b.Bytes()
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
buf = &bytes.Buffer{}
|
|
errorCbCalled = false
|
|
})
|
|
|
|
for _, bt := range []bodyType{bodyTypeRequest, bodyTypeResponse} {
|
|
bodyType := bt
|
|
|
|
Context(fmt.Sprintf("using a %s body", bodyType), func() {
|
|
BeforeEach(func() {
|
|
str = mockquic.NewMockStream(mockCtrl)
|
|
str.EXPECT().Write(gomock.Any()).DoAndReturn(func(b []byte) (int, error) {
|
|
return buf.Write(b)
|
|
}).AnyTimes()
|
|
str.EXPECT().Read(gomock.Any()).DoAndReturn(func(b []byte) (int, error) {
|
|
return buf.Read(b)
|
|
}).AnyTimes()
|
|
|
|
switch bodyType {
|
|
case bodyTypeRequest:
|
|
rb = newRequestBody(str, errorCb)
|
|
case bodyTypeResponse:
|
|
reqDone = make(chan struct{})
|
|
rb = newResponseBody(str, reqDone, errorCb)
|
|
}
|
|
})
|
|
|
|
It("reads DATA frames in a single run", func() {
|
|
buf.Write(getDataFrame([]byte("foobar")))
|
|
b := make([]byte, 6)
|
|
n, err := rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(6))
|
|
Expect(b).To(Equal([]byte("foobar")))
|
|
})
|
|
|
|
It("reads DATA frames in multiple runs", func() {
|
|
buf.Write(getDataFrame([]byte("foobar")))
|
|
b := make([]byte, 3)
|
|
n, err := rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(3))
|
|
Expect(b).To(Equal([]byte("foo")))
|
|
n, err = rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(3))
|
|
Expect(b).To(Equal([]byte("bar")))
|
|
})
|
|
|
|
It("reads DATA frames into too large buffers", func() {
|
|
buf.Write(getDataFrame([]byte("foobar")))
|
|
b := make([]byte, 10)
|
|
n, err := rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(6))
|
|
Expect(b[:n]).To(Equal([]byte("foobar")))
|
|
})
|
|
|
|
It("reads DATA frames into too large buffers, in multiple runs", func() {
|
|
buf.Write(getDataFrame([]byte("foobar")))
|
|
b := make([]byte, 4)
|
|
n, err := rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(4))
|
|
Expect(b).To(Equal([]byte("foob")))
|
|
n, err = rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(2))
|
|
Expect(b[:n]).To(Equal([]byte("ar")))
|
|
})
|
|
|
|
It("reads multiple DATA frames", func() {
|
|
buf.Write(getDataFrame([]byte("foo")))
|
|
buf.Write(getDataFrame([]byte("bar")))
|
|
b := make([]byte, 6)
|
|
n, err := rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(3))
|
|
Expect(b[:n]).To(Equal([]byte("foo")))
|
|
n, err = rb.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(3))
|
|
Expect(b[:n]).To(Equal([]byte("bar")))
|
|
})
|
|
|
|
It("skips HEADERS frames", func() {
|
|
buf.Write(getDataFrame([]byte("foo")))
|
|
(&headersFrame{Length: 10}).Write(buf)
|
|
buf.Write(make([]byte, 10))
|
|
buf.Write(getDataFrame([]byte("bar")))
|
|
b := make([]byte, 6)
|
|
n, err := io.ReadFull(rb, b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(6))
|
|
Expect(b).To(Equal([]byte("foobar")))
|
|
})
|
|
|
|
It("errors when it can't parse the frame", func() {
|
|
buf.Write([]byte("invalid"))
|
|
_, err := rb.Read([]byte{0})
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
|
|
It("errors on unexpected frames, and calls the error callback", func() {
|
|
(&settingsFrame{}).Write(buf)
|
|
_, err := rb.Read([]byte{0})
|
|
Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame"))
|
|
Expect(errorCbCalled).To(BeTrue())
|
|
})
|
|
|
|
if bodyType == bodyTypeResponse {
|
|
It("closes the reqDone channel when Read errors", func() {
|
|
buf.Write([]byte("invalid"))
|
|
_, err := rb.Read([]byte{0})
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(reqDone).To(BeClosed())
|
|
})
|
|
|
|
It("allows multiple calls to Read, when Read errors", func() {
|
|
buf.Write([]byte("invalid"))
|
|
_, err := rb.Read([]byte{0})
|
|
Expect(err).To(HaveOccurred())
|
|
Expect(reqDone).To(BeClosed())
|
|
_, err = rb.Read([]byte{0})
|
|
Expect(err).To(HaveOccurred())
|
|
})
|
|
|
|
It("closes responses", func() {
|
|
str.EXPECT().CancelRead(quic.ErrorCode(errorRequestCanceled))
|
|
Expect(rb.Close()).To(Succeed())
|
|
})
|
|
|
|
It("allows multiple calls to Close", func() {
|
|
str.EXPECT().CancelRead(quic.ErrorCode(errorRequestCanceled)).MaxTimes(2)
|
|
Expect(rb.Close()).To(Succeed())
|
|
Expect(reqDone).To(BeClosed())
|
|
Expect(rb.Close()).To(Succeed())
|
|
})
|
|
}
|
|
})
|
|
}
|
|
})
|