Skip to content

Commit 40472b6

Browse files
authored
feat: add env var to control X-KEDA-HTTP-Cold-Start response header (#1354)
Add KEDA_HTTP_ENABLE_COLD_START_HEADER environment variable to allow users to control whether the X-KEDA-HTTP-Cold-Start response header is included in HTTP responses. Changes: - Add EnableColdStartHeader config option to Serving config (default: true) - Add enableColdStartHeader field to forwardingConfig struct - Conditionally add X-KEDA-HTTP-Cold-Start header based on configuration - Update runProxyServer to accept serving config parameter - Update all tests to reflect default behavior (header enabled by default) - Update CHANGELOG.md with improvement entry - Add comprehensive documentation in docs/operate.md The header is enabled by default to maintain current behavior. Users can disable it by setting KEDA_HTTP_ENABLE_COLD_START_HEADER=false. Fixes #1355 Signed-off-by: Sean Redmond <[email protected]>
1 parent 70a640a commit 40472b6

File tree

7 files changed

+60
-27
lines changed

7 files changed

+60
-27
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This changelog keeps track of work items that have been completed and are ready
3434
### Improvements
3535

3636
- **Interceptor**: Support HTTPScaledObject scoped timeout ([#813](https://github.com/kedacore/http-add-on/issues/813))
37+
- **Interceptor**: Add environment variable to control X-KEDA-HTTP-Cold-Start response header ([#1355](https://github.com/kedacore/http-add-on/issues/1355))
3738

3839
### Fixes
3940

docs/operate.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,17 @@ For setting multiple TLS certs, set `KEDA_HTTP_PROXY_TLS_CERT_STORE_PATHS` with
3131
To disable certificate chain verification, set `KEDA_HTTP_PROXY_TLS_SKIP_VERIFY` to `false`
3232

3333
The matching between certs and requests is performed during the TLS ClientHelo message, where the SNI service name is compared to SANs provided in each cert and the first matching cert will be used for the rest of the TLS handshake.
34+
35+
# Configuring cold start header for the KEDA HTTP Add-on interceptor proxy
36+
37+
The interceptor proxy includes a `X-KEDA-HTTP-Cold-Start` response header in HTTP responses to indicate whether a cold start occurred. By default, this header is **enabled**.
38+
39+
The header can be controlled via the environment variable `KEDA_HTTP_ENABLE_COLD_START_HEADER` on the interceptor deployment (`true` by default). When enabled, the header will contain `true` if a cold start occurred (i.e., the workload was scaled from zero), or `false` if the workload was already running.
40+
41+
To disable the cold start header (for example, to reduce information disclosure in production environments), set `KEDA_HTTP_ENABLE_COLD_START_HEADER` to `false`.
42+
43+
This header is useful for debugging and monitoring cold start behavior.
44+
3445
# Configuring tracing for the KEDA HTTP Add-on interceptor proxy
3546

3647
### Supported Exporters:

interceptor/config/serving.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ type Serving struct {
4949
TLSPort int `envconfig:"KEDA_HTTP_PROXY_TLS_PORT" default:"8443"`
5050
// ProfilingAddr if not empty, pprof will be available on this address, assuming host:port here
5151
ProfilingAddr string `envconfig:"PROFILING_BIND_ADDRESS" default:""`
52+
// EnableColdStartHeader enables/disables the X-KEDA-HTTP-Cold-Start response header
53+
EnableColdStartHeader bool `envconfig:"KEDA_HTTP_ENABLE_COLD_START_HEADER" default:"true"`
5254
}
5355

5456
// Parse parses standard configs using envconfig and returns a pointer to the

interceptor/main.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func main() {
198198

199199
setupLog.Info("starting the proxy server with TLS enabled", "port", proxyTLSPort)
200200

201-
if err := runProxyServer(ctx, ctrl.Log, queues, waitFunc, routingTable, svcCache, timeoutCfg, proxyTLSPort, proxyTLSEnabled, proxyTLSConfig, tracingCfg); !util.IsIgnoredErr(err) {
201+
if err := runProxyServer(ctx, ctrl.Log, queues, waitFunc, routingTable, svcCache, timeoutCfg, servingCfg, proxyTLSPort, proxyTLSEnabled, proxyTLSConfig, tracingCfg); !util.IsIgnoredErr(err) {
202202
setupLog.Error(err, "tls proxy server failed")
203203
return err
204204
}
@@ -212,7 +212,7 @@ func main() {
212212
setupLog.Info("starting the proxy server with TLS disabled", "port", proxyPort)
213213

214214
k8sSharedInformerFactory.WaitForCacheSync(ctx.Done())
215-
if err := runProxyServer(ctx, ctrl.Log, queues, waitFunc, routingTable, svcCache, timeoutCfg, proxyPort, false, nil, tracingCfg); !util.IsIgnoredErr(err) {
215+
if err := runProxyServer(ctx, ctrl.Log, queues, waitFunc, routingTable, svcCache, timeoutCfg, servingCfg, proxyPort, false, nil, tracingCfg); !util.IsIgnoredErr(err) {
216216
setupLog.Error(err, "proxy server failed")
217217
return err
218218
}
@@ -405,6 +405,7 @@ func runProxyServer(
405405
routingTable routing.Table,
406406
svcCache k8s.ServiceCache,
407407
timeouts *config.Timeouts,
408+
serving *config.Serving,
408409
port int,
409410
tlsEnabled bool,
410411
tlsConfig map[string]interface{},
@@ -440,7 +441,7 @@ func runProxyServer(
440441
logger,
441442
dialContextFunc,
442443
waitFunc,
443-
newForwardingConfigFromTimeouts(timeouts),
444+
newForwardingConfigFromTimeouts(timeouts, serving),
444445
forwardingTLSCfg,
445446
tracingConfig,
446447
)

interceptor/main_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ func TestRunProxyServerCountMiddleware(t *testing.T) {
9090
routingTable,
9191
svcCache,
9292
timeouts,
93+
&config.Serving{EnableColdStartHeader: true},
9394
port,
9495
false,
9596
map[string]interface{}{},
@@ -230,6 +231,7 @@ func TestRunProxyServerWithTLSCountMiddleware(t *testing.T) {
230231
routingTable,
231232
svcCache,
232233
timeouts,
234+
&config.Serving{EnableColdStartHeader: true},
233235
port,
234236
true,
235237
map[string]interface{}{"certificatePath": "../certs/tls.crt", "keyPath": "../certs/tls.key", "skipVerify": true},
@@ -380,6 +382,7 @@ func TestRunProxyServerWithMultipleCertsTLSCountMiddleware(t *testing.T) {
380382
routingTable,
381383
svcCache,
382384
timeouts,
385+
&config.Serving{EnableColdStartHeader: true},
383386
port,
384387
true,
385388
map[string]interface{}{"certstorePaths": "../certs"},

interceptor/proxy_handlers.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ type forwardingConfig struct {
2525
idleConnTimeout time.Duration
2626
tlsHandshakeTimeout time.Duration
2727
expectContinueTimeout time.Duration
28+
enableColdStartHeader bool
2829
}
2930

30-
func newForwardingConfigFromTimeouts(t *config.Timeouts) forwardingConfig {
31+
func newForwardingConfigFromTimeouts(t *config.Timeouts, s *config.Serving) forwardingConfig {
3132
return forwardingConfig{
3233
waitTimeout: t.WorkloadReplicas,
3334
respHeaderTimeout: t.ResponseHeader,
@@ -36,6 +37,7 @@ func newForwardingConfigFromTimeouts(t *config.Timeouts) forwardingConfig {
3637
idleConnTimeout: t.IdleConnTimeout,
3738
tlsHandshakeTimeout: t.TLSHandshakeTimeout,
3839
expectContinueTimeout: t.ExpectContinueTimeout,
40+
enableColdStartHeader: s.EnableColdStartHeader,
3941
}
4042
}
4143

@@ -101,7 +103,9 @@ func newForwardingHandler(
101103
}
102104
return
103105
}
104-
w.Header().Add("X-KEDA-HTTP-Cold-Start", strconv.FormatBool(isColdStart))
106+
if fwdCfg.enableColdStartHeader {
107+
w.Header().Add("X-KEDA-HTTP-Cold-Start", strconv.FormatBool(isColdStart))
108+
}
105109
r.Header.Add("X-KEDA-HTTP-Cold-Start-Ref-Name", httpso.Spec.ScaleTargetRef.Name)
106110
r.Header.Add("X-KEDA-HTTP-Cold-Start-Ref-Namespace", httpso.Namespace)
107111

interceptor/proxy_handlers_test.go

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,9 @@ func TestImmediatelySuccessfulProxy(t *testing.T) {
7575
dialCtxFunc,
7676
waitFunc,
7777
forwardingConfig{
78-
waitTimeout: timeouts.WorkloadReplicas,
79-
respHeaderTimeout: timeouts.ResponseHeader,
78+
waitTimeout: timeouts.WorkloadReplicas,
79+
respHeaderTimeout: timeouts.ResponseHeader,
80+
enableColdStartHeader: true,
8081
},
8182
&tls.Config{},
8283
&config.Tracing{},
@@ -127,8 +128,9 @@ func TestImmediatelySuccessfulProxyTLS(t *testing.T) {
127128
dialCtxFunc,
128129
waitFunc,
129130
forwardingConfig{
130-
waitTimeout: timeouts.WorkloadReplicas,
131-
respHeaderTimeout: timeouts.ResponseHeader,
131+
waitTimeout: timeouts.WorkloadReplicas,
132+
respHeaderTimeout: timeouts.ResponseHeader,
133+
enableColdStartHeader: true,
132134
},
133135
&TestTLSConfig,
134136
&config.Tracing{},
@@ -183,8 +185,9 @@ func TestImmediatelySuccessfulFailoverProxy(t *testing.T) {
183185
dialCtxFunc,
184186
waitFunc,
185187
forwardingConfig{
186-
waitTimeout: 0,
187-
respHeaderTimeout: timeouts.ResponseHeader,
188+
waitTimeout: 0,
189+
respHeaderTimeout: timeouts.ResponseHeader,
190+
enableColdStartHeader: true,
188191
},
189192
&tls.Config{},
190193
&config.Tracing{},
@@ -243,8 +246,9 @@ func TestWaitFailedConnection(t *testing.T) {
243246
dialCtxFunc,
244247
waitFunc,
245248
forwardingConfig{
246-
waitTimeout: timeouts.WorkloadReplicas,
247-
respHeaderTimeout: timeouts.ResponseHeader,
249+
waitTimeout: timeouts.WorkloadReplicas,
250+
respHeaderTimeout: timeouts.ResponseHeader,
251+
enableColdStartHeader: true,
248252
},
249253
&tls.Config{},
250254
&config.Tracing{},
@@ -294,8 +298,9 @@ func TestWaitFailedConnectionTLS(t *testing.T) {
294298
dialCtxFunc,
295299
waitFunc,
296300
forwardingConfig{
297-
waitTimeout: timeouts.WorkloadReplicas,
298-
respHeaderTimeout: timeouts.ResponseHeader,
301+
waitTimeout: timeouts.WorkloadReplicas,
302+
respHeaderTimeout: timeouts.ResponseHeader,
303+
enableColdStartHeader: true,
299304
},
300305
&TestTLSConfig,
301306
&config.Tracing{},
@@ -346,8 +351,9 @@ func TestTimesOutOnWaitFunc(t *testing.T) {
346351
dialCtxFunc,
347352
waitFunc,
348353
forwardingConfig{
349-
waitTimeout: timeouts.WorkloadReplicas,
350-
respHeaderTimeout: timeouts.ResponseHeader,
354+
waitTimeout: timeouts.WorkloadReplicas,
355+
respHeaderTimeout: timeouts.ResponseHeader,
356+
enableColdStartHeader: true,
351357
},
352358
&tls.Config{},
353359
&config.Tracing{},
@@ -419,8 +425,9 @@ func TestTimesOutOnWaitFuncTLS(t *testing.T) {
419425
dialCtxFunc,
420426
waitFunc,
421427
forwardingConfig{
422-
waitTimeout: timeouts.WorkloadReplicas,
423-
respHeaderTimeout: timeouts.ResponseHeader,
428+
waitTimeout: timeouts.WorkloadReplicas,
429+
respHeaderTimeout: timeouts.ResponseHeader,
430+
enableColdStartHeader: true,
424431
},
425432
&TestTLSConfig,
426433
&config.Tracing{},
@@ -503,8 +510,9 @@ func TestWaitsForWaitFunc(t *testing.T) {
503510
dialCtxFunc,
504511
waitFunc,
505512
forwardingConfig{
506-
waitTimeout: timeouts.WorkloadReplicas,
507-
respHeaderTimeout: timeouts.ResponseHeader,
513+
waitTimeout: timeouts.WorkloadReplicas,
514+
respHeaderTimeout: timeouts.ResponseHeader,
515+
enableColdStartHeader: true,
508516
},
509517
&tls.Config{},
510518
&config.Tracing{},
@@ -570,8 +578,9 @@ func TestWaitsForWaitFuncTLS(t *testing.T) {
570578
dialCtxFunc,
571579
waitFunc,
572580
forwardingConfig{
573-
waitTimeout: timeouts.WorkloadReplicas,
574-
respHeaderTimeout: timeouts.ResponseHeader,
581+
waitTimeout: timeouts.WorkloadReplicas,
582+
respHeaderTimeout: timeouts.ResponseHeader,
583+
enableColdStartHeader: true,
575584
},
576585
&TestTLSConfig,
577586
&config.Tracing{},
@@ -641,8 +650,9 @@ func TestWaitHeaderTimeout(t *testing.T) {
641650
dialCtxFunc,
642651
waitFunc,
643652
forwardingConfig{
644-
waitTimeout: timeouts.WorkloadReplicas,
645-
respHeaderTimeout: timeouts.ResponseHeader,
653+
waitTimeout: timeouts.WorkloadReplicas,
654+
respHeaderTimeout: timeouts.ResponseHeader,
655+
enableColdStartHeader: true,
646656
},
647657
&tls.Config{},
648658
&config.Tracing{},
@@ -700,8 +710,9 @@ func TestWaitHeaderTimeoutTLS(t *testing.T) {
700710
dialCtxFunc,
701711
waitFunc,
702712
forwardingConfig{
703-
waitTimeout: timeouts.WorkloadReplicas,
704-
respHeaderTimeout: timeouts.ResponseHeader,
713+
waitTimeout: timeouts.WorkloadReplicas,
714+
respHeaderTimeout: timeouts.ResponseHeader,
715+
enableColdStartHeader: true,
705716
},
706717
&TestTLSConfig,
707718
&config.Tracing{},

0 commit comments

Comments
 (0)