@@ -57,12 +57,9 @@ type Request struct {
5757 // reads may return immediately with EOF. For outgoing requests, the body is
5858 // optional. A body may only be read once.
5959 //
60- // It is possible to assign the unread body of the incoming client request
61- // to the body field of a different request. When that second request is
62- // sent, the body will be efficiently streamed from the incoming request.
63- //
64- // It is also possible to assign the unread body of a received response to
65- // the body field of a request, with the same results.
60+ // Prefer using the SetBody method over assigning to this value directly,
61+ // as it enables optimizations when sending outgoing requests. See the
62+ // SetBody documentation for more information.
6663 Body io.ReadCloser
6764
6865 // Host is the hostname parsed from the incoming request URL.
@@ -116,11 +113,16 @@ func NewRequest(method string, uri string, body io.Reader) (*Request, error) {
116113 return nil , err
117114 }
118115
116+ rc , ok := body .(io.ReadCloser )
117+ if ! ok && body != nil {
118+ rc = nopCloser {body }
119+ }
120+
119121 return & Request {
120122 Method : method ,
121123 URL : u ,
122124 Header : NewHeader (),
123- Body : makeBodyFor ( body ) ,
125+ Body : rc ,
124126 Host : u .Host ,
125127 }, nil
126128}
@@ -209,8 +211,30 @@ func newClientRequest() (*Request, error) {
209211 }, nil
210212}
211213
214+ // SetBody sets the [Request]'s body to the provided [io.Reader]. Prefer
215+ // using this method over setting the Body field directly, as it enables
216+ // optimizations in the runtime.
217+ //
218+ // If an unread body from an incoming client request is set on an
219+ // outgoing upstream request, the body will be efficiently streamed from
220+ // the incoming request. It is also possible to set the unread body of
221+ // a received response to the body of a request, with the same results.
222+ //
223+ // If the body is set from an in-memory reader such as [bytes.Buffer],
224+ // [bytes.Reader], or [strings.Reader], the runtime will send the
225+ // request with a Content-Length header instead of Transfer-Encoding:
226+ // chunked.
227+ func (req * Request ) SetBody (body io.Reader ) {
228+ rc , ok := body .(io.ReadCloser )
229+ if ! ok && body != nil {
230+ rc = nopCloser {body }
231+ }
232+
233+ req .Body = rc
234+ }
235+
212236// Clone returns a copy of the request. The returned copy will have a nil Body
213- // field, and it's URL will have a nil User field.
237+ // field, and its URL will have a nil User field.
214238func (req * Request ) Clone () * Request {
215239 return & Request {
216240 Method : req .Method ,
@@ -230,6 +254,14 @@ func (req *Request) Clone() *Request {
230254 }
231255}
232256
257+ // CloneWithBody returns a copy of the request, with the Body field set
258+ // to the provided io.Reader. Its URL will have a nil User field.
259+ func (req * Request ) CloneWithBody (body io.Reader ) * Request {
260+ r := req .Clone ()
261+ r .SetBody (body )
262+ return r
263+ }
264+
233265func cloneURL (u * url.URL ) * url.URL {
234266 return & url.URL {
235267 Scheme : u .Scheme ,
@@ -555,18 +587,6 @@ func abiBodyFrom(rc io.ReadCloser) (*fastly.HTTPBody, error) {
555587 return b , nil
556588}
557589
558- func makeBodyFor (r io.Reader ) io.ReadCloser {
559- if r == nil {
560- return nil
561- }
562-
563- if b , ok := r .(* fastly.HTTPBody ); ok {
564- return b
565- }
566-
567- return nopCloser {r }
568- }
569-
570590func safePollInterval (d time.Duration ) time.Duration {
571591 const (
572592 min = 1 * time .Millisecond
0 commit comments