summaryrefslogtreecommitdiff
path: root/modules/caddyhttp/reverseproxy
diff options
context:
space:
mode:
authorMatt Holt <mholt@users.noreply.github.com>2022-08-12 13:15:41 -0600
committerGitHub <noreply@github.com>2022-08-12 13:15:41 -0600
commitf5dce84a7028d1b116db7fead27ff8b2506baf78 (patch)
tree4db181b0294738a9db2f1d78a02b38db5c15972f /modules/caddyhttp/reverseproxy
parent922d9f5c251a27bc9f76c4b74bde151cc03cc9b3 (diff)
reverseproxy: Ignore context cancel in stream mode (#4952)
Diffstat (limited to 'modules/caddyhttp/reverseproxy')
-rw-r--r--modules/caddyhttp/reverseproxy/reverseproxy.go27
1 files changed, 27 insertions, 0 deletions
diff --git a/modules/caddyhttp/reverseproxy/reverseproxy.go b/modules/caddyhttp/reverseproxy/reverseproxy.go
index 276616b..cc6b530 100644
--- a/modules/caddyhttp/reverseproxy/reverseproxy.go
+++ b/modules/caddyhttp/reverseproxy/reverseproxy.go
@@ -109,6 +109,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
@@ -757,6 +762,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()
@@ -1374,6 +1388,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.