Skip to content

Commit

Permalink
Fix hanging caused by Transfer-Encoding: chunked
Browse files Browse the repository at this point in the history
* Fixes #5236
* enable request body buffering in reverse proxy
  when the request header has Transfer-Encoding: chunked
  • Loading branch information
u5surf committed Jan 7, 2023
1 parent e450a73 commit f30bb21
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions modules/caddyhttp/reverseproxy/reverseproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -622,8 +622,8 @@ func (h Handler) prepareRequest(req *http.Request, repl *caddy.Replacer) (*http.
// attacks, so it is strongly recommended to only use this
// feature if absolutely required, if read timeouts are
// set, and if body size is limited
if h.BufferRequests && req.Body != nil {
req.Body = h.bufferedBody(req.Body)
if (h.BufferRequests || isChunkedRequest(req)) && req.Body != nil {
req.Body, req.ContentLength = h.bufferedBody(req.Body)
}

if req.ContentLength == 0 {
Expand Down Expand Up @@ -854,7 +854,7 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe

// if enabled, buffer the response body
if h.BufferResponses {
res.Body = h.bufferedBody(res.Body)
res.Body, _ = h.bufferedBody(res.Body)
}

// see if any response handler is configured for this response from the backend
Expand Down Expand Up @@ -1125,7 +1125,8 @@ func (h Handler) provisionUpstream(upstream *Upstream) {

// bufferedBody reads originalBody into a buffer, then returns a reader for the buffer.
// Always close the return value when done with it, just like if it was the original body!
func (h Handler) bufferedBody(originalBody io.ReadCloser) io.ReadCloser {
func (h Handler) bufferedBody(originalBody io.ReadCloser) (io.ReadCloser, int64) {
var written int64
buf := bufPool.Get().(*bytes.Buffer)
buf.Reset()
if h.MaxBufferSize > 0 {
Expand All @@ -1135,16 +1136,25 @@ func (h Handler) bufferedBody(originalBody io.ReadCloser) io.ReadCloser {
Reader: io.MultiReader(buf, originalBody),
buf: buf,
body: originalBody,
}
}, n
}
} else {
_, _ = io.Copy(buf, originalBody)
written, _ = io.Copy(buf, originalBody)
}
originalBody.Close() // no point in keeping it open
return bodyReadCloser{
Reader: buf,
buf: buf,
}, written
}

func isChunkedRequest(req *http.Request) bool {
for _, transferEncoding := range req.TransferEncoding {
if transferEncoding == "chunked" {
return true
}
}
return false
}

// cloneRequest makes a semi-deep clone of origReq.
Expand Down

0 comments on commit f30bb21

Please # to comment.