Skip to content

Commit

Permalink
reverseproxy: Ignore context cancel in stream mode (caddyserver#4952)
Browse files Browse the repository at this point in the history
  • Loading branch information
mholt authored and WilczynskiT committed Aug 17, 2022
1 parent 296a108 commit 73ff2f6
Showing 1 changed file with 27 additions and 0 deletions.
27 changes: 27 additions & 0 deletions modules/caddyhttp/reverseproxy/reverseproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ type Handler struct {
// response is recognized as a streaming response, or if its
// content length is -1; for such responses, writes are flushed
// to the client immediately.
//
// Normally, a request will be canceled if the client disconnects
// before the response is received from the backend. If explicitly
// set to -1, client disconnection will be ignored and the request
// will be completed to help facilitate low-latency streaming.
FlushInterval caddy.Duration `json:"flush_interval,omitempty"`

// A list of IP ranges (supports CIDR notation) from which
Expand Down Expand Up @@ -752,6 +757,15 @@ func (h *Handler) reverseProxy(rw http.ResponseWriter, req *http.Request, origRe
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
}

// if FlushInterval is explicitly configured to -1 (i.e. flush continuously to achieve
// low-latency streaming), don't let the transport cancel the request if the client
// disconnects: user probably wants us to finish sending the data to the upstream
// regardless, and we should expect client disconnection in low-latency streaming
// scenarios (see issue #4922)
if h.FlushInterval == -1 {
req = req.WithContext(ignoreClientGoneContext{req.Context(), h.ctx.Done()})
}

// do the round-trip; emit debug log with values we know are
// safe, or if there is no error, emit fuller log entry
start := time.Now()
Expand Down Expand Up @@ -1369,6 +1383,19 @@ type handleResponseContext struct {
isFinalized bool
}

// ignoreClientGoneContext is a special context.Context type
// intended for use when doing a RoundTrip where you don't
// want a client disconnection to cancel the request during
// the roundtrip. Set its done field to a Done() channel
// of a context that doesn't get canceled when the client
// disconnects, such as caddy.Context.Done() instead.
type ignoreClientGoneContext struct {
context.Context
done <-chan struct{}
}

func (c ignoreClientGoneContext) Done() <-chan struct{} { return c.done }

// proxyHandleResponseContextCtxKey is the context key for the active proxy handler
// so that handle_response routes can inherit some config options
// from the proxy handler.
Expand Down

0 comments on commit 73ff2f6

Please sign in to comment.