git.iko.soy/typeable/oauth2-proxy
on master help sign in
feat: allow disable-keep-alives configuration in upstream (#3156)

Signed-off-by: Jan Larwig <jan@larwig.com>

commitf18a0b7b0744eac3c0696764aa6ccdcc08856b0c authorjet <71936688+jet-go@users.noreply.github.com> dateTue 19 Aug 2025, 14:56 (1 year ago) parent3978b2f
8 files changed+51−10
CHANGELOG.md+1 docs/docs/configuration/alpha_config.md+1 docs/docs/configuration/overview.md+1 pkg/apis/options/legacy_options.go+9−4 pkg/apis/options/legacy_options_test.go+11 pkg/apis/options/upstreams.go+4 pkg/upstream/http.go+4 pkg/upstream/http_test.go+20−6
CHANGELOG.md+1
@@ -13,6 +13,7 @@
1313
1414- [#2273](https://github.com/oauth2-proxy/oauth2-proxy/pull/2273) feat: add Cidaas provider (@Bibob7, @Teko012)
1515- [#3166](https://github.com/oauth2-proxy/oauth2-proxy/pull/3166) chore(dep): upgrade to latest golang 1.24.6 (@tuunit)
16+- [#3156](https://github.com/oauth2-proxy/oauth2-proxy/pull/3156) feat: allow disable-keep-alives configuration for upstream (@jet-go)
1617
1718# V7.11.0
1819
docs/docs/configuration/alpha_config.md+1
@@ -551,6 +551,7 @@ Requests will be proxied to this upstream if the path matches the request path.
551551| `passHostHeader` | _bool_ | PassHostHeader determines whether the request host header should be proxied<br/>to the upstream server.<br/>Defaults to true. |
552552| `proxyWebSockets` | _bool_ | ProxyWebSockets enables proxying of websockets to upstream servers<br/>Defaults to true. |
553553| `timeout` | _[Duration](#duration)_ | Timeout is the maximum duration the server will wait for a response from the upstream server.<br/>Defaults to 30 seconds. |
554+| `disableKeepAlives` | _bool_ | DisableKeepAlives disables HTTP keep-alive connections to the upstream server.<br/>Defaults to false. |
554555
555556### UpstreamConfig
556557
docs/docs/configuration/overview.md+1
@@ -261,6 +261,7 @@ Provider specific options can be found on their respective subpages.
261261| flag: `--pass-host-header`<br/>toml: `pass_host_header` | bool | pass the request Host Header to upstream | true |
262262| flag: `--proxy-websockets`<br/>toml: `proxy_websockets` | bool | enables WebSocket proxying | true |
263263| flag: `--ssl-upstream-insecure-skip-verify`<br/>toml: `ssl_upstream_insecure_skip_verify` | bool | skip validation of certificates presented when using HTTPS upstreams | false |
264+| flag: `--disable-keep-alives`<br/>toml: `disable_keep_alives` | bool | disable HTTP keep-alive connections to the upstream server | false |
264265| flag: `--upstream-timeout`<br/>toml: `upstream_timeout` | duration | maximum amount of time the server will wait for a response from the upstream | 30s |
265266| flag: `--upstream`<br/>toml: `upstreams` | string \| list | the http url(s) of the upstream endpoint, file:// paths for static files or `static://<status_code>` for static response. Routing is based on the path | |
266267
pkg/apis/options/legacy_options.go+9−4
@@ -31,10 +31,11 @@ type LegacyOptions struct {
3131func NewLegacyOptions() *LegacyOptions {
3232 return &LegacyOptions{
3333 LegacyUpstreams: LegacyUpstreams{
34− PassHostHeader: true,
35− ProxyWebSockets: true,
36− FlushInterval: DefaultUpstreamFlushInterval,
37− Timeout: DefaultUpstreamTimeout,
34+ PassHostHeader: true,
35+ ProxyWebSockets: true,
36+ FlushInterval: DefaultUpstreamFlushInterval,
37+ Timeout: DefaultUpstreamTimeout,
38+ DisableKeepAlives: false,
3839 },
3940
4041 LegacyHeaders: LegacyHeaders{
@@ -105,6 +106,7 @@ type LegacyUpstreams struct {
105106 SSLUpstreamInsecureSkipVerify bool `flag:"ssl-upstream-insecure-skip-verify" cfg:"ssl_upstream_insecure_skip_verify"`
106107 Upstreams []string `flag:"upstream" cfg:"upstreams"`
107108 Timeout time.Duration `flag:"upstream-timeout" cfg:"upstream_timeout"`
109+ DisableKeepAlives bool `flag:"disable-keep-alives" cfg:"disable_keep_alives"`
108110}
109111
110112func legacyUpstreamsFlagSet() *pflag.FlagSet {
@@ -116,6 +118,7 @@ func legacyUpstreamsFlagSet() *pflag.FlagSet {
116118 flagSet.Bool("ssl-upstream-insecure-skip-verify", false, "skip validation of certificates presented when using HTTPS upstreams")
117119 flagSet.StringSlice("upstream", []string{}, "the http url(s) of the upstream endpoint, file:// paths for static files or static://<status_code> for static response. Routing is based on the path")
118120 flagSet.Duration("upstream-timeout", DefaultUpstreamTimeout, "maximum amount of time the server will wait for a response from the upstream")
121+ flagSet.Bool("disable-keep-alives", false, "disable HTTP keep-alive connections to the upstream server")
119122
120123 return flagSet
121124}
@@ -144,6 +147,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
144147 ProxyWebSockets: &l.ProxyWebSockets,
145148 FlushInterval: &flushInterval,
146149 Timeout: &timeout,
150+ DisableKeepAlives: l.DisableKeepAlives,
147151 }
148152
149153 switch u.Scheme {
@@ -176,6 +180,7 @@ func (l *LegacyUpstreams) convert() (UpstreamConfig, error) {
176180 upstream.ProxyWebSockets = nil
177181 upstream.FlushInterval = nil
178182 upstream.Timeout = nil
183+ upstream.DisableKeepAlives = false
179184 case "unix":
180185 upstream.Path = "/"
181186 }
pkg/apis/options/legacy_options_test.go+11
@@ -24,6 +24,7 @@ var _ = Describe("Legacy Options", func() {
2424 legacyOpts.LegacyUpstreams.SSLUpstreamInsecureSkipVerify = true
2525 legacyOpts.LegacyUpstreams.Upstreams = []string{"http://foo.bar/baz", "file:///var/lib/website#/bar", "static://204"}
2626 legacyOpts.LegacyProvider.ClientID = "oauth-proxy"
27+ legacyOpts.LegacyUpstreams.DisableKeepAlives = false
2728
2829 truth := true
2930 staticCode := 204
@@ -38,6 +39,7 @@ var _ = Describe("Legacy Options", func() {
3839 PassHostHeader: &truth,
3940 ProxyWebSockets: &truth,
4041 Timeout: &timeout,
42+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
4143 },
4244 {
4345 ID: "/bar",
@@ -48,6 +50,7 @@ var _ = Describe("Legacy Options", func() {
4850 PassHostHeader: &truth,
4951 ProxyWebSockets: &truth,
5052 Timeout: &timeout,
53+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
5154 },
5255 {
5356 ID: "static://204",
@@ -60,6 +63,7 @@ var _ = Describe("Legacy Options", func() {
6063 PassHostHeader: nil,
6164 ProxyWebSockets: nil,
6265 Timeout: nil,
66+ DisableKeepAlives: legacyOpts.LegacyUpstreams.DisableKeepAlives,
6367 },
6468 },
6569 }
@@ -145,6 +149,7 @@ var _ = Describe("Legacy Options", func() {
145149 proxyWebSockets := true
146150 flushInterval := Duration(5 * time.Second)
147151 timeout := Duration(5 * time.Second)
152+ disableKeepAlives := true
148153
149154 // Test cases and expected outcomes
150155 validHTTP := "http://foo.bar/baz"
@@ -157,6 +162,7 @@ var _ = Describe("Legacy Options", func() {
157162 ProxyWebSockets: &proxyWebSockets,
158163 FlushInterval: &flushInterval,
159164 Timeout: &timeout,
165+ DisableKeepAlives: disableKeepAlives,
160166 }
161167
162168 // Test cases and expected outcomes
@@ -170,6 +176,7 @@ var _ = Describe("Legacy Options", func() {
170176 ProxyWebSockets: &proxyWebSockets,
171177 FlushInterval: &flushInterval,
172178 Timeout: &timeout,
179+ DisableKeepAlives: disableKeepAlives,
173180 }
174181
175182 validFileWithFragment := "file:///var/lib/website#/bar"
@@ -182,6 +189,7 @@ var _ = Describe("Legacy Options", func() {
182189 ProxyWebSockets: &proxyWebSockets,
183190 FlushInterval: &flushInterval,
184191 Timeout: &timeout,
192+ DisableKeepAlives: disableKeepAlives,
185193 }
186194
187195 validStatic := "static://204"
@@ -197,6 +205,7 @@ var _ = Describe("Legacy Options", func() {
197205 ProxyWebSockets: nil,
198206 FlushInterval: nil,
199207 Timeout: nil,
208+ DisableKeepAlives: false,
200209 }
201210
202211 invalidStatic := "static://abc"
@@ -212,6 +221,7 @@ var _ = Describe("Legacy Options", func() {
212221 ProxyWebSockets: nil,
213222 FlushInterval: nil,
214223 Timeout: nil,
224+ DisableKeepAlives: false,
215225 }
216226
217227 invalidHTTP := ":foo"
@@ -226,6 +236,7 @@ var _ = Describe("Legacy Options", func() {
226236 ProxyWebSockets: proxyWebSockets,
227237 FlushInterval: time.Duration(flushInterval),
228238 Timeout: time.Duration(timeout),
239+ DisableKeepAlives: disableKeepAlives,
229240 }
230241
231242 upstreams, err := legacyUpstreams.convert()
pkg/apis/options/upstreams.go+4
@@ -93,4 +93,8 @@ type Upstream struct {
9393 // Timeout is the maximum duration the server will wait for a response from the upstream server.
9494 // Defaults to 30 seconds.
9595 Timeout *Duration `json:"timeout,omitempty"`
96+
97+ // DisableKeepAlives disables HTTP keep-alive connections to the upstream server.
98+ // Defaults to false.
99+ DisableKeepAlives bool `json:"disableKeepAlives,omitempty"`
96100}
pkg/upstream/http.go+4
@@ -166,6 +166,10 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
166166 proxy.ErrorHandler = errorHandler
167167 }
168168
169+ // Pass on DisableKeepAlives to the transport settings
170+ // to allow for disabling HTTP keep-alive connections
171+ transport.DisableKeepAlives = upstream.DisableKeepAlives
172+
169173 // Apply the customized transport to our proxy before returning it
170174 proxy.Transport = transport
171175
pkg/upstream/http_test.go+20−6
@@ -372,12 +372,13 @@ var _ = Describe("HTTP Upstream Suite", func() {
372372 })
373373
374374 type newUpstreamTableInput struct {
375− proxyWebSockets bool
376− flushInterval options.Duration
377− skipVerify bool
378− sigData *options.SignatureData
379− errorHandler func(http.ResponseWriter, *http.Request, error)
380− timeout options.Duration
375+ proxyWebSockets bool
376+ flushInterval options.Duration
377+ skipVerify bool
378+ sigData *options.SignatureData
379+ errorHandler func(http.ResponseWriter, *http.Request, error)
380+ timeout options.Duration
381+ disableKeepAlives bool
381382 }
382383
383384 DescribeTable("newHTTPUpstreamProxy",
@@ -391,6 +392,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
391392 InsecureSkipTLSVerify: in.skipVerify,
392393 ProxyWebSockets: &in.proxyWebSockets,
393394 Timeout: &in.timeout,
395+ DisableKeepAlives: in.disableKeepAlives,
394396 }
395397
396398 handler := newHTTPUpstreamProxy(upstream, u, in.sigData, in.errorHandler)
@@ -412,6 +414,9 @@ var _ = Describe("HTTP Upstream Suite", func() {
412414 if in.skipVerify {
413415 Expect(transport.TLSClientConfig.InsecureSkipVerify).To(Equal(true))
414416 }
417+ if in.disableKeepAlives {
418+ Expect(transport.DisableKeepAlives).To(Equal(true))
419+ }
415420 },
416421 Entry("with proxy websockets", &newUpstreamTableInput{
417422 proxyWebSockets: true,
@@ -463,6 +468,15 @@ var _ = Describe("HTTP Upstream Suite", func() {
463468 errorHandler: nil,
464469 timeout: options.Duration(5 * time.Second),
465470 }),
471+ Entry("with a DisableKeepAlives", &newUpstreamTableInput{
472+ proxyWebSockets: false,
473+ flushInterval: defaultFlushInterval,
474+ skipVerify: false,
475+ sigData: nil,
476+ errorHandler: nil,
477+ timeout: defaultTimeout,
478+ disableKeepAlives: true,
479+ }),
466480 )
467481
468482 Context("with a websocket proxy", func() {