No functional change expected.
buf := &bytes.Buffer{}
rstr := NewMockDatagramStream(mockCtrl)
rstr.EXPECT().Write(gomock.Any()).Do(buf.Write).AnyTimes()
- rw := newResponseWriter(newStream(rstr, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
+ rw := newResponseWriter(newStream(rstr, nil, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
rw.WriteHeader(status)
rw.Flush()
return buf.Bytes()
var rspBuf bytes.Buffer
rstr := NewMockDatagramStream(gomock.NewController(t))
rstr.EXPECT().Write(gomock.Any()).Do(rspBuf.Write).AnyTimes()
- rw := newResponseWriter(newStream(rstr, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
+ rw := newResponseWriter(newStream(rstr, nil, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
rw.header.Add("Link", "foo")
rw.header.Add("Link", "bar")
for range numEarlyHints {
var rspBuf bytes.Buffer
rstr := NewMockDatagramStream(gomock.NewController(t))
rstr.EXPECT().Write(gomock.Any()).Do(rspBuf.Write).AnyTimes()
- rw := newResponseWriter(newStream(rstr, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
+ rw := newResponseWriter(newStream(rstr, nil, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
rw.WriteHeader(http.StatusOK)
if responseAddContentEncoding {
rw.header.Add("Content-Encoding", "gzip")
rsp := &http.Response{}
trace := httptrace.ContextClientTrace(ctx)
return newRequestStream(
- newStream(hstr, c, func(r io.Reader, l uint64) error {
+ newStream(hstr, c, trace, func(r io.Reader, l uint64) error {
hdr, err := c.decodeTrailers(r, l, maxHeaderBytes)
if err != nil {
return err
disableCompression,
maxHeaderBytes,
rsp,
- trace,
), nil
}
str.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
str.EXPECT().SetReadDeadline(gomock.Any()).Return(nil).AnyTimes()
str.EXPECT().SetWriteDeadline(gomock.Any()).Return(nil).AnyTimes()
- rw := newResponseWriter(newStream(str, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
+ rw := newResponseWriter(newStream(str, nil, nil, func(r io.Reader, u uint64) error { return nil }), nil, false, nil)
return &testResponseWriter{responseWriter: rw, buf: buf}
}
if _, ok := req.Header["Content-Length"]; ok && req.ContentLength >= 0 {
contentLength = req.ContentLength
}
- hstr := newStream(str, conn, nil)
+ hstr := newStream(str, conn, nil, nil)
body := newRequestBody(hstr, contentLength, conn.Context(), conn.ReceivedSettings(), conn.Settings)
req.Body = body
// When writing to and reading from the stream, data is framed in HTTP/3 DATA frames.
type Stream struct {
datagramStream
- conn *Conn
+ conn *Conn
+ frameParser *frameParser
buf []byte // used as a temporary buffer when writing the HTTP/3 frame headers
parsedTrailer bool
}
-func newStream(str datagramStream, conn *Conn, parseTrailer func(io.Reader, uint64) error) *Stream {
+func newStream(str datagramStream, conn *Conn, trace *httptrace.ClientTrace, parseTrailer func(io.Reader, uint64) error) *Stream {
return &Stream{
datagramStream: str,
conn: conn,
buf: make([]byte, 16),
parseTrailer: parseTrailer,
+ frameParser: &frameParser{
+ closeConn: conn.CloseWithError,
+ r: &tracingReader{Reader: str, trace: trace},
+ },
}
}
reqDone chan<- struct{}
disableCompression bool
response *http.Response
- trace *httptrace.ClientTrace
sentRequest bool
requestedGzip bool
isConnect bool
- firstByte bool
}
func newRequestStream(
disableCompression bool,
maxHeaderBytes uint64,
rsp *http.Response,
- trace *httptrace.ClientTrace,
) *RequestStream {
return &RequestStream{
str: str,
disableCompression: disableCompression,
maxHeaderBytes: maxHeaderBytes,
response: rsp,
- trace: trace,
}
}
// It doesn't set Response.Request and Response.TLS.
// It is invalid to call it after Read has been called.
func (s *RequestStream) ReadResponse() (*http.Response, error) {
- qstr := s.str.datagramStream
- fp := &frameParser{
- closeConn: s.str.conn.CloseWithError,
- r: &tracingReader{
- Reader: qstr,
- first: &s.firstByte,
- trace: s.trace,
- },
- }
- frame, err := fp.ParseNext()
+ frame, err := s.str.frameParser.ParseNext()
if err != nil {
s.str.CancelRead(quic.StreamErrorCode(ErrCodeFrameError))
s.str.CancelWrite(quic.StreamErrorCode(ErrCodeFrameError))
return nil, fmt.Errorf("http3: HEADERS frame too large: %d bytes (max: %d)", hf.Length, s.maxHeaderBytes)
}
headerBlock := make([]byte, hf.Length)
- if _, err := io.ReadFull(qstr, headerBlock); err != nil {
+ if _, err := io.ReadFull(s.str.datagramStream, headerBlock); err != nil {
s.str.CancelRead(quic.StreamErrorCode(ErrCodeRequestIncomplete))
s.str.CancelWrite(quic.StreamErrorCode(ErrCodeRequestIncomplete))
return nil, fmt.Errorf("http3: failed to read response headers: %w", err)
type tracingReader struct {
io.Reader
- first *bool
- trace *httptrace.ClientTrace
+ readFirst bool
+ trace *httptrace.ClientTrace
}
func (r *tracingReader) Read(b []byte) (int, error) {
n, err := r.Reader.Read(b)
- if n > 0 && r.first != nil && !*r.first {
+ if n > 0 && !r.readFirst {
traceGotFirstResponseByte(r.trace)
- *r.first = true
+ r.readFirst = true
}
return n, err
}
nil,
0,
),
+ nil,
func(r io.Reader, u uint64) error { return nil },
)
str := newStream(
qstr,
newConnection(context.Background(), clientConn, false, protocol.PerspectiveClient, nil, 0),
+ nil,
func(r io.Reader, u uint64) error { return nil },
)
mockCtrl := gomock.NewController(t)
qstr := NewMockDatagramStream(mockCtrl)
qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
- str := newStream(qstr, nil, func(r io.Reader, u uint64) error { return nil })
+ str := newStream(qstr, nil, nil, func(r io.Reader, u uint64) error { return nil })
str.Write([]byte("foo"))
str.Write([]byte("foobar"))
newStream(
qstr,
newConnection(context.Background(), clientConn, false, protocol.PerspectiveClient, nil, 0),
+ &httptrace.ClientTrace{},
func(r io.Reader, u uint64) error { return nil },
),
requestWriter,
true,
math.MaxUint64,
&http.Response{},
- &httptrace.ClientTrace{},
)
_, err := str.Read(make([]byte, 100))