diff --git a/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml b/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml index 544718e877..29b89844e8 100644 --- a/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml +++ b/config/crd/bases/k8s.nginx.org_virtualserverroutes.yaml @@ -878,6 +878,10 @@ spec: is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' type: string + client-body-buffer-size: + description: Sets the size of the buffer used for reading the + client request body. + type: string client-max-body-size: description: Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap diff --git a/config/crd/bases/k8s.nginx.org_virtualservers.yaml b/config/crd/bases/k8s.nginx.org_virtualservers.yaml index ac5bdd456a..798948336b 100644 --- a/config/crd/bases/k8s.nginx.org_virtualservers.yaml +++ b/config/crd/bases/k8s.nginx.org_virtualservers.yaml @@ -1067,6 +1067,10 @@ spec: is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' type: string + client-body-buffer-size: + description: Sets the size of the buffer used for reading the + client request body. + type: string client-max-body-size: description: Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap diff --git a/deploy/crds.yaml b/deploy/crds.yaml index fd080f2d59..d6aa0ce873 100644 --- a/deploy/crds.yaml +++ b/deploy/crds.yaml @@ -1916,6 +1916,10 @@ spec: is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' type: string + client-body-buffer-size: + description: Sets the size of the buffer used for reading the + client request body. + type: string client-max-body-size: description: Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap @@ -3335,6 +3339,10 @@ spec: is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' type: string + client-body-buffer-size: + description: Sets the size of the buffer used for reading the + client request body. + type: string client-max-body-size: description: Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap diff --git a/docs/crd/k8s.nginx.org_virtualserverroutes.md b/docs/crd/k8s.nginx.org_virtualserverroutes.md index 52426dacee..bd7c136f31 100644 --- a/docs/crd/k8s.nginx.org_virtualserverroutes.md +++ b/docs/crd/k8s.nginx.org_virtualserverroutes.md @@ -169,6 +169,7 @@ The `.spec` object supports the following fields: | `upstreams[].buffers.number` | `integer` | Configures the number of buffers. The default is set in the proxy-buffers ConfigMap key. | | `upstreams[].buffers.size` | `string` | Configures the size of a buffer. The default is set in the proxy-buffers ConfigMap key. | | `upstreams[].busy-buffers-size` | `string` | Sets the size of the buffers used for reading a response from the upstream server when the proxy_buffering is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' | +| `upstreams[].client-body-buffer-size` | `string` | Sets the size of the buffer used for reading the client request body. | | `upstreams[].client-max-body-size` | `string` | Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap key. | | `upstreams[].connect-timeout` | `string` | The timeout for establishing a connection with an upstream server. The default is specified in the proxy-connect-timeout ConfigMap key. | | `upstreams[].fail-timeout` | `string` | The time during which the specified number of unsuccessful attempts to communicate with an upstream server should happen to consider the server unavailable. The default is set in the fail-timeout ConfigMap key. | diff --git a/docs/crd/k8s.nginx.org_virtualservers.md b/docs/crd/k8s.nginx.org_virtualservers.md index 280e1bb8b2..4366b8824b 100644 --- a/docs/crd/k8s.nginx.org_virtualservers.md +++ b/docs/crd/k8s.nginx.org_virtualservers.md @@ -204,6 +204,7 @@ The `.spec` object supports the following fields: | `upstreams[].buffers.number` | `integer` | Configures the number of buffers. The default is set in the proxy-buffers ConfigMap key. | | `upstreams[].buffers.size` | `string` | Configures the size of a buffer. The default is set in the proxy-buffers ConfigMap key. | | `upstreams[].busy-buffers-size` | `string` | Sets the size of the buffers used for reading a response from the upstream server when the proxy_buffering is enabled. The default is set in the proxy-busy-buffers-size ConfigMap key.' | +| `upstreams[].client-body-buffer-size` | `string` | Sets the size of the buffer used for reading the client request body. | | `upstreams[].client-max-body-size` | `string` | Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap key. | | `upstreams[].connect-timeout` | `string` | The timeout for establishing a connection with an upstream server. The default is specified in the proxy-connect-timeout ConfigMap key. | | `upstreams[].fail-timeout` | `string` | The time during which the specified number of unsuccessful attempts to communicate with an upstream server should happen to consider the server unavailable. The default is set in the fail-timeout ConfigMap key. | diff --git a/internal/configs/config_params.go b/internal/configs/config_params.go index 1410fd45f6..454d18cc6d 100644 --- a/internal/configs/config_params.go +++ b/internal/configs/config_params.go @@ -12,6 +12,7 @@ import ( type ConfigParams struct { Context context.Context ClientMaxBodySize string + ClientBodyBufferSize string DefaultServerAccessLogOff bool DefaultServerReturn string FailTimeout string diff --git a/internal/configs/version2/__snapshots__/templates_test.snap b/internal/configs/version2/__snapshots__/templates_test.snap index 50f2fe69b4..a470046f6a 100644 --- a/internal/configs/version2/__snapshots__/templates_test.snap +++ b/internal/configs/version2/__snapshots__/templates_test.snap @@ -1876,6 +1876,56 @@ server { --- +[TestExecuteVirtualServerTemplate_RendersTemplateWithClientBodyBufferSize - 1] + + +server { + listen 80; + listen [::]:80; + + + server_name example.com; + status_zone example.com; + set $resource_type "virtualserver"; + set $resource_name ""; + set $resource_namespace ""; + + server_tokens ""; + + + + + location / { + set $service ""; + status_zone ""; + + + set $default_connection_header close; + proxy_connect_timeout ; + proxy_read_timeout ; + proxy_send_timeout ; + client_max_body_size ; + client_body_buffer_size 16k; + + proxy_buffering off; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $vs_connection_header; + proxy_pass_request_headers off; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $host; + proxy_set_header X-Forwarded-Port $server_port; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_pass http://test-upstream; + proxy_next_upstream ; + proxy_next_upstream_timeout ; + proxy_next_upstream_tries 0; + } +} + +--- + [TestExecuteVirtualServerTemplate_RendersTemplateWithCustomListener - 1] @@ -3052,6 +3102,7 @@ server { proxy_read_timeout 31s; proxy_send_timeout 32s; client_max_body_size 1m; + client_body_buffer_size 8k; proxy_max_temp_file_size 1024m; proxy_buffering on; diff --git a/internal/configs/version2/http.go b/internal/configs/version2/http.go index ea806bd563..da730fcf95 100644 --- a/internal/configs/version2/http.go +++ b/internal/configs/version2/http.go @@ -198,6 +198,7 @@ type Location struct { ProxyReadTimeout string ProxySendTimeout string ClientMaxBodySize string + ClientBodyBufferSize string ProxyMaxTempFileSize string ProxyBuffering bool ProxyBuffers string diff --git a/internal/configs/version2/nginx-plus.virtualserver.tmpl b/internal/configs/version2/nginx-plus.virtualserver.tmpl index 4886cc2463..9133427225 100644 --- a/internal/configs/version2/nginx-plus.virtualserver.tmpl +++ b/internal/configs/version2/nginx-plus.virtualserver.tmpl @@ -674,6 +674,9 @@ server { {{ $proxyOrGRPC }}_read_timeout {{ $l.ProxyReadTimeout }}; {{ $proxyOrGRPC }}_send_timeout {{ $l.ProxySendTimeout }}; client_max_body_size {{ $l.ClientMaxBodySize }}; + {{- if $l.ClientBodyBufferSize }} + client_body_buffer_size {{ $l.ClientBodyBufferSize }}; + {{- end }} {{- if $l.ProxyMaxTempFileSize }} proxy_max_temp_file_size {{ $l.ProxyMaxTempFileSize }}; diff --git a/internal/configs/version2/nginx.virtualserver.tmpl b/internal/configs/version2/nginx.virtualserver.tmpl index 13432de9d0..f4468e9464 100644 --- a/internal/configs/version2/nginx.virtualserver.tmpl +++ b/internal/configs/version2/nginx.virtualserver.tmpl @@ -368,6 +368,9 @@ server { {{ $proxyOrGRPC }}_read_timeout {{ $l.ProxyReadTimeout }}; {{ $proxyOrGRPC }}_send_timeout {{ $l.ProxySendTimeout }}; client_max_body_size {{ $l.ClientMaxBodySize }}; + {{- if $l.ClientBodyBufferSize }} + client_body_buffer_size {{ $l.ClientBodyBufferSize }}; + {{- end }} {{- if $l.ProxyMaxTempFileSize }} proxy_max_temp_file_size {{ $l.ProxyMaxTempFileSize }}; diff --git a/internal/configs/version2/templates_test.go b/internal/configs/version2/templates_test.go index 7b5253f92a..551bebdba4 100644 --- a/internal/configs/version2/templates_test.go +++ b/internal/configs/version2/templates_test.go @@ -440,6 +440,21 @@ func TestExecuteVirtualServerTemplate_RendersPlusTemplateWithHTTP2Off(t *testing t.Log(string(got)) } +func TestExecuteVirtualServerTemplate_RendersTemplateWithClientBodyBufferSize(t *testing.T) { + t.Parallel() + executor := newTmplExecutorNGINXPlus(t) + + got, err := executor.ExecuteVirtualServerTemplate(&virtualServerCfgWithClientBodyBufferSize) + if err != nil { + t.Error(err) + } + if !bytes.Contains(got, []byte("client_body_buffer_size 16k;")) { + t.Error("want `client_body_buffer_size 16k;` directive in generated template") + } + snaps.MatchSnapshot(t, string(got)) + t.Log(string(got)) +} + func TestExecuteVirtualServerTemplate_RendersOSSTemplateWithHTTP2On(t *testing.T) { t.Parallel() executor := newTmplExecutorNGINX(t) @@ -1594,6 +1609,7 @@ var ( ProxyReadTimeout: "31s", ProxySendTimeout: "32s", ClientMaxBodySize: "1m", + ClientBodyBufferSize: "8k", ProxyBuffering: true, ProxyBuffers: "8 4k", ProxyBufferSize: "4k", @@ -2171,6 +2187,20 @@ var ( }, } + virtualServerCfgWithClientBodyBufferSize = VirtualServerConfig{ + Server: Server{ + ServerName: "example.com", + StatusZone: "example.com", + Locations: []Location{ + { + Path: "/", + ProxyPass: "http://test-upstream", + ClientBodyBufferSize: "16k", + }, + }, + }, + } + virtualServerCfgWithRateLimitJWTClaim = VirtualServerConfig{ LimitReqZones: []LimitReqZone{ { diff --git a/internal/configs/virtualserver.go b/internal/configs/virtualserver.go index 8dc42d007a..9696afb174 100644 --- a/internal/configs/virtualserver.go +++ b/internal/configs/virtualserver.go @@ -2647,6 +2647,7 @@ func generateLocationForProxying(path string, upstreamName string, upstream conf ProxyReadTimeout: generateTimeWithDefault(upstream.ProxyReadTimeout, cfgParams.ProxyReadTimeout), ProxySendTimeout: generateTimeWithDefault(upstream.ProxySendTimeout, cfgParams.ProxySendTimeout), ClientMaxBodySize: generateString(upstream.ClientMaxBodySize, cfgParams.ClientMaxBodySize), + ClientBodyBufferSize: generateString(upstream.ClientBodyBufferSize, cfgParams.ClientBodyBufferSize), ProxyMaxTempFileSize: cfgParams.ProxyMaxTempFileSize, ProxyBuffering: generateBool(upstream.ProxyBuffering, cfgParams.ProxyBuffering), ProxyBuffers: generateBuffers(upstream.ProxyBuffers, cfgParams.ProxyBuffers), diff --git a/internal/configs/virtualserver_test.go b/internal/configs/virtualserver_test.go index bf5f8d78d1..d9dd0cf516 100644 --- a/internal/configs/virtualserver_test.go +++ b/internal/configs/virtualserver_test.go @@ -16221,6 +16221,7 @@ func TestGenerateLocationForProxying(t *testing.T) { ProxyReadTimeout: "31s", ProxySendTimeout: "32s", ClientMaxBodySize: "1m", + ClientBodyBufferSize: "16k", ProxyMaxTempFileSize: "1024m", ProxyBuffering: true, ProxyBuffers: "8 4k", @@ -16238,6 +16239,7 @@ func TestGenerateLocationForProxying(t *testing.T) { ProxyReadTimeout: "31s", ProxySendTimeout: "32s", ClientMaxBodySize: "1m", + ClientBodyBufferSize: "16k", ProxyMaxTempFileSize: "1024m", ProxyBuffering: true, ProxyBuffers: "8 4k", @@ -16269,6 +16271,7 @@ func TestGenerateLocationForGrpcProxying(t *testing.T) { ProxyReadTimeout: "31s", ProxySendTimeout: "32s", ClientMaxBodySize: "1m", + ClientBodyBufferSize: "16k", ProxyMaxTempFileSize: "1024m", ProxyBuffering: true, ProxyBuffers: "8 4k", @@ -16288,6 +16291,7 @@ func TestGenerateLocationForGrpcProxying(t *testing.T) { ProxyReadTimeout: "31s", ProxySendTimeout: "32s", ClientMaxBodySize: "1m", + ClientBodyBufferSize: "16k", ProxyMaxTempFileSize: "1024m", ProxyBuffering: true, ProxyBuffers: "8 4k", diff --git a/pkg/apis/configuration/v1/types.go b/pkg/apis/configuration/v1/types.go index 1f3dd4a6b7..a2fdfcbc91 100644 --- a/pkg/apis/configuration/v1/types.go +++ b/pkg/apis/configuration/v1/types.go @@ -157,6 +157,8 @@ type Upstream struct { ProxyBusyBuffersSize string `json:"busy-buffers-size"` // Sets the maximum allowed size of the client request body. The default is set in the client-max-body-size ConfigMap key. ClientMaxBodySize string `json:"client-max-body-size"` + // Sets the size of the buffer used for reading the client request body. + ClientBodyBufferSize string `json:"client-body-buffer-size"` // The TLS configuration for the Upstream. TLS UpstreamTLS `json:"tls"` // The health check configuration for the Upstream. Note: this feature is supported only in NGINX Plus. diff --git a/pkg/apis/configuration/validation/virtualserver.go b/pkg/apis/configuration/validation/virtualserver.go index e5edfff294..b8e5e57cac 100644 --- a/pkg/apis/configuration/validation/virtualserver.go +++ b/pkg/apis/configuration/validation/virtualserver.go @@ -634,6 +634,7 @@ func (vsv *VirtualServerValidator) validateUpstreams(upstreams []v1.Upstream, fi allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.Keepalive, idxPath.Child("keepalive"))...) allErrs = append(allErrs, validatePositiveIntOrZeroFromPointer(u.MaxConns, idxPath.Child("max-conns"))...) allErrs = append(allErrs, validateOffset(u.ClientMaxBodySize, idxPath.Child("client-max-body-size"))...) + allErrs = append(allErrs, validateSize(u.ClientBodyBufferSize, idxPath.Child("client-body-buffer-size"))...) allErrs = append(allErrs, validateUpstreamHealthCheck(u.HealthCheck, u.Type, idxPath.Child("healthCheck"))...) allErrs = append(allErrs, validateTime(u.SlowStart, idxPath.Child("slow-start"))...) allErrs = append(allErrs, validateBuffer(u.ProxyBuffers, idxPath.Child("buffers"))...) diff --git a/pkg/apis/configuration/validation/virtualserver_test.go b/pkg/apis/configuration/validation/virtualserver_test.go index 3a2c2edcda..27d84f9d53 100644 --- a/pkg/apis/configuration/validation/virtualserver_test.go +++ b/pkg/apis/configuration/validation/virtualserver_test.go @@ -550,6 +550,7 @@ func TestValidateUpstreams(t *testing.T) { ProxyNextUpstreamTries: 5, MaxConns: createPointerFromInt(16), Type: "grpc", + ClientBodyBufferSize: "16k", }, { Name: "upstream2", @@ -743,6 +744,20 @@ func TestValidateUpstreamsFails(t *testing.T) { }, msg: "invalid value for ClientMaxBodySize", }, + { + upstreams: []v1.Upstream{ + { + Name: "upstream1", + Service: "test-1", + Port: 80, + ClientBodyBufferSize: "10hello", + }, + }, + expectedUpstreamNames: map[string]sets.Empty{ + "upstream1": {}, + }, + msg: "invalid value for ClientBodyBufferSize", + }, { upstreams: []v1.Upstream{ {