77package http3
88
99import (
10+ "errors"
1011 "io"
1112 "net/http"
1213 "strconv"
14+ "sync"
1315
1416 "golang.org/x/net/internal/httpcommon"
1517)
1618
19+ type roundTripState struct {
20+ cc * ClientConn
21+ st * stream
22+
23+ // Request body, provided by the caller.
24+ onceCloseReqBody sync.Once
25+ reqBody io.ReadCloser
26+
27+ reqBodyWriter bodyWriter
28+
29+ // Response.Body, provided to the caller.
30+ respBody bodyReader
31+
32+ errOnce sync.Once
33+ err error
34+ }
35+
36+ // abort terminates the RoundTrip.
37+ // It returns the first fatal error encountered by the RoundTrip call.
38+ func (rt * roundTripState ) abort (err error ) error {
39+ rt .errOnce .Do (func () {
40+ rt .err = err
41+ switch e := err .(type ) {
42+ case * connectionError :
43+ rt .cc .abort (e )
44+ case * streamError :
45+ rt .st .stream .CloseRead ()
46+ rt .st .stream .Reset (uint64 (e .code ))
47+ default :
48+ rt .st .stream .CloseRead ()
49+ rt .st .stream .Reset (uint64 (errH3NoError ))
50+ }
51+ })
52+ return rt .err
53+ }
54+
55+ // closeReqBody closes the Request.Body, at most once.
56+ func (rt * roundTripState ) closeReqBody () {
57+ if rt .reqBody != nil {
58+ rt .onceCloseReqBody .Do (func () {
59+ rt .reqBody .Close ()
60+ })
61+ }
62+ }
63+
1764// RoundTrip sends a request on the connection.
1865func (cc * ClientConn ) RoundTrip (req * http.Request ) (_ * http.Response , err error ) {
1966 // Each request gets its own QUIC stream.
2067 st , err := newConnStream (req .Context (), cc .qconn , streamTypeRequest )
2168 if err != nil {
2269 return nil , err
2370 }
71+ rt := & roundTripState {
72+ cc : cc ,
73+ st : st ,
74+ }
2475 defer func () {
25- switch e := err .(type ) {
26- case nil :
27- case * connectionError :
28- cc .abort (e )
29- case * streamError :
30- st .stream .CloseRead ()
31- st .stream .Reset (uint64 (e .code ))
32- default :
33- st .stream .CloseRead ()
34- st .stream .Reset (uint64 (errH3NoError ))
76+ if err != nil {
77+ err = rt .abort (err )
3578 }
3679 }()
3780
@@ -64,7 +107,13 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (_ *http.Response, err error)
64107 }
65108
66109 if encr .HasBody {
67- // TODO: Send the request body.
110+ // TODO: Defer sending the request body when "Expect: 100-continue" is set.
111+ rt .reqBody = req .Body
112+ rt .reqBodyWriter .st = st
113+ rt .reqBodyWriter .remain = httpcommon .ActualContentLength (req )
114+ rt .reqBodyWriter .flush = true
115+ rt .reqBodyWriter .name = "request"
116+ go copyRequestBody (rt )
68117 }
69118
70119 // Read the response headers.
@@ -91,14 +140,16 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (_ *http.Response, err error)
91140 if err != nil {
92141 return nil , err
93142 }
143+ rt .respBody .st = st
144+ rt .respBody .remain = contentLength
94145 resp := & http.Response {
95146 Proto : "HTTP/3.0" ,
96147 ProtoMajor : 3 ,
97148 Header : h ,
98149 StatusCode : statusCode ,
99150 Status : strconv .Itoa (statusCode ) + " " + http .StatusText (statusCode ),
100151 ContentLength : contentLength ,
101- Body : io . NopCloser ( nil ), // TODO: read the response body
152+ Body : ( * transportResponseBody )( rt ),
102153 }
103154 // TODO: Automatic Content-Type: gzip decoding.
104155 return resp , nil
@@ -114,6 +165,55 @@ func (cc *ClientConn) RoundTrip(req *http.Request) (_ *http.Response, err error)
114165 }
115166}
116167
168+ func copyRequestBody (rt * roundTripState ) {
169+ defer rt .closeReqBody ()
170+ _ , err := io .Copy (& rt .reqBodyWriter , rt .reqBody )
171+ if closeErr := rt .reqBodyWriter .Close (); err == nil {
172+ err = closeErr
173+ }
174+ if err != nil {
175+ // Something went wrong writing the body.
176+ rt .abort (err )
177+ } else {
178+ // We wrote the whole body.
179+ rt .st .stream .CloseWrite ()
180+ }
181+ }
182+
183+ // transportResponseBody is the Response.Body returned by RoundTrip.
184+ type transportResponseBody roundTripState
185+
186+ // Read is Response.Body.Read.
187+ func (b * transportResponseBody ) Read (p []byte ) (n int , err error ) {
188+ return b .respBody .Read (p )
189+ }
190+
191+ var errRespBodyClosed = errors .New ("response body closed" )
192+
193+ // Close is Response.Body.Close.
194+ // Closing the response body is how the caller signals that they're done with a request.
195+ func (b * transportResponseBody ) Close () error {
196+ rt := (* roundTripState )(b )
197+ // Close the request body, which should wake up copyRequestBody if it's
198+ // currently blocked reading the body.
199+ rt .closeReqBody ()
200+ // Close the request stream, since we're done with the request.
201+ // Reset closes the sending half of the stream.
202+ rt .st .stream .Reset (uint64 (errH3NoError ))
203+ // respBody.Close is responsible for closing the receiving half.
204+ err := rt .respBody .Close ()
205+ if err == nil {
206+ err = errRespBodyClosed
207+ }
208+ err = rt .abort (err )
209+ if err == errRespBodyClosed {
210+ // No other errors occurred before closing Response.Body,
211+ // so consider this a successful request.
212+ return nil
213+ }
214+ return err
215+ }
216+
117217func parseResponseContentLength (method string , statusCode int , h http.Header ) (int64 , error ) {
118218 clens := h ["Content-Length" ]
119219 if len (clens ) == 0 {
0 commit comments