Skip to content

Commit

Permalink
Merge pull request #105 from fastly/joeshaw/clone-with-body
Browse files Browse the repository at this point in the history
  • Loading branch information
joeshaw authored Mar 26, 2024
2 parents bd600e4 + 74a7e6e commit 1a69131
Showing 1 changed file with 40 additions and 20 deletions.
60 changes: 40 additions & 20 deletions fsthttp/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,9 @@ type Request struct {
// reads may return immediately with EOF. For outgoing requests, the body is
// optional. A body may only be read once.
//
// It is possible to assign the unread body of the incoming client request
// to the body field of a different request. When that second request is
// sent, the body will be efficiently streamed from the incoming request.
//
// It is also possible to assign the unread body of a received response to
// the body field of a request, with the same results.
// Prefer using the SetBody method over assigning to this value directly,
// as it enables optimizations when sending outgoing requests. See the
// SetBody documentation for more information.
Body io.ReadCloser

// Host is the hostname parsed from the incoming request URL.
Expand Down Expand Up @@ -116,11 +113,16 @@ func NewRequest(method string, uri string, body io.Reader) (*Request, error) {
return nil, err
}

rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = nopCloser{body}
}

return &Request{
Method: method,
URL: u,
Header: NewHeader(),
Body: makeBodyFor(body),
Body: rc,
Host: u.Host,
}, nil
}
Expand Down Expand Up @@ -209,8 +211,30 @@ func newClientRequest() (*Request, error) {
}, nil
}

// SetBody sets the [Request]'s body to the provided [io.Reader]. Prefer
// using this method over setting the Body field directly, as it enables
// optimizations in the runtime.
//
// If an unread body from an incoming client request is set on an
// outgoing upstream request, the body will be efficiently streamed from
// the incoming request. It is also possible to set the unread body of
// a received response to the body of a request, with the same results.
//
// If the body is set from an in-memory reader such as [bytes.Buffer],
// [bytes.Reader], or [strings.Reader], the runtime will send the
// request with a Content-Length header instead of Transfer-Encoding:
// chunked.
func (req *Request) SetBody(body io.Reader) {
rc, ok := body.(io.ReadCloser)
if !ok && body != nil {
rc = nopCloser{body}
}

req.Body = rc
}

// Clone returns a copy of the request. The returned copy will have a nil Body
// field, and it's URL will have a nil User field.
// field, and its URL will have a nil User field.
func (req *Request) Clone() *Request {
return &Request{
Method: req.Method,
Expand All @@ -230,6 +254,14 @@ func (req *Request) Clone() *Request {
}
}

// CloneWithBody returns a copy of the request, with the Body field set
// to the provided io.Reader. Its URL will have a nil User field.
func (req *Request) CloneWithBody(body io.Reader) *Request {
r := req.Clone()
r.SetBody(body)
return r
}

func cloneURL(u *url.URL) *url.URL {
return &url.URL{
Scheme: u.Scheme,
Expand Down Expand Up @@ -555,18 +587,6 @@ func abiBodyFrom(rc io.ReadCloser) (*fastly.HTTPBody, error) {
return b, nil
}

func makeBodyFor(r io.Reader) io.ReadCloser {
if r == nil {
return nil
}

if b, ok := r.(*fastly.HTTPBody); ok {
return b
}

return nopCloser{r}
}

func safePollInterval(d time.Duration) time.Duration {
const (
min = 1 * time.Millisecond
Expand Down

0 comments on commit 1a69131

Please sign in to comment.