http3: reduce usage of bytes.Buffer (#3539)

This commit is contained in:
Marten Seemann 2022-09-01 16:39:21 +03:00 committed by GitHub
parent dfd35cb071
commit 62b82789c0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 136 additions and 149 deletions

View file

@ -1,7 +1,6 @@
package http3 package http3
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"errors" "errors"
@ -136,11 +135,11 @@ func (c *client) setupConn() error {
if err != nil { if err != nil {
return err return err
} }
buf := &bytes.Buffer{} b := make([]byte, 0, 64)
quicvarint.Write(buf, streamTypeControlStream) b = quicvarint.Append(b, streamTypeControlStream)
// send the SETTINGS frame // send the SETTINGS frame
(&settingsFrame{Datagram: c.opts.EnableDatagram, Other: c.opts.AdditionalSettings}).Write(buf) b = (&settingsFrame{Datagram: c.opts.EnableDatagram, Other: c.opts.AdditionalSettings}).Append(b)
_, err = str.Write(buf.Bytes()) _, err = str.Write(b)
return err return err
} }

View file

@ -455,11 +455,11 @@ var _ = Describe("Client", func() {
}) })
It("parses the SETTINGS frame", func() { It("parses the SETTINGS frame", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{}).Append(b)
(&settingsFrame{}).Write(buf) r := bytes.NewReader(b)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -521,11 +521,11 @@ var _ = Describe("Client", func() {
}) })
It("errors when the first frame on the control stream is not a SETTINGS frame", func() { It("errors when the first frame on the control stream is not a SETTINGS frame", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&dataFrame{}).Append(b)
(&dataFrame{}).Write(buf) r := bytes.NewReader(b)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -545,13 +545,11 @@ var _ = Describe("Client", func() {
}) })
It("errors when parsing the frame on the control stream fails", func() { It("errors when parsing the frame on the control stream fails", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{}).Append(b)
b := &bytes.Buffer{} r := bytes.NewReader(b[:len(b)-1])
(&settingsFrame{}).Write(b)
buf.Write(b.Bytes()[:b.Len()-1])
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -595,11 +593,11 @@ var _ = Describe("Client", func() {
It("errors when the server advertises datagram support (and we enabled support for it)", func() { It("errors when the server advertises datagram support (and we enabled support for it)", func() {
client.opts.EnableDatagram = true client.opts.EnableDatagram = true
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{Datagram: true}).Append(b)
(&settingsFrame{Datagram: true}).Write(buf) r := bytes.NewReader(b)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -631,16 +629,15 @@ var _ = Describe("Client", func() {
testDone := make(chan struct{}) testDone := make(chan struct{})
getHeadersFrame := func(headers map[string]string) []byte { getHeadersFrame := func(headers map[string]string) []byte {
buf := &bytes.Buffer{}
headerBuf := &bytes.Buffer{} headerBuf := &bytes.Buffer{}
enc := qpack.NewEncoder(headerBuf) enc := qpack.NewEncoder(headerBuf)
for name, value := range headers { for name, value := range headers {
Expect(enc.WriteField(qpack.HeaderField{Name: name, Value: value})).To(Succeed()) Expect(enc.WriteField(qpack.HeaderField{Name: name, Value: value})).To(Succeed())
} }
Expect(enc.Close()).To(Succeed()) Expect(enc.Close()).To(Succeed())
(&headersFrame{Length: uint64(headerBuf.Len())}).Write(buf) b := (&headersFrame{Length: uint64(headerBuf.Len())}).Append(nil)
buf.Write(headerBuf.Bytes()) b = append(b, headerBuf.Bytes()...)
return buf.Bytes() return b
} }
decodeHeader := func(str io.Reader) map[string]string { decodeHeader := func(str io.Reader) map[string]string {
@ -805,18 +802,18 @@ var _ = Describe("Client", func() {
It("sets the Content-Length", func() { It("sets the Content-Length", func() {
done := make(chan struct{}) done := make(chan struct{})
buf := &bytes.Buffer{} b := getHeadersFrame(map[string]string{
buf.Write(getHeadersFrame(map[string]string{
":status": "200", ":status": "200",
"Content-Length": "1337", "Content-Length": "1337",
})) })
(&dataFrame{Length: 0x6}).Write(buf) b = (&dataFrame{Length: 0x6}).Append(b)
buf.Write([]byte("foobar")) b = append(b, []byte("foobar")...)
r := bytes.NewReader(b)
str.EXPECT().Close().Do(func() { close(done) }) str.EXPECT().Close().Do(func() { close(done) })
conn.EXPECT().ConnectionState().Return(quic.ConnectionState{}) conn.EXPECT().ConnectionState().Return(quic.ConnectionState{})
str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1) // when reading the response errors str.EXPECT().CancelWrite(gomock.Any()).MaxTimes(1) // when reading the response errors
// the response body is sent asynchronously, while already reading the response // the response body is sent asynchronously, while already reading the response
str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
req, err := client.RoundTripOpt(req, RoundTripOpt{}) req, err := client.RoundTripOpt(req, RoundTripOpt{})
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(req.ContentLength).To(BeEquivalentTo(1337)) Expect(req.ContentLength).To(BeEquivalentTo(1337))
@ -824,24 +821,24 @@ var _ = Describe("Client", func() {
}) })
It("closes the connection when the first frame is not a HEADERS frame", func() { It("closes the connection when the first frame is not a HEADERS frame", func() {
buf := &bytes.Buffer{} b := (&dataFrame{Length: 0x42}).Append(nil)
(&dataFrame{Length: 0x42}).Write(buf)
conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), gomock.Any()) conn.EXPECT().CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), gomock.Any())
closed := make(chan struct{}) closed := make(chan struct{})
r := bytes.NewReader(b)
str.EXPECT().Close().Do(func() { close(closed) }) str.EXPECT().Close().Do(func() { close(closed) })
str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
_, err := client.RoundTripOpt(req, RoundTripOpt{}) _, err := client.RoundTripOpt(req, RoundTripOpt{})
Expect(err).To(MatchError("expected first frame to be a HEADERS frame")) Expect(err).To(MatchError("expected first frame to be a HEADERS frame"))
Eventually(closed).Should(BeClosed()) Eventually(closed).Should(BeClosed())
}) })
It("cancels the stream when the HEADERS frame is too large", func() { It("cancels the stream when the HEADERS frame is too large", func() {
buf := &bytes.Buffer{} b := (&headersFrame{Length: 1338}).Append(nil)
(&headersFrame{Length: 1338}).Write(buf) r := bytes.NewReader(b)
str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)) str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError))
closed := make(chan struct{}) closed := make(chan struct{})
str.EXPECT().Close().Do(func() { close(closed) }) str.EXPECT().Close().Do(func() { close(closed) })
str.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() str.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
_, err := client.RoundTripOpt(req, RoundTripOpt{}) _, err := client.RoundTripOpt(req, RoundTripOpt{})
Expect(err).To(MatchError("HEADERS frame too large: 1338 bytes (max: 1337)")) Expect(err).To(MatchError("HEADERS frame too large: 1338 bytes (max: 1337)"))
Eventually(closed).Should(BeClosed()) Eventually(closed).Should(BeClosed())

View file

@ -74,18 +74,18 @@ type dataFrame struct {
Length uint64 Length uint64
} }
func (f *dataFrame) Write(b *bytes.Buffer) { func (f *dataFrame) Append(b []byte) []byte {
quicvarint.Write(b, 0x0) b = quicvarint.Append(b, 0x0)
quicvarint.Write(b, f.Length) return quicvarint.Append(b, f.Length)
} }
type headersFrame struct { type headersFrame struct {
Length uint64 Length uint64
} }
func (f *headersFrame) Write(b *bytes.Buffer) { func (f *headersFrame) Append(b []byte) []byte {
quicvarint.Write(b, 0x1) b = quicvarint.Append(b, 0x1)
quicvarint.Write(b, f.Length) return quicvarint.Append(b, f.Length)
} }
const settingDatagram = 0xffd277 const settingDatagram = 0xffd277
@ -142,8 +142,8 @@ func parseSettingsFrame(r io.Reader, l uint64) (*settingsFrame, error) {
return frame, nil return frame, nil
} }
func (f *settingsFrame) Write(b *bytes.Buffer) { func (f *settingsFrame) Append(b []byte) []byte {
quicvarint.Write(b, 0x4) b = quicvarint.Append(b, 0x4)
var l protocol.ByteCount var l protocol.ByteCount
for id, val := range f.Other { for id, val := range f.Other {
l += quicvarint.Len(id) + quicvarint.Len(val) l += quicvarint.Len(id) + quicvarint.Len(val)
@ -151,13 +151,14 @@ func (f *settingsFrame) Write(b *bytes.Buffer) {
if f.Datagram { if f.Datagram {
l += quicvarint.Len(settingDatagram) + quicvarint.Len(1) l += quicvarint.Len(settingDatagram) + quicvarint.Len(1)
} }
quicvarint.Write(b, uint64(l)) b = quicvarint.Append(b, uint64(l))
if f.Datagram { if f.Datagram {
quicvarint.Write(b, settingDatagram) b = quicvarint.Append(b, settingDatagram)
quicvarint.Write(b, 1) b = quicvarint.Append(b, 1)
} }
for id, val := range f.Other { for id, val := range f.Other {
quicvarint.Write(b, id) b = quicvarint.Append(b, id)
quicvarint.Write(b, val) b = quicvarint.Append(b, val)
} }
return b
} }

View file

@ -24,12 +24,12 @@ var _ = Describe("Frames", func() {
} }
It("skips unknown frames", func() { It("skips unknown frames", func() {
data := appendVarInt(nil, 0xdeadbeef) // type byte b := appendVarInt(nil, 0xdeadbeef) // type byte
data = appendVarInt(data, 0x42) b = appendVarInt(b, 0x42)
data = append(data, make([]byte, 0x42)...) b = append(b, make([]byte, 0x42)...)
buf := bytes.NewBuffer(data) b = (&dataFrame{Length: 0x1234}).Append(b)
(&dataFrame{Length: 0x1234}).Write(buf) r := bytes.NewReader(b)
frame, err := parseNextFrame(buf, nil) frame, err := parseNextFrame(r, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1234))) Expect(frame.(*dataFrame).Length).To(Equal(uint64(0x1234)))
@ -46,9 +46,8 @@ var _ = Describe("Frames", func() {
}) })
It("writes", func() { It("writes", func() {
buf := &bytes.Buffer{} b := (&dataFrame{Length: 0xdeadbeef}).Append(nil)
(&dataFrame{Length: 0xdeadbeef}).Write(buf) frame, err := parseNextFrame(bytes.NewReader(b), nil)
frame, err := parseNextFrame(buf, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) Expect(frame).To(BeAssignableToTypeOf(&dataFrame{}))
@ -67,9 +66,8 @@ var _ = Describe("Frames", func() {
}) })
It("writes", func() { It("writes", func() {
buf := &bytes.Buffer{} b := (&headersFrame{Length: 0xdeadbeef}).Append(nil)
(&headersFrame{Length: 0xdeadbeef}).Write(buf) frame, err := parseNextFrame(bytes.NewReader(b), nil)
frame, err := parseNextFrame(buf, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(BeAssignableToTypeOf(&headersFrame{})) Expect(frame).To(BeAssignableToTypeOf(&headersFrame{}))
@ -112,9 +110,7 @@ var _ = Describe("Frames", func() {
99: 999, 99: 999,
13: 37, 13: 37,
}} }}
buf := &bytes.Buffer{} frame, err := parseNextFrame(bytes.NewReader(sf.Append(nil)), nil)
sf.Write(buf)
frame, err := parseNextFrame(buf, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(Equal(sf)) Expect(frame).To(Equal(sf))
}) })
@ -124,10 +120,8 @@ var _ = Describe("Frames", func() {
13: 37, 13: 37,
0xdeadbeef: 0xdecafbad, 0xdeadbeef: 0xdecafbad,
}} }}
buf := &bytes.Buffer{} data := sf.Append(nil)
sf.Write(buf)
data := buf.Bytes()
_, err := parseNextFrame(bytes.NewReader(data), nil) _, err := parseNextFrame(bytes.NewReader(data), nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
@ -177,9 +171,7 @@ var _ = Describe("Frames", func() {
It("writes the H3_DATAGRAM setting", func() { It("writes the H3_DATAGRAM setting", func() {
sf := &settingsFrame{Datagram: true} sf := &settingsFrame{Datagram: true}
buf := &bytes.Buffer{} frame, err := parseNextFrame(bytes.NewReader(sf.Append(nil)), nil)
sf.Write(buf)
frame, err := parseNextFrame(buf, nil)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(frame).To(Equal(sf)) Expect(frame).To(Equal(sf))
}) })
@ -222,16 +214,15 @@ var _ = Describe("Frames", func() {
}) })
It("reads a frame without hijacking the stream", func() { It("reads a frame without hijacking the stream", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, 1337)
quicvarint.Write(buf, 1337)
customFrameContents := []byte("custom frame") customFrameContents := []byte("custom frame")
quicvarint.Write(buf, uint64(len(customFrameContents))) b = quicvarint.Append(b, uint64(len(customFrameContents)))
buf.Write(customFrameContents) b = append(b, customFrameContents...)
(&dataFrame{Length: 6}).Write(buf) b = (&dataFrame{Length: 6}).Append(b)
buf.WriteString("foobar") b = append(b, []byte("foobar")...)
var called bool var called bool
frame, err := parseNextFrame(buf, func(ft FrameType, e error) (hijacked bool, err error) { frame, err := parseNextFrame(bytes.NewReader(b), func(ft FrameType, e error) (hijacked bool, err error) {
Expect(e).ToNot(HaveOccurred()) Expect(e).ToNot(HaveOccurred())
Expect(ft).To(BeEquivalentTo(1337)) Expect(ft).To(BeEquivalentTo(1337))
called = true called = true

View file

@ -1,7 +1,6 @@
package http3 package http3
import ( import (
"bytes"
"fmt" "fmt"
"github.com/lucas-clemente/quic-go" "github.com/lucas-clemente/quic-go"
@ -16,6 +15,8 @@ type Stream quic.Stream
type stream struct { type stream struct {
quic.Stream quic.Stream
buf []byte
onFrameError func() onFrameError func()
bytesRemainingInFrame uint64 bytesRemainingInFrame uint64
} }
@ -23,7 +24,11 @@ type stream struct {
var _ Stream = &stream{} var _ Stream = &stream{}
func newStream(str quic.Stream, onFrameError func()) *stream { func newStream(str quic.Stream, onFrameError func()) *stream {
return &stream{Stream: str, onFrameError: onFrameError} return &stream{
Stream: str,
onFrameError: onFrameError,
buf: make([]byte, 0, 16),
}
} }
func (s *stream) Read(b []byte) (int, error) { func (s *stream) Read(b []byte) (int, error) {
@ -62,9 +67,9 @@ func (s *stream) Read(b []byte) (int, error) {
} }
func (s *stream) Write(b []byte) (int, error) { func (s *stream) Write(b []byte) (int, error) {
buf := &bytes.Buffer{} s.buf = s.buf[:0]
(&dataFrame{Length: uint64(len(b))}).Write(buf) s.buf = (&dataFrame{Length: uint64(len(b))}).Append(s.buf)
if _, err := s.Stream.Write(buf.Bytes()); err != nil { if _, err := s.Stream.Write(s.buf); err != nil {
return 0, err return 0, err
} }
return s.Stream.Write(b) return s.Stream.Write(b)

View file

@ -22,10 +22,8 @@ var _ = Describe("Stream", func() {
errorCb := func() { errorCbCalled = true } errorCb := func() { errorCbCalled = true }
getDataFrame := func(data []byte) []byte { getDataFrame := func(data []byte) []byte {
b := &bytes.Buffer{} b := (&dataFrame{Length: uint64(len(data))}).Append(nil)
(&dataFrame{Length: uint64(len(data))}).Write(b) return append(b, data...)
b.Write(data)
return b.Bytes()
} }
BeforeEach(func() { BeforeEach(func() {
@ -96,15 +94,16 @@ var _ = Describe("Stream", func() {
}) })
It("skips HEADERS frames", func() { It("skips HEADERS frames", func() {
buf.Write(getDataFrame([]byte("foo"))) b := getDataFrame([]byte("foo"))
(&headersFrame{Length: 10}).Write(buf) b = (&headersFrame{Length: 10}).Append(b)
buf.Write(make([]byte, 10)) b = append(b, make([]byte, 10)...)
buf.Write(getDataFrame([]byte("bar"))) b = append(b, getDataFrame([]byte("bar"))...)
b := make([]byte, 6) buf.Write(b)
n, err := io.ReadFull(str, b) r := make([]byte, 6)
n, err := io.ReadFull(str, r)
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(n).To(Equal(6)) Expect(n).To(Equal(6))
Expect(b).To(Equal([]byte("foobar"))) Expect(r).To(Equal([]byte("foobar")))
}) })
It("errors when it can't parse the frame", func() { It("errors when it can't parse the frame", func() {
@ -114,7 +113,8 @@ var _ = Describe("Stream", func() {
}) })
It("errors on unexpected frames, and calls the error callback", func() { It("errors on unexpected frames, and calls the error callback", func() {
(&settingsFrame{}).Write(buf) b := (&settingsFrame{}).Append(nil)
buf.Write(b)
_, err := str.Read([]byte{0}) _, err := str.Read([]byte{0})
Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame")) Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame"))
Expect(errorCbCalled).To(BeTrue()) Expect(errorCbCalled).To(BeTrue())

View file

@ -58,10 +58,9 @@ func (w *requestWriter) writeHeaders(wr io.Writer, req *http.Request, gzip bool)
return err return err
} }
buf := &bytes.Buffer{} b := make([]byte, 0, 128)
hf := headersFrame{Length: uint64(w.headerBuf.Len())} b = (&headersFrame{Length: uint64(w.headerBuf.Len())}).Append(b)
hf.Write(buf) if _, err := wr.Write(b); err != nil {
if _, err := wr.Write(buf.Bytes()); err != nil {
return err return err
} }
_, err := wr.Write(w.headerBuf.Bytes()) _, err := wr.Write(w.headerBuf.Bytes())

View file

@ -15,6 +15,7 @@ import (
type responseWriter struct { type responseWriter struct {
conn quic.Connection conn quic.Connection
bufferedStr *bufio.Writer bufferedStr *bufio.Writer
buf []byte
header http.Header header http.Header
status int // status code passed to WriteHeader status int // status code passed to WriteHeader
@ -32,6 +33,7 @@ var (
func newResponseWriter(str quic.Stream, conn quic.Connection, logger utils.Logger) *responseWriter { func newResponseWriter(str quic.Stream, conn quic.Connection, logger utils.Logger) *responseWriter {
return &responseWriter{ return &responseWriter{
header: http.Header{}, header: http.Header{},
buf: make([]byte, 16),
conn: conn, conn: conn,
bufferedStr: bufio.NewWriter(str), bufferedStr: bufio.NewWriter(str),
logger: logger, logger: logger,
@ -62,10 +64,10 @@ func (w *responseWriter) WriteHeader(status int) {
} }
} }
buf := &bytes.Buffer{} w.buf = w.buf[:0]
(&headersFrame{Length: uint64(headers.Len())}).Write(buf) w.buf = (&headersFrame{Length: uint64(headers.Len())}).Append(w.buf)
w.logger.Infof("Responding with %d", status) w.logger.Infof("Responding with %d", status)
if _, err := w.bufferedStr.Write(buf.Bytes()); err != nil { if _, err := w.bufferedStr.Write(w.buf); err != nil {
w.logger.Errorf("could not write headers frame: %s", err.Error()) w.logger.Errorf("could not write headers frame: %s", err.Error())
} }
if _, err := w.bufferedStr.Write(headers.Bytes()); err != nil { if _, err := w.bufferedStr.Write(headers.Bytes()); err != nil {
@ -84,9 +86,9 @@ func (w *responseWriter) Write(p []byte) (int, error) {
return 0, http.ErrBodyNotAllowed return 0, http.ErrBodyNotAllowed
} }
df := &dataFrame{Length: uint64(len(p))} df := &dataFrame{Length: uint64(len(p))}
buf := &bytes.Buffer{} w.buf = w.buf[:0]
df.Write(buf) w.buf = df.Append(w.buf)
if _, err := w.bufferedStr.Write(buf.Bytes()); err != nil { if _, err := w.bufferedStr.Write(w.buf); err != nil {
return 0, err return 0, err
} }
return w.bufferedStr.Write(p) return w.bufferedStr.Write(p)

View file

@ -1,7 +1,6 @@
package http3 package http3
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"errors" "errors"
@ -413,10 +412,10 @@ func (s *Server) handleConn(conn quic.EarlyConnection) {
s.logger.Debugf("Opening the control stream failed.") s.logger.Debugf("Opening the control stream failed.")
return return
} }
buf := &bytes.Buffer{} b := make([]byte, 0, 64)
quicvarint.Write(buf, streamTypeControlStream) // stream type b = quicvarint.Append(b, streamTypeControlStream) // stream type
(&settingsFrame{Datagram: s.EnableDatagrams, Other: s.AdditionalSettings}).Write(buf) b = (&settingsFrame{Datagram: s.EnableDatagrams, Other: s.AdditionalSettings}).Append(b)
str.Write(buf.Bytes()) str.Write(b)
go s.handleUnidirectionalStreams(conn) go s.handleUnidirectionalStreams(conn)

View file

@ -438,11 +438,11 @@ var _ = Describe("Server", func() {
AfterEach(func() { testDone <- struct{}{} }) AfterEach(func() { testDone <- struct{}{} })
It("parses the SETTINGS frame", func() { It("parses the SETTINGS frame", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{}).Append(b)
(&settingsFrame{}).Write(buf)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() r := bytes.NewReader(b)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -501,11 +501,11 @@ var _ = Describe("Server", func() {
}) })
It("errors when the first frame on the control stream is not a SETTINGS frame", func() { It("errors when the first frame on the control stream is not a SETTINGS frame", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&dataFrame{}).Append(b)
(&dataFrame{}).Write(buf)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() r := bytes.NewReader(b)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -524,13 +524,11 @@ var _ = Describe("Server", func() {
}) })
It("errors when parsing the frame on the control stream fails", func() { It("errors when parsing the frame on the control stream fails", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{}).Append(b)
b := &bytes.Buffer{} r := bytes.NewReader(b[:len(b)-1])
(&settingsFrame{}).Write(b)
buf.Write(b.Bytes()[:b.Len()-1])
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -549,11 +547,11 @@ var _ = Describe("Server", func() {
}) })
It("errors when the client opens a push stream", func() { It("errors when the client opens a push stream", func() {
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypePushStream)
quicvarint.Write(buf, streamTypePushStream) b = (&dataFrame{}).Append(b)
(&dataFrame{}).Write(buf) r := bytes.NewReader(b)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -573,11 +571,11 @@ var _ = Describe("Server", func() {
It("errors when the client advertises datagram support (and we enabled support for it)", func() { It("errors when the client advertises datagram support (and we enabled support for it)", func() {
s.EnableDatagrams = true s.EnableDatagrams = true
buf := &bytes.Buffer{} b := quicvarint.Append(nil, streamTypeControlStream)
quicvarint.Write(buf, streamTypeControlStream) b = (&settingsFrame{Datagram: true}).Append(b)
(&settingsFrame{Datagram: true}).Write(buf) r := bytes.NewReader(b)
controlStr := mockquic.NewMockStream(mockCtrl) controlStr := mockquic.NewMockStream(mockCtrl)
controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes() controlStr.EXPECT().Read(gomock.Any()).DoAndReturn(r.Read).AnyTimes()
conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) { conn.EXPECT().AcceptUniStream(gomock.Any()).DoAndReturn(func(context.Context) (quic.ReceiveStream, error) {
return controlStr, nil return controlStr, nil
}) })
@ -628,11 +626,10 @@ var _ = Describe("Server", func() {
}) })
requestData := encodeRequest(exampleGetRequest) requestData := encodeRequest(exampleGetRequest)
buf := &bytes.Buffer{} b := (&dataFrame{Length: 6}).Append(nil) // add a body
(&dataFrame{Length: 6}).Write(buf) // add a body b = append(b, []byte("foobar")...)
buf.Write([]byte("foobar"))
responseBuf := &bytes.Buffer{} responseBuf := &bytes.Buffer{}
setRequest(append(requestData, buf.Bytes()...)) setRequest(append(requestData, b...))
done := make(chan struct{}) done := make(chan struct{})
str.EXPECT().Context().Return(reqContext) str.EXPECT().Context().Return(reqContext)
str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes()
@ -655,10 +652,9 @@ var _ = Describe("Server", func() {
}) })
requestData := encodeRequest(exampleGetRequest) requestData := encodeRequest(exampleGetRequest)
buf := &bytes.Buffer{} b := (&dataFrame{Length: 6}).Append(nil) // add a body
(&dataFrame{Length: 6}).Write(buf) // add a body b = append(b, []byte("foobar")...)
buf.Write([]byte("foobar")) setRequest(append(requestData, b...))
setRequest(append(requestData, buf.Bytes()...))
str.EXPECT().Context().Return(reqContext) str.EXPECT().Context().Return(reqContext)
str.EXPECT().Write([]byte("foobar")).Return(6, nil) str.EXPECT().Write([]byte("foobar")).Return(6, nil)
@ -673,11 +669,10 @@ var _ = Describe("Server", func() {
}) })
requestData := encodeRequest(exampleGetRequest) requestData := encodeRequest(exampleGetRequest)
buf := &bytes.Buffer{} b := (&dataFrame{Length: 6}).Append(nil) // add a body
(&dataFrame{Length: 6}).Write(buf) // add a body b = append(b, []byte("foobar")...)
buf.Write([]byte("foobar"))
responseBuf := &bytes.Buffer{} responseBuf := &bytes.Buffer{}
setRequest(append(requestData, buf.Bytes()...)) setRequest(append(requestData, b...))
done := make(chan struct{}) done := make(chan struct{})
str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes() str.EXPECT().Write(gomock.Any()).DoAndReturn(responseBuf.Write).AnyTimes()
str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)).Do(func(quic.StreamErrorCode) { close(done) }) str.EXPECT().CancelWrite(quic.StreamErrorCode(errorFrameError)).Do(func(quic.StreamErrorCode) { close(done) })
@ -707,9 +702,8 @@ var _ = Describe("Server", func() {
close(handlerCalled) close(handlerCalled)
}) })
buf := &bytes.Buffer{} b := (&dataFrame{}).Append(nil)
(&dataFrame{}).Write(buf) setRequest(b)
setRequest(buf.Bytes())
str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) { str.EXPECT().Write(gomock.Any()).DoAndReturn(func(p []byte) (int, error) {
return len(p), nil return len(p), nil
}).AnyTimes() }).AnyTimes()