diff --git a/api/v1alpha1/backendtrafficpolicy_types.go b/api/v1alpha1/backendtrafficpolicy_types.go
index 9468331583..50310d14c5 100644
--- a/api/v1alpha1/backendtrafficpolicy_types.go
+++ b/api/v1alpha1/backendtrafficpolicy_types.go
@@ -138,6 +138,32 @@ type BackendTrafficPolicySpec struct {
//
// +optional
RequestBuffer *RequestBuffer `json:"requestBuffer,omitempty"`
+
+ // RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ // This configures Envoy's request body buffer limit without enabling full request buffering.
+ //
+ // This Request body buffer limit is independent of the connection buffer limits configured by
+ // ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ // and upstream connection read/write buffering and back pressure, while this field sets the
+ // maximum size of an individual request body that Envoy may buffer for HTTP processing.
+ //
+ // For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ // before or while the request body limit is enforced. Configure connection buffer limits large
+ // enough for the request body buffering you expect to allow on those connections.
+ //
+ // For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ // can be in flight before back pressure applies. They affect upload throughput and buffering
+ // behavior, but they are not a replacement for this request body size limit.
+ //
+ // If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+ //
+ // Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ //
+ // +kubebuilder:validation:XIntOrString
+ // +kubebuilder:validation:Pattern="^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$"
+ // +optional
+ RequestBodyBufferLimit *resource.Quantity `json:"requestBodyBufferLimit,omitempty"`
+
// Telemetry configures the telemetry settings for the policy target (Gateway or xRoute).
// This will override the telemetry settings in the EnvoyProxy resource.
//
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 7245a49145..69e52b84b4 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -886,6 +886,11 @@ func (in *BackendTrafficPolicySpec) DeepCopyInto(out *BackendTrafficPolicySpec)
*out = new(RequestBuffer)
(*in).DeepCopyInto(*out)
}
+ if in.RequestBodyBufferLimit != nil {
+ in, out := &in.RequestBodyBufferLimit, &out.RequestBodyBufferLimit
+ x := (*in).DeepCopy()
+ *out = &x
+ }
if in.Telemetry != nil {
in, out := &in.Telemetry, &out.Telemetry
*out = new(BackendTelemetry)
diff --git a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 575c1136c5..8b5ad60752 100644
--- a/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-crds-helm/templates/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -2305,6 +2305,34 @@ spec:
- Local
type: string
type: object
+ requestBodyBufferLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ This configures Envoy's request body buffer limit without enabling full request buffering.
+
+ This Request body buffer limit is independent of the connection buffer limits configured by
+ ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ and upstream connection read/write buffering and back pressure, while this field sets the
+ maximum size of an individual request body that Envoy may buffer for HTTP processing.
+
+ For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ before or while the request body limit is enforced. Configure connection buffer limits large
+ enough for the request body buffering you expect to allow on those connections.
+
+ For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ can be in flight before back pressure applies. They affect upload throughput and buffering
+ behavior, but they are not a replacement for this request body size limit.
+
+ If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+
+ Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ x-kubernetes-int-or-string: true
requestBuffer:
description: |-
RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request
diff --git a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
index 7572080368..c2c58849db 100644
--- a/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
+++ b/charts/gateway-helm/charts/crds/crds/generated/gateway.envoyproxy.io_backendtrafficpolicies.yaml
@@ -2304,6 +2304,34 @@ spec:
- Local
type: string
type: object
+ requestBodyBufferLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ This configures Envoy's request body buffer limit without enabling full request buffering.
+
+ This Request body buffer limit is independent of the connection buffer limits configured by
+ ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ and upstream connection read/write buffering and back pressure, while this field sets the
+ maximum size of an individual request body that Envoy may buffer for HTTP processing.
+
+ For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ before or while the request body limit is enforced. Configure connection buffer limits large
+ enough for the request body buffering you expect to allow on those connections.
+
+ For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ can be in flight before back pressure applies. They affect upload throughput and buffering
+ behavior, but they are not a replacement for this request body size limit.
+
+ If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+
+ Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ x-kubernetes-int-or-string: true
requestBuffer:
description: |-
RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 933ba491fb..2fb44fc596 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -15,6 +15,7 @@ import (
perr "github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
+ apiresource "k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
@@ -1070,6 +1071,7 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, o
h2 *ir.HTTP2Settings
ro *ir.ResponseOverride
rb *ir.RequestBuffer
+ rbbl *uint64
cp []*ir.Compression
httpUpgrade []ir.HTTPUpgradeConfig
err, errs error
@@ -1138,6 +1140,11 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, o
errs = errors.Join(errs, err)
}
+ if rbbl, err = buildRequestBodyBufferLimit(policy.Spec.RequestBodyBufferLimit); err != nil {
+ err = perr.WithMessage(err, "RequestBodyBufferLimit")
+ errs = errors.Join(errs, err)
+ }
+
if err = validateTelemetry(policy.Spec.Telemetry); err != nil {
err = perr.WithMessage(err, "Telemetry")
errs = errors.Join(errs, err)
@@ -1154,25 +1161,26 @@ func (t *Translator) buildTrafficFeatures(policy *egv1a1.BackendTrafficPolicy, o
ds = translateDNS(&policy.Spec.ClusterSettings, utils.NamespacedName(policy).String())
return &ir.TrafficFeatures{
- RateLimit: rl,
- BandwidthLimit: bl,
- LoadBalancer: lb,
- ProxyProtocol: pp,
- HealthCheck: hc,
- CircuitBreaker: cb,
- FaultInjection: fi,
- AdmissionControl: ac,
- TCPKeepalive: ka,
- Retry: rt,
- BackendConnection: bc,
- HTTP2: h2,
- DNS: ds,
- Timeout: to,
- ResponseOverride: ro,
- RequestBuffer: rb,
- Compression: cp,
- HTTPUpgrade: httpUpgrade,
- Telemetry: buildBackendTelemetry(policy.Spec.Telemetry),
+ RateLimit: rl,
+ BandwidthLimit: bl,
+ LoadBalancer: lb,
+ ProxyProtocol: pp,
+ HealthCheck: hc,
+ CircuitBreaker: cb,
+ FaultInjection: fi,
+ AdmissionControl: ac,
+ TCPKeepalive: ka,
+ Retry: rt,
+ BackendConnection: bc,
+ HTTP2: h2,
+ DNS: ds,
+ Timeout: to,
+ ResponseOverride: ro,
+ RequestBuffer: rb,
+ RequestBodyBufferLimit: rbbl,
+ Compression: cp,
+ HTTPUpgrade: httpUpgrade,
+ Telemetry: buildBackendTelemetry(policy.Spec.Telemetry),
}, errs
}
@@ -1906,6 +1914,25 @@ func buildRequestBuffer(spec *egv1a1.RequestBuffer) (*ir.RequestBuffer, error) {
}, nil
}
+// buildRequestBodyBufferLimit converts the request body buffer limit Quantity into a byte count.
+func buildRequestBodyBufferLimit(limit *apiresource.Quantity) (*uint64, error) {
+ if limit == nil {
+ return nil, nil
+ }
+
+ v, ok := limit.AsInt64()
+ if !ok {
+ return nil, fmt.Errorf("invalid RequestBodyBufferLimit value %s", limit.String())
+ }
+
+ if v < 0 {
+ return nil, fmt.Errorf("RequestBodyBufferLimit value %s is out of range, must be >= 0", limit.String())
+ }
+
+ out := uint64(v)
+ return &out, nil
+}
+
func (t *Translator) buildResponseOverride(policy *egv1a1.BackendTrafficPolicy, owners *backendTrafficPolicyOwners) (*ir.ResponseOverride, error) {
if len(policy.Spec.ResponseOverride) == 0 {
return nil, nil
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.in.yaml
new file mode 100644
index 0000000000..d4f48f39a3
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.in.yaml
@@ -0,0 +1,91 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: All
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-2
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 8080
+ allowedRoutes:
+ namespaces:
+ from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-1
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/foo"
+ backendRefs:
+ - name: service-1
+ port: 8080
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-2
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-2
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/foo"
+ backendRefs:
+ - name: service-2
+ port: 8080
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-for-route
+ namespace: default
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-2
+ requestBodyBufferLimit: 4Mi
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ requestBodyBufferLimit: 4Mi
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.out.yaml
new file mode 100644
index 0000000000..1dae83fc43
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-request-body-buffer-limit.out.yaml
@@ -0,0 +1,418 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-for-route
+ namespace: default
+ spec:
+ requestBodyBufferLimit: 4Mi
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: httproute-2
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: BackendTrafficPolicy
+ metadata:
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ spec:
+ requestBodyBufferLimit: 4Mi
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: spec.targetRef is deprecated, use spec.targetRefs instead
+ reason: DeprecatedField
+ status: "True"
+ type: Warning
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ name: gateway-2
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 8080
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-1
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ matches:
+ - path:
+ value: /foo
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ name: httproute-2
+ namespace: default
+ spec:
+ hostnames:
+ - gateway.envoyproxy.io
+ parentRefs:
+ - name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-2
+ port: 8080
+ matches:
+ - path:
+ value: /foo
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-1
+ namespace: envoy-gateway-system
+ envoy-gateway/gateway-2:
+ proxy:
+ listeners:
+ - name: envoy-gateway/gateway-2/http
+ ports:
+ - containerPort: 8080
+ name: http-8080
+ protocol: HTTP
+ servicePort: 8080
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-2
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ ownerReference:
+ kind: GatewayClass
+ name: envoy-gateway-class
+ name: envoy-gateway/gateway-2
+ namespace: envoy-gateway-system
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-1-196ae069
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-1
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 80
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ metadata:
+ kind: Service
+ name: service-1
+ namespace: default
+ sectionName: "8080"
+ name: httproute/default/httproute-1/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /foo
+ traffic:
+ requestBodyBufferLimit: 4194304
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
+ envoy-gateway/gateway-2:
+ accessLog:
+ json:
+ - path: /dev/stdout
+ globalResources:
+ proxyServiceCluster:
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-2-4a0e4eb9
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-2
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.6.5.4
+ port: 8080
+ zone: zone1
+ metadata:
+ kind: Service
+ name: envoy-envoy-gateway-gateway-2-4a0e4eb9
+ namespace: envoy-gateway-system
+ sectionName: "8080"
+ name: envoy-gateway/gateway-2
+ protocol: TCP
+ http:
+ - address: 0.0.0.0
+ externalPort: 8080
+ hostnames:
+ - '*'
+ metadata:
+ kind: Gateway
+ name: gateway-2
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-2/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 8080
+ routes:
+ - destination:
+ metadata:
+ kind: HTTPRoute
+ name: httproute-2
+ namespace: default
+ name: httproute/default/httproute-2/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ metadata:
+ kind: Service
+ name: service-2
+ namespace: default
+ sectionName: "8080"
+ name: httproute/default/httproute-2/rule/0/backend/0
+ protocol: HTTP
+ weight: 1
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-2
+ namespace: default
+ policies:
+ - kind: BackendTrafficPolicy
+ name: policy-for-route
+ namespace: default
+ name: httproute/default/httproute-2/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /foo
+ traffic:
+ requestBodyBufferLimit: 4194304
+ readyListener:
+ address: 0.0.0.0
+ ipFamily: IPv4
+ path: /ready
+ port: 19003
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index c8ec2b573d..34ceb857dd 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -1083,6 +1083,8 @@ type TrafficFeatures struct {
Telemetry *BackendTelemetry `json:"telemetry,omitempty" yaml:"telemetry,omitempty"`
// RequestBuffer defines the schema for enabling buffered requests
RequestBuffer *RequestBuffer `json:"requestBuffer,omitempty" yaml:"requestBuffer,omitempty"`
+ // RequestBodyBufferLimit is the maximum number of bytes Envoy may buffer for an individual request body.
+ RequestBodyBufferLimit *uint64 `json:"requestBodyBufferLimit,omitempty" yaml:"requestBodyBufferLimit,omitempty"`
}
// BackendTelemetry defines the telemetry configuration for the backend.
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index fd9e5ce9fe..e7936edb35 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -5222,6 +5222,11 @@ func (in *TrafficFeatures) DeepCopyInto(out *TrafficFeatures) {
*out = new(RequestBuffer)
(*in).DeepCopyInto(*out)
}
+ if in.RequestBodyBufferLimit != nil {
+ in, out := &in.RequestBodyBufferLimit, &out.RequestBodyBufferLimit
+ *out = new(uint64)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TrafficFeatures.
diff --git a/internal/xds/translator/route.go b/internal/xds/translator/route.go
index 21eb69951c..64e54b1877 100644
--- a/internal/xds/translator/route.go
+++ b/internal/xds/translator/route.go
@@ -145,6 +145,11 @@ func buildXdsRoute(httpRoute *ir.HTTPRoute, httpListener *ir.HTTPListener) (*rou
// Metrics
router.StatPrefix = ptr.Deref(httpRoute.StatName, "")
+ // Request body buffer limit
+ if httpRoute.Traffic != nil && httpRoute.Traffic.RequestBodyBufferLimit != nil {
+ router.RequestBodyBufferLimit = wrapperspb.UInt64(*httpRoute.Traffic.RequestBodyBufferLimit)
+ }
+
// Add per route filter configs to the route, if needed.
if err := patchRouteWithPerRouteConfig(router, httpRoute, httpListener); err != nil {
return nil, err
diff --git a/internal/xds/translator/testdata/in/xds-ir/request-body-buffer-limit.yaml b/internal/xds/translator/testdata/in/xds-ir/request-body-buffer-limit.yaml
new file mode 100644
index 0000000000..410fda13f3
--- /dev/null
+++ b/internal/xds/translator/testdata/in/xds-ir/request-body-buffer-limit.yaml
@@ -0,0 +1,38 @@
+http:
+ - address: 0.0.0.0
+ hostnames:
+ - "*"
+ metadata:
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ name: httproute/default/httproute-1/rule/0/backend/0
+ hostname: gateway.envoyproxy.io
+ isHTTP2: false
+ metadata:
+ kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /
+ traffic:
+ requestBodyBufferLimit: 10485760
diff --git a/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.clusters.yaml b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.clusters.yaml
new file mode 100644
index 0000000000..54d3dffabe
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.clusters.yaml
@@ -0,0 +1,23 @@
+- circuitBreakers:
+ thresholds:
+ - maxRetries: 1024
+ commonLbConfig: {}
+ connectTimeout: 10s
+ dnsLookupFamily: V4_PREFERRED
+ edsClusterConfig:
+ edsConfig:
+ ads: {}
+ resourceApiVersion: V3
+ serviceName: httproute/default/httproute-1/rule/0
+ ignoreHealthOnHostRemoval: true
+ loadBalancingPolicy:
+ policies:
+ - typedExtensionConfig:
+ name: envoy.load_balancing_policies.least_request
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.load_balancing_policies.least_request.v3.LeastRequest
+ localityLbConfig:
+ localityWeightedLbConfig: {}
+ name: httproute/default/httproute-1/rule/0
+ perConnectionBufferLimitBytes: 32768
+ type: EDS
diff --git a/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.endpoints.yaml b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.endpoints.yaml
new file mode 100644
index 0000000000..29bb6b4e44
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.endpoints.yaml
@@ -0,0 +1,12 @@
+- clusterName: httproute/default/httproute-1/rule/0
+ endpoints:
+ - lbEndpoints:
+ - endpoint:
+ address:
+ socketAddress:
+ address: 7.7.7.7
+ portValue: 8080
+ loadBalancingWeight: 1
+ loadBalancingWeight: 1
+ locality:
+ region: httproute/default/httproute-1/rule/0/backend/0
diff --git a/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.listeners.yaml
new file mode 100644
index 0000000000..4d0fe90c54
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.listeners.yaml
@@ -0,0 +1,35 @@
+- address:
+ socketAddress:
+ address: 0.0.0.0
+ portValue: 10080
+ defaultFilterChain:
+ filters:
+ - name: envoy.filters.network.http_connection_manager
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
+ commonHttpProtocolOptions:
+ headersWithUnderscoresAction: REJECT_REQUEST
+ http2ProtocolOptions:
+ initialConnectionWindowSize: 1048576
+ initialStreamWindowSize: 65536
+ maxConcurrentStreams: 100
+ httpFilters:
+ - name: envoy.filters.http.router
+ typedConfig:
+ '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
+ suppressEnvoyHeaders: true
+ mergeSlashes: true
+ normalizePath: true
+ pathWithEscapedSlashesAction: UNESCAPE_AND_REDIRECT
+ rds:
+ configSource:
+ ads: {}
+ resourceApiVersion: V3
+ routeConfigName: envoy-gateway/gateway-1/http
+ serverHeaderTransformation: PASS_THROUGH
+ statPrefix: http-10080
+ useRemoteAddress: true
+ name: envoy-gateway/gateway-1/http
+ maxConnectionsToAcceptPerSocketEvent: 1
+ name: envoy-gateway/gateway-1/http
+ perConnectionBufferLimitBytes: 32768
diff --git a/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.routes.yaml b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.routes.yaml
new file mode 100644
index 0000000000..8da53d06b7
--- /dev/null
+++ b/internal/xds/translator/testdata/out/xds-ir/request-body-buffer-limit.routes.yaml
@@ -0,0 +1,30 @@
+- ignorePortInHostMatching: true
+ name: envoy-gateway/gateway-1/http
+ virtualHosts:
+ - domains:
+ - gateway.envoyproxy.io
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ name: envoy-gateway/gateway-1/http/gateway_envoyproxy_io
+ routes:
+ - match:
+ prefix: /
+ metadata:
+ filterMetadata:
+ envoy-gateway:
+ resources:
+ - kind: HTTPRoute
+ name: httproute-1
+ namespace: default
+ name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+ requestBodyBufferLimit: "10485760"
+ route:
+ cluster: httproute/default/httproute-1/rule/0
+ upgradeConfigs:
+ - upgradeType: websocket
diff --git a/release-notes/current/new_features/8893-requestbodybufferlimit-backendtrafficpolicy-max-request-body-buffer.md b/release-notes/current/new_features/8893-requestbodybufferlimit-backendtrafficpolicy-max-request-body-buffer.md
new file mode 100644
index 0000000000..2660417025
--- /dev/null
+++ b/release-notes/current/new_features/8893-requestbodybufferlimit-backendtrafficpolicy-max-request-body-buffer.md
@@ -0,0 +1 @@
+Added a `requestBodyBufferLimit` field to `BackendTrafficPolicy`, setting the maximum request body size Envoy will buffer for a route. Requests whose buffered body exceeds the limit are rejected with HTTP 413. Unlike `requestBuffer`, this only raises the buffer limit without enabling full request buffering.
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index 66d6c6cd5a..f5cd8d2c89 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -594,6 +594,7 @@ _Appears in:_
| `responseOverride` | _[ResponseOverride](#responseoverride) array_ | false | | ResponseOverride defines the configuration to override specific responses with a custom one.
If multiple configurations are specified, the first one to match wins. |
| `httpUpgrade` | _[ProtocolUpgradeConfig](#protocolupgradeconfig) array_ | false | | HTTPUpgrade defines the configuration for HTTP protocol upgrades.
If not specified, the default upgrade configuration (websocket) will be used.
However, if requestBuffer is configured, the default upgrade configuration
will be ignored. |
| `requestBuffer` | _[RequestBuffer](#requestbuffer)_ | false | | RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request
upstream to the backends. This can be helpful to shield your backend servers from slow clients, and also to enforce a maximum size per request
as any requests larger than the buffer size will be rejected.
This can have a negative performance impact so should only be enabled when necessary.
When enabling this option, you should also configure your connection buffer size to account for these request buffers. There will also be an
increase in memory usage for Envoy that should be accounted for in your deployment settings.
Request buffering is incompatible with streaming APIs and protocol upgrades such as gRPC streaming and WebSocket. Do not enable this option
on routes that need those protocols, because requests can hang instead of being forwarded upstream. |
+| `requestBodyBufferLimit` | _[Quantity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#quantity-resource-api)_ | false | | RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
This configures Envoy's request body buffer limit without enabling full request buffering.
This Request body buffer limit is independent of the connection buffer limits configured by
ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
and upstream connection read/write buffering and back pressure, while this field sets the
maximum size of an individual request body that Envoy may buffer for HTTP processing.
For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
before or while the request body limit is enforced. Configure connection buffer limits large
enough for the request body buffering you expect to allow on those connections.
For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
can be in flight before back pressure applies. They affect upload throughput and buffering
behavior, but they are not a replacement for this request body size limit.
If exceeded, the request will be rejected with HTTP 413 Content Too Large.
Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki"). |
| `telemetry` | _[BackendTelemetry](#backendtelemetry)_ | false | | Telemetry configures the telemetry settings for the policy target (Gateway or xRoute).
This will override the telemetry settings in the EnvoyProxy resource. |
| `routingType` | _[RoutingType](#routingtype)_ | false | | RoutingType can be set to "Service" to use the Service Cluster IP for routing to the backend,
or it can be set to "Endpoint" to use Endpoint routing.
When specified, this overrides the EnvoyProxy-level setting for the relevant targetRefs.
If not specified, the EnvoyProxy-level setting is used. |
diff --git a/test/cel-validation/backendtrafficpolicy_test.go b/test/cel-validation/backendtrafficpolicy_test.go
index 6bd00eb1c0..4bc5f7f2f7 100644
--- a/test/cel-validation/backendtrafficpolicy_test.go
+++ b/test/cel-validation/backendtrafficpolicy_test.go
@@ -3060,6 +3060,53 @@ func TestBackendTrafficPolicyTarget(t *testing.T) {
},
wantErrors: []string{},
},
+ {
+ desc: "request body buffer limit with http upgrade",
+ mutate: func(btp *egv1a1.BackendTrafficPolicy) {
+ btp.Spec = egv1a1.BackendTrafficPolicySpec{
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ {
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: "gateway.networking.k8s.io",
+ Kind: "Gateway",
+ Name: "eg",
+ },
+ },
+ },
+ },
+ RequestBodyBufferLimit: new(resource.MustParse("1Mi")),
+ HTTPUpgrade: []*egv1a1.ProtocolUpgradeConfig{
+ {
+ Type: "websocket",
+ },
+ },
+ }
+ },
+ wantErrors: []string{},
+ },
+ {
+ desc: "invalid request body buffer limit format",
+ mutate: func(btp *egv1a1.BackendTrafficPolicy) {
+ btp.Spec = egv1a1.BackendTrafficPolicySpec{
+ PolicyTargetReferences: egv1a1.PolicyTargetReferences{
+ TargetRefs: []gwapiv1.LocalPolicyTargetReferenceWithSectionName{
+ {
+ LocalPolicyTargetReference: gwapiv1.LocalPolicyTargetReference{
+ Group: "gateway.networking.k8s.io",
+ Kind: "Gateway",
+ Name: "eg",
+ },
+ },
+ },
+ },
+ RequestBodyBufferLimit: new(resource.MustParse("1m")),
+ }
+ },
+ wantErrors: []string{
+ "spec.requestBodyBufferLimit: Invalid value: \"1m\": spec.requestBodyBufferLimit in body should match '^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$', : Invalid value: \"\"",
+ },
+ },
{
desc: "request buffer with websocket upgrade",
mutate: func(btp *egv1a1.BackendTrafficPolicy) {
diff --git a/test/e2e/testdata/request-body-buffer-limit.yaml b/test/e2e/testdata/request-body-buffer-limit.yaml
new file mode 100644
index 0000000000..033cf53609
--- /dev/null
+++ b/test/e2e/testdata/request-body-buffer-limit.yaml
@@ -0,0 +1,82 @@
+# This manifest exercises the BackendTrafficPolicy requestBodyBufferLimit field.
+#
+# Both routes are forced to buffer the request body via an inline Lua filter that
+# calls request_handle:body(). Buffering is what makes Envoy enforce the request
+# body buffer limit (a 413 is only returned when a filter buffers the body and the
+# buffered size exceeds the stream decoder buffer limit; pure streaming proxies any
+# size). The Lua filter is used purely as the buffering trigger and, unlike the
+# Buffer filter, does not impose its own max_request_bytes.
+#
+# - request-body-buffer-limit-default has no requestBodyBufferLimit, so the limit
+# defaults to the listener per_connection_buffer_limit_bytes (32768 bytes). A body
+# larger than that is rejected with HTTP 413.
+# - request-body-buffer-limit-high sets requestBodyBufferLimit to 1Mi, which raises
+# the stream decoder buffer limit so the same body is buffered and forwarded (200).
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: request-body-buffer-limit-default
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /request-body-buffer-limit-default
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: HTTPRoute
+metadata:
+ name: request-body-buffer-limit-high
+ namespace: gateway-conformance-infra
+spec:
+ parentRefs:
+ - name: same-namespace
+ rules:
+ - matches:
+ - path:
+ type: PathPrefix
+ value: /request-body-buffer-limit-high
+ backendRefs:
+ - name: infra-backend-v1
+ port: 8080
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: EnvoyExtensionPolicy
+metadata:
+ name: request-body-buffer-limit-buffer-body
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: request-body-buffer-limit-default
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: request-body-buffer-limit-high
+ lua:
+ - type: Inline
+ inline: |
+ function envoy_on_request(request_handle)
+ -- Force Envoy to buffer the full request body so that the configured
+ -- request body buffer limit is enforced.
+ request_handle:body()
+ end
+---
+apiVersion: gateway.envoyproxy.io/v1alpha1
+kind: BackendTrafficPolicy
+metadata:
+ name: request-body-buffer-limit-high
+ namespace: gateway-conformance-infra
+spec:
+ targetRefs:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ name: request-body-buffer-limit-high
+ requestBodyBufferLimit: 1Mi
diff --git a/test/e2e/tests/request_body_buffer_limit.go b/test/e2e/tests/request_body_buffer_limit.go
new file mode 100644
index 0000000000..2ed8da8660
--- /dev/null
+++ b/test/e2e/tests/request_body_buffer_limit.go
@@ -0,0 +1,100 @@
+// Copyright Envoy Gateway Authors
+// SPDX-License-Identifier: Apache-2.0
+// The full text of the Apache license is available in the LICENSE file at
+// the root of the repo.
+
+//go:build e2e
+
+package tests
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+ "testing"
+ "time"
+
+ "k8s.io/apimachinery/pkg/types"
+ gwapiv1 "sigs.k8s.io/gateway-api/apis/v1"
+ httputils "sigs.k8s.io/gateway-api/conformance/utils/http"
+ "sigs.k8s.io/gateway-api/conformance/utils/kubernetes"
+ "sigs.k8s.io/gateway-api/conformance/utils/suite"
+ "sigs.k8s.io/gateway-api/conformance/utils/tlog"
+
+ "github.com/envoyproxy/gateway/internal/gatewayapi"
+ "github.com/envoyproxy/gateway/internal/gatewayapi/resource"
+)
+
+func init() {
+ ConformanceTests = append(ConformanceTests, RequestBodyBufferLimitTest)
+}
+
+var RequestBodyBufferLimitTest = suite.ConformanceTest{
+ ShortName: "RequestBodyBufferLimit",
+ Description: "Verify that BackendTrafficPolicy requestBodyBufferLimit bounds the request body size Envoy will buffer",
+ Manifests: []string{"testdata/request-body-buffer-limit.yaml"},
+ Test: func(t *testing.T, suite *suite.ConformanceTestSuite) {
+ ns := "gateway-conformance-infra"
+ defaultRouteNN := types.NamespacedName{Name: "request-body-buffer-limit-default", Namespace: ns}
+ highRouteNN := types.NamespacedName{Name: "request-body-buffer-limit-high", Namespace: ns}
+ gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns}
+
+ gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(
+ t, suite.Client, suite.TimeoutConfig, suite.ControllerName,
+ kubernetes.NewGatewayRef(gwNN), defaultRouteNN, highRouteNN,
+ )
+
+ ancestorRef := gwapiv1.ParentReference{
+ Group: gatewayapi.GroupPtr(gwapiv1.GroupName),
+ Kind: gatewayapi.KindPtr(resource.KindGateway),
+ Namespace: gatewayapi.NamespacePtr(gwNN.Namespace),
+ Name: gwapiv1.ObjectName(gwNN.Name),
+ }
+ // The inline Lua filter forces the request body to be buffered on both routes.
+ EnvoyExtensionPolicyMustBeAccepted(t, suite.Client,
+ types.NamespacedName{Name: "request-body-buffer-limit-buffer-body", Namespace: ns},
+ suite.ControllerName, ancestorRef)
+ // The requestBodyBufferLimit policy raises the buffer limit on the "high" route.
+ BackendTrafficPolicyMustBeAccepted(t, suite.Client,
+ types.NamespacedName{Name: "request-body-buffer-limit-high", Namespace: ns},
+ suite.ControllerName, ancestorRef)
+
+ // 100 KiB body: larger than the default per-connection buffer limit (32768 bytes)
+ // but smaller than the 1Mi requestBodyBufferLimit configured on the "high" route.
+ body := strings.Repeat("x", 100*1024)
+
+ t.Run("request exceeding the default buffer limit is rejected with 413", func(t *testing.T) {
+ expectRequestBodyStatus(t, suite, gwAddr, "/request-body-buffer-limit-default", body, http.StatusRequestEntityTooLarge)
+ })
+
+ t.Run("request within the configured buffer limit succeeds", func(t *testing.T) {
+ expectRequestBodyStatus(t, suite, gwAddr, "/request-body-buffer-limit-high", body, http.StatusOK)
+ })
+ },
+}
+
+// expectRequestBodyStatus POSTs the given body to gwAddr+path and waits until the
+// response status code converges to expectedStatusCode.
+func expectRequestBodyStatus(t *testing.T, suite *suite.ConformanceTestSuite, gwAddr, path, body string, expectedStatusCode int) {
+ t.Helper()
+
+ url := fmt.Sprintf("http://%s%s", gwAddr, path)
+ httpClient := &http.Client{Timeout: 30 * time.Second}
+
+ httputils.AwaitConvergence(t, suite.TimeoutConfig.RequiredConsecutiveSuccesses, suite.TimeoutConfig.MaxTimeToConsistency,
+ func(elapsed time.Duration) bool {
+ resp, err := httpClient.Post(url, "application/octet-stream", strings.NewReader(body))
+ if err != nil {
+ tlog.Logf(t, "POST %s error after %v: %v", url, elapsed, err)
+ return false
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode == expectedStatusCode {
+ return true
+ }
+
+ tlog.Logf(t, "POST %s got status %d, want %d after %v", url, resp.StatusCode, expectedStatusCode, elapsed)
+ return false
+ })
+}
diff --git a/test/helm/gateway-crds-helm/all.out.yaml b/test/helm/gateway-crds-helm/all.out.yaml
index 31f9836eeb..a8cae400c8 100644
--- a/test/helm/gateway-crds-helm/all.out.yaml
+++ b/test/helm/gateway-crds-helm/all.out.yaml
@@ -26922,6 +26922,34 @@ spec:
- Local
type: string
type: object
+ requestBodyBufferLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ This configures Envoy's request body buffer limit without enabling full request buffering.
+
+ This Request body buffer limit is independent of the connection buffer limits configured by
+ ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ and upstream connection read/write buffering and back pressure, while this field sets the
+ maximum size of an individual request body that Envoy may buffer for HTTP processing.
+
+ For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ before or while the request body limit is enforced. Configure connection buffer limits large
+ enough for the request body buffering you expect to allow on those connections.
+
+ For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ can be in flight before back pressure applies. They affect upload throughput and buffering
+ behavior, but they are not a replacement for this request body size limit.
+
+ If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+
+ Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ x-kubernetes-int-or-string: true
requestBuffer:
description: |-
RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request
diff --git a/test/helm/gateway-crds-helm/e2e.out.yaml b/test/helm/gateway-crds-helm/e2e.out.yaml
index 02b6065f79..2a76f48114 100644
--- a/test/helm/gateway-crds-helm/e2e.out.yaml
+++ b/test/helm/gateway-crds-helm/e2e.out.yaml
@@ -2860,6 +2860,34 @@ spec:
- Local
type: string
type: object
+ requestBodyBufferLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ This configures Envoy's request body buffer limit without enabling full request buffering.
+
+ This Request body buffer limit is independent of the connection buffer limits configured by
+ ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ and upstream connection read/write buffering and back pressure, while this field sets the
+ maximum size of an individual request body that Envoy may buffer for HTTP processing.
+
+ For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ before or while the request body limit is enforced. Configure connection buffer limits large
+ enough for the request body buffering you expect to allow on those connections.
+
+ For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ can be in flight before back pressure applies. They affect upload throughput and buffering
+ behavior, but they are not a replacement for this request body size limit.
+
+ If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+
+ Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ x-kubernetes-int-or-string: true
requestBuffer:
description: |-
RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request
diff --git a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
index 0b1d7764f0..625afb833d 100644
--- a/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
+++ b/test/helm/gateway-crds-helm/envoy-gateway-crds.out.yaml
@@ -2860,6 +2860,34 @@ spec:
- Local
type: string
type: object
+ requestBodyBufferLimit:
+ allOf:
+ - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
+ - pattern: ^[1-9]+[0-9]*([EPTGMK]i|[EPTGMk])?$
+ anyOf:
+ - type: integer
+ - type: string
+ description: |-
+ RequestBodyBufferLimit specifies the maximum size in bytes that Envoy may buffer for request bodies.
+ This configures Envoy's request body buffer limit without enabling full request buffering.
+
+ This Request body buffer limit is independent of the connection buffer limits configured by
+ ClientTrafficPolicy and BackendTrafficPolicy. The connection buffer limits control downstream
+ and upstream connection read/write buffering and back pressure, while this field sets the
+ maximum size of an individual request body that Envoy may buffer for HTTP processing.
+
+ For HTTP/1, connection buffer limits can still affect how request body data is read and buffered
+ before or while the request body limit is enforced. Configure connection buffer limits large
+ enough for the request body buffering you expect to allow on those connections.
+
+ For HTTP/2, initial stream and connection window sizes control HTTP/2 flow control: how much data
+ can be in flight before back pressure applies. They affect upload throughput and buffering
+ behavior, but they are not a replacement for this request body size limit.
+
+ If exceeded, the request will be rejected with HTTP 413 Content Too Large.
+
+ Accepts values in resource.Quantity format (e.g., "10Mi", "500Ki").
+ x-kubernetes-int-or-string: true
requestBuffer:
description: |-
RequestBuffer allows the gateway to buffer and fully receive each request from a client before continuing to send the request