From 798e5087695a6468fd44c2100200e9ec47366007 Mon Sep 17 00:00:00 2001 From: Sanjay Tripathi Date: Thu, 23 Jul 2026 16:01:15 +0530 Subject: [PATCH] RFE-2924: Return HTTP 429 when route HTTP rate limit is exceeded When haproxy.router.openshift.io/rate-limit-connections.rate-http is hit, use http-request deny deny_status 429 instead of silently rejecting the TCP connection so clients get a proper HTTP status. TCP concurrent and connection-rate limits remain tcp-request reject. --- .../haproxy/conf/haproxy-config.template | 4 +- pkg/router/router_test.go | 41 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/images/router/haproxy/conf/haproxy-config.template b/images/router/haproxy/conf/haproxy-config.template index 33fc45901..a1a7247ab 100644 --- a/images/router/haproxy/conf/haproxy-config.template +++ b/images/router/haproxy/conf/haproxy-config.template @@ -708,7 +708,9 @@ backend {{ genBackendNamePrefix $cfg.TLSTermination }}:{{ $cfgIdx }} {{- end }} {{- if (isInteger (index $cfg.Annotations "haproxy.router.openshift.io/rate-limit-connections.rate-http")) }} - tcp-request content reject if { src_http_req_rate ge {{ index $cfg.Annotations "haproxy.router.openshift.io/rate-limit-connections.rate-http" }} } + # Return HTTP 429 when the per-source HTTP request rate limit is exceeded (RFE-2924). + # TCP connection limits above still use tcp-request reject because no HTTP response is possible there. + http-request deny deny_status 429 if { src_http_req_rate ge {{ index $cfg.Annotations "haproxy.router.openshift.io/rate-limit-connections.rate-http" }} } {{- else }} #HTTP request rate not restricted {{- end }} diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 12a686476..97546ed78 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -976,6 +976,47 @@ func TestConfigTemplate(t *testing.T) { }, }, }, + "HTTP rate limit returns 429": { + mustCreateWithConfig{ + mustCreateRoute: mustCreateRoute{ + name: "rate-limit-http", + host: "rate-limit-http.example.com", + path: "", + time: start, + annotations: map[string]string{ + "haproxy.router.openshift.io/rate-limit-connections": "true", + "haproxy.router.openshift.io/rate-limit-connections.rate-http": "40", + }, + tlsTermination: routev1.TLSTerminationEdge, + }, + mustMatchConfig: mustMatchConfig{ + section: "backend", + sectionName: edgeBackendName(h.namespace, "rate-limit-http"), + attribute: "http-request", + value: `deny deny_status 429 if { src_http_req_rate ge 40 }`, + }, + }, + }, + "HTTP rate limit 429 on insecure backend": { + mustCreateWithConfig{ + mustCreateRoute: mustCreateRoute{ + name: "rate-limit-http-insecure", + host: "rate-limit-http-insecure.example.com", + path: "", + time: start, + annotations: map[string]string{ + "haproxy.router.openshift.io/rate-limit-connections": "true", + "haproxy.router.openshift.io/rate-limit-connections.rate-http": "10", + }, + }, + mustMatchConfig: mustMatchConfig{ + section: "backend", + sectionName: insecureBackendName(h.namespace, "rate-limit-http-insecure"), + attribute: "http-request", + value: `deny deny_status 429 if { src_http_req_rate ge 10 }`, + }, + }, + }, } defer cleanUpRoutes(t)