Skip to content

Commit 74a7e6e

Browse files
committed
add Request.CloneWithBody and Request.SetBody
These methods wrap provided `io.Reader`s that aren't already also closers in our own `nopCloser` type, which we already do in NewRequest. This extends our ability to send `Content-Length` with certain in-memory types to other uses of requests. We note in the documentation now that using `SetBody` is preferred over setting the `Body` field directly.) Fixes #104
1 parent bd600e4 commit 74a7e6e

File tree

1 file changed

+40
-20
lines changed

1 file changed

+40
-20
lines changed

fsthttp/request.go

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
214238
func (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+
233265
func 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-
570590
func safePollInterval(d time.Duration) time.Duration {
571591
const (
572592
min = 1 * time.Millisecond

0 commit comments

Comments
 (0)