diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7bcc843b..dce52ba7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,7 @@
- [#2273](https://github.com/oauth2-proxy/oauth2-proxy/pull/2273) feat: add Cidaas provider (@Bibob7, @Teko012)
- [#3166](https://github.com/oauth2-proxy/oauth2-proxy/pull/3166) chore(dep): upgrade to latest golang 1.24.6 (@tuunit)
+- [#3156](https://github.com/oauth2-proxy/oauth2-proxy/pull/3156) feat: allow disable-keep-alives configuration for upstream (@jet-go)
# V7.11.0
diff --git a/docs/docs/configuration/alpha_config.md b/docs/docs/configuration/alpha_config.md
index 018a2941..28645ceb 100644
--- a/docs/docs/configuration/alpha_config.md
+++ b/docs/docs/configuration/alpha_config.md
@@ -551,6 +551,7 @@ Requests will be proxied to this upstream if the path matches the request path.
| `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied
to the upstream server.
Defaults to true. |
| `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers
Defaults to true. |
| `timeout` | _[Duration](#duration)_ | Timeout is the maximum duration the server will wait for a response from the upstream server.
Defaults to 30 seconds. |
+| `disableKeepAlives` | _bool_ | DisableKeepAlives disables HTTP keep-alive connections to the upstream server.
Defaults to false. |
### UpstreamConfig
diff --git a/docs/docs/configuration/overview.md b/docs/docs/configuration/overview.md
index 7c216dfb..b159df09 100644
--- a/docs/docs/configuration/overview.md
+++ b/docs/docs/configuration/overview.md
@@ -261,6 +261,7 @@ Provider specific options can be found on their respective subpages.
| flag: `--pass-host-header`
toml: `pass_host_header` | bool | pass the request Host Header to upstream | true |
| flag: `--proxy-websockets`
toml: `proxy_websockets` | bool | enables WebSocket proxying | true |
| flag: `--ssl-upstream-insecure-skip-verify`
toml: `ssl_upstream_insecure_skip_verify` | bool | skip validation of certificates presented when using HTTPS upstreams | false |
+| flag: `--disable-keep-alives`
toml: `disable_keep_alives` | bool | disable HTTP keep-alive connections to the upstream server | false |
| flag: `--upstream-timeout`
toml: `upstream_timeout` | duration | maximum amount of time the server will wait for a response from the upstream | 30s |
| flag: `--upstream`
toml: `upstreams` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://` for static response. Routing is based on the path | |
diff --git a/pkg/apis/options/legacy_options.go b/pkg/apis/options/legacy_options.go
index a2c5f4e3..12975225 100644
--- a/pkg/apis/options/legacy_options.go
+++ b/pkg/apis/options/legacy_options.go
@@ -31,10 +31,11 @@ type LegacyOptions struct {
func NewLegacyOptions() *LegacyOptions {
return &LegacyOptions{
LegacyUpstreams: LegacyUpstreams{
- PassHostHeader: true,
- ProxyWebSockets: true,
- FlushInterval: DefaultUpstreamFlushInterval,
- Timeout: DefaultUpstreamTimeout,
+ PassHostHeader: true,
+ ProxyWebSockets: true,
+ FlushInterval: DefaultUpstreamFlushInterval,
+ Timeout: DefaultUpstreamTimeout,
+ DisableKeepAlives: false,
},
LegacyHeaders: LegacyHeaders{
@@ -105,6 +106,7 @@ type LegacyUpstreams struct {
SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"`
Upstreams []string `flag:"upstream" cfg:"upstreams"`
Timeout time.Duration `flag:"upstream-timeout" cfg:"upstream_timeout"`
+ DisableKeepAlives bool `flag:"disable-keep-alives" cfg:"disable_keep_alives"`
}
func legacyUpstreamsFlagSet() *pflag.FlagSet {
@@ -116,6 +118,7 @@ func legacyUpstreamsFlagSet() *pflag.FlagSet {
flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static:// for static response. Routing is based on the path")
flagSet.Duration("upstream-timeout", DefaultUpstreamTimeout, "maximum amount of time the server will wait for a response from the upstream")
+ flagSet.Bool("disable-keep-alives", false, "disable HTTP keep-alive connections to the upstream server")
return flagSet
}
@@ -144,6 +147,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
ProxyWebSockets: &l.ProxyWebSockets,
FlushInterval: &flushInterval,
Timeout: &timeout,
+ DisableKeepAlives: l.DisableKeepAlives,
}
switch u.Scheme {
@@ -176,6 +180,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
upstream.ProxyWebSockets = nil
upstream.FlushInterval = nil
upstream.Timeout = nil
+ upstream.DisableKeepAlives = false
case "unix":
upstream.Path = "/"
}
diff --git a/pkg/apis/options/legacy_options_test.go b/pkg/apis/options/legacy_options_test.go
index 84d7661f..9481cf95 100644
--- a/pkg/apis/options/legacy_options_test.go
+++ b/pkg/apis/options/legacy_options_test.go
@@ -24,6 +24,7 @@ var _ = Describe("Legacy Options", func() {
legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true
legacyOpts.LegacyUpstreams.Upstreams = []string{"http://foo.bar/baz", "file:///var/lib/website#/bar", "static://204"}
legacyOpts.LegacyProvider.ClientID = "oauth-proxy"
+ legacyOpts.LegacyUpstreams.DisableKeepAlives = false
truth := true
staticCode := 204
@@ -38,6 +39,7 @@ var _ = Describe("Legacy Options", func() {
PassHostHeader: &truth,
ProxyWebSockets: &truth,
Timeout: &timeout,
+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
},
{
ID: "/bar",
@@ -48,6 +50,7 @@ var _ = Describe("Legacy Options", func() {
PassHostHeader: &truth,
ProxyWebSockets: &truth,
Timeout: &timeout,
+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
},
{
ID: "static://204",
@@ -60,6 +63,7 @@ var _ = Describe("Legacy Options", func() {
PassHostHeader: nil,
ProxyWebSockets: nil,
Timeout: nil,
+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
},
},
}
@@ -145,6 +149,7 @@ var _ = Describe("Legacy Options", func() {
proxyWebSockets := true
flushInterval := Duration(5 * time.Second)
timeout := Duration(5 * time.Second)
+ disableKeepAlives := true
// Test cases and expected outcomes
validHTTP := "http://foo.bar/baz"
@@ -157,6 +162,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: &proxyWebSockets,
FlushInterval: &flushInterval,
Timeout: &timeout,
+ DisableKeepAlives: disableKeepAlives,
}
// Test cases and expected outcomes
@@ -170,6 +176,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: &proxyWebSockets,
FlushInterval: &flushInterval,
Timeout: &timeout,
+ DisableKeepAlives: disableKeepAlives,
}
validFileWithFragment := "file:///var/lib/website#/bar"
@@ -182,6 +189,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: &proxyWebSockets,
FlushInterval: &flushInterval,
Timeout: &timeout,
+ DisableKeepAlives: disableKeepAlives,
}
validStatic := "static://204"
@@ -197,6 +205,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: nil,
FlushInterval: nil,
Timeout: nil,
+ DisableKeepAlives: false,
}
invalidStatic := "static://abc"
@@ -212,6 +221,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: nil,
FlushInterval: nil,
Timeout: nil,
+ DisableKeepAlives: false,
}
invalidHTTP := ":foo"
@@ -226,6 +236,7 @@ var _ = Describe("Legacy Options", func() {
ProxyWebSockets: proxyWebSockets,
FlushInterval: time.Duration(flushInterval),
Timeout: time.Duration(timeout),
+ DisableKeepAlives: disableKeepAlives,
}
upstreams, err := legacyUpstreams.convert()
diff --git a/pkg/apis/options/upstreams.go b/pkg/apis/options/upstreams.go
index 971d151e..b3c7195f 100644
--- a/pkg/apis/options/upstreams.go
+++ b/pkg/apis/options/upstreams.go
@@ -93,4 +93,8 @@ type Upstream struct {
// Timeout is the maximum duration the server will wait for a response from the upstream server.
// Defaults to 30 seconds.
Timeout *Duration `json:"timeout,omitempty"`
+
+ // DisableKeepAlives disables HTTP keep-alive connections to the upstream server.
+ // Defaults to false.
+ DisableKeepAlives bool `json:"disableKeepAlives,omitempty"`
}
diff --git a/pkg/upstream/http.go b/pkg/upstream/http.go
index 56fcc23e..7a0e6e84 100644
--- a/pkg/upstream/http.go
+++ b/pkg/upstream/http.go
@@ -166,6 +166,10 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
proxy.ErrorHandler = errorHandler
}
+ // Pass on DisableKeepAlives to the transport settings
+ // to allow for disabling HTTP keep-alive connections
+ transport.DisableKeepAlives = upstream.DisableKeepAlives
+
// Apply the customized transport to our proxy before returning it
proxy.Transport = transport
diff --git a/pkg/upstream/http_test.go b/pkg/upstream/http_test.go
index 16f75f2d..31476df0 100644
--- a/pkg/upstream/http_test.go
+++ b/pkg/upstream/http_test.go
@@ -372,12 +372,13 @@ var _ = Describe("HTTP Upstream Suite", func() {
})
type newUpstreamTableInput struct {
- proxyWebSockets bool
- flushInterval options.Duration
- skipVerify bool
- sigData *options.SignatureData
- errorHandler func(http.ResponseWriter, *http.Request, error)
- timeout options.Duration
+ proxyWebSockets bool
+ flushInterval options.Duration
+ skipVerify bool
+ sigData *options.SignatureData
+ errorHandler func(http.ResponseWriter, *http.Request, error)
+ timeout options.Duration
+ disableKeepAlives bool
}
DescribeTable("newHTTPUpstreamProxy",
@@ -391,6 +392,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
InsecureSkipTLSVerify: in.skipVerify,
ProxyWebSockets: &in.proxyWebSockets,
Timeout: &in.timeout,
+ DisableKeepAlives: in.disableKeepAlives,
}
handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler)
@@ -412,6 +414,9 @@ var _ = Describe("HTTP Upstream Suite", func() {
if in.skipVerify {
Expect(transport.TLSClientConfig.InsecureSkipVerify).To(Equal(true))
}
+ if in.disableKeepAlives {
+ Expect(transport.DisableKeepAlives).To(Equal(true))
+ }
},
Entry("with proxy websockets", &newUpstreamTableInput{
proxyWebSockets: true,
@@ -463,6 +468,15 @@ var _ = Describe("HTTP Upstream Suite", func() {
errorHandler: nil,
timeout: options.Duration(5 * time.Second),
}),
+ Entry("with a DisableKeepAlives", &newUpstreamTableInput{
+ proxyWebSockets: false,
+ flushInterval: defaultFlushInterval,
+ skipVerify: false,
+ sigData: nil,
+ errorHandler: nil,
+ timeout: defaultTimeout,
+ disableKeepAlives: true,
+ }),
)
Context("with a websocket proxy", func() {