refactor HTTP/3 stream handling to use a dedicated stream

Reading from and writing onto this stream applies HTTP/3 DATA framing.
This commit is contained in:
Marten Seemann
2022-05-29 19:22:05 +02:00
parent ccf897e519
commit 04d46526c7
10 changed files with 344 additions and 360 deletions

View File

@@ -298,15 +298,59 @@ func (c *client) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Respon
return rsp, rerr.err
}
func (c *client) sendRequestBody(str Stream, body io.ReadCloser) error {
defer body.Close()
b := make([]byte, bodyCopyBufferSize)
for {
n, rerr := body.Read(b)
if n == 0 {
if rerr == nil {
continue
}
if rerr == io.EOF {
break
}
}
if _, err := str.Write(b[:n]); err != nil {
return err
}
if rerr != nil {
if rerr == io.EOF {
break
}
str.CancelWrite(quic.StreamErrorCode(errorRequestCanceled))
return rerr
}
}
return nil
}
func (c *client) doRequest(req *http.Request, str quic.Stream, opt RoundTripOpt, reqDone chan struct{}) (*http.Response, requestError) {
var requestGzip bool
if !c.opts.DisableCompression && req.Method != "HEAD" && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" {
requestGzip = true
}
if err := c.requestWriter.WriteRequest(str, req, opt.DontCloseRequestStream, requestGzip); err != nil {
if err := c.requestWriter.WriteRequestHeader(str, req, requestGzip); err != nil {
return nil, newStreamError(errorInternalError, err)
}
if req.Body == nil && !opt.DontCloseRequestStream {
str.Close()
}
hstr := newStream(str, func() { c.conn.CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), "") })
if req.Body != nil {
// send the request body asynchronously
go func() {
if err := c.sendRequestBody(hstr, req.Body); err != nil {
c.logger.Errorf("Error writing request: %s", err)
}
if !opt.DontCloseRequestStream {
hstr.Close()
}
}()
}
frame, err := parseNextFrame(str, nil)
if err != nil {
return nil, newStreamError(errorFrameError, err)
@@ -348,9 +392,7 @@ func (c *client) doRequest(req *http.Request, str quic.Stream, opt RoundTripOpt,
res.Header.Add(hf.Name, hf.Value)
}
}
respBody := newResponseBody(str, c.conn, reqDone, func() {
c.conn.CloseWithError(quic.ApplicationErrorCode(errorFrameUnexpected), "")
})
respBody := newResponseBody(hstr, c.conn, reqDone)
// Rules for when to set Content-Length are defined in https://tools.ietf.org/html/rfc7230#section-3.3.2.
_, hasTransferEncoding := res.Header["Transfer-Encoding"]