request-decompress
The request-decompress plugin decompresses a request body sent with a Content-Encoding of gzip or deflate, which RFC 9110 allows, and rewrites the request with the plain body. Every plugin that runs after it reads the decompressed content, so plugins such as oas-validator and request-validation work on a compressed request the same way they do on a plain one. Logging plugins also record the decompressed body, unless forward_compressed is enabled.
The plugin is available in API7 Enterprise from version 3.10.9.
A request without a Content-Encoding header passes through untouched and its body is never read, so the same route serves both compressed and uncompressed clients. A gzip body may carry several members concatenated, which RFC 1952 allows: every member is decompressed, and max_req_body_size bounds their total.
By default, the upstream also receives the decompressed body and the Content-Encoding header is removed. Set forward_compressed to true to send a compressed body upstream instead.
The plugin rejects the following requests:
| Condition | Status code | Notes |
|---|---|---|
A coding other than gzip, deflate or identity, such as br | 415 | The response carries an Accept-Encoding: gzip, deflate header. |
| A malformed compressed body, or bytes trailing the last gzip member | 400 | |
A compressed body beyond max_req_body_size, as received or once decompressed | 413 | A request without Content-Encoding is not checked. |
Plugin Order
request-decompress runs in the rewrite phase with a priority of 2850. That places it after the plugins that reject a request cheaply, so a blocked request is never decompressed, and before every plugin that reads the request body. A route that configures the plugin takes precedence over a global rule that also configures it, so the request body is handled once with the route's settings.
Both of the following can be changed with the _meta.priority setting of the affected plugin:
hmac-authwithvalidate_request_bodyenabled hashes the decompressed body on a route that also enables this plugin. A client that signs the compressed bytes needshmac-authconfigured with a priority above2850.- Decompression happens before authentication, so an unauthenticated client can cause decompression work. It is bounded by
max_req_body_sizeand by the gateway'sclient_max_body_size.
With forward_compressed enabled, the compressed body is restored just before the request is proxied, so the plugins that read the body in the log phase see the compressed bytes.
During an upgrade, enable this plugin only after every gateway instance of the gateway group runs version 3.10.9 or later. An older gateway instance does not have the plugin and discards the configuration that uses it.
Examples
The examples below demonstrate how you can configure request-decompress for different scenarios.
If Admin API authentication is enabled, export an admin-role key before continuing:
export ADMIN_API_KEY=replace-with-admin-api-key
Decompress a Request Body
The following example demonstrates how to decompress a gzip request body and forward the plain body to the upstream.
- Admin API
- ADC
- Ingress Controller
Create a route with the request-decompress plugin:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "request-decompress-route",
"uri": "/anything",
"plugins": {
"request-decompress": {}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Create a route with the request-decompress plugin:
services:
- name: request-decompress-service
labels:
example: request-decompress
routes:
- name: request-decompress-route
uris:
- /anything
plugins:
request-decompress: {}
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector example=request-decompress
- Gateway API
- APISIX CRD
Create a route with the request-decompress plugin:
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: request-decompress-plugin-config
spec:
plugins:
- name: request-decompress
config: {}
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: request-decompress-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: request-decompress-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
Create a route with the request-decompress plugin:
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: request-decompress-route
spec:
ingressClassName: apisix
http:
- name: request-decompress-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: request-decompress
enable: true
config: {}
Apply the configuration to your cluster:
kubectl apply -f request-decompress-ic.yaml
Send a gzip-compressed body to the route:
echo -n '{"name":"world"}' | gzip | curl "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-H "Content-Encoding: gzip" \
--data-binary @-
You should see a response similar to the following, showing that the upstream received the plain body and no Content-Encoding header:
{
"data": "{\"name\":\"world\"}",
"headers": {
"Content-Type": "application/json",
...
},
...
}
Send a body compressed with an unsupported coding:
echo -n '{"name":"world"}' | curl -i "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-H "Content-Encoding: br" \
--data-binary @-
You should receive an HTTP/1.1 415 Unsupported Media Type response with an Accept-Encoding: gzip, deflate header.
Forward a Compressed Body to the Upstream
The following example demonstrates how to keep the upstream receiving a compressed body while the plugins on the route still read the plain one.
- Admin API
- ADC
- Ingress Controller
Create a route with the request-decompress plugin:
curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
-H "X-API-KEY: ${ADMIN_API_KEY}" \
-d '{
"id": "request-decompress-route",
"uri": "/anything",
"plugins": {
"request-decompress": {
"forward_compressed": true
}
},
"upstream": {
"type": "roundrobin",
"nodes": {
"httpbin.org:80": 1
}
}
}'
Create a route with the request-decompress plugin:
services:
- name: request-decompress-service
labels:
example: request-decompress
routes:
- name: request-decompress-route
uris:
- /anything
plugins:
request-decompress:
forward_compressed: true
upstream:
type: roundrobin
nodes:
- host: httpbin.org
port: 80
weight: 1
Synchronize the configuration to the gateway:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector example=request-decompress
- Gateway API
- APISIX CRD
Create a route with the request-decompress plugin:
apiVersion: v1
kind: Service
metadata:
namespace: aic
name: httpbin-external-domain
spec:
type: ExternalName
externalName: httpbin.org
---
apiVersion: apisix.apache.org/v1alpha1
kind: PluginConfig
metadata:
namespace: aic
name: request-decompress-plugin-config
spec:
plugins:
- name: request-decompress
config:
forward_compressed: true
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
namespace: aic
name: request-decompress-route
spec:
parentRefs:
- name: apisix
rules:
- matches:
- path:
type: Exact
value: /anything
filters:
- type: ExtensionRef
extensionRef:
group: apisix.apache.org
kind: PluginConfig
name: request-decompress-plugin-config
backendRefs:
- name: httpbin-external-domain
port: 80
Create a route with the request-decompress plugin:
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: aic
name: httpbin-external-domain
spec:
ingressClassName: apisix
externalNodes:
- type: Domain
name: httpbin.org
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: aic
name: request-decompress-route
spec:
ingressClassName: apisix
http:
- name: request-decompress-route
match:
paths:
- /anything
upstreams:
- name: httpbin-external-domain
plugins:
- name: request-decompress
enable: true
config:
forward_compressed: true
Apply the configuration to your cluster:
kubectl apply -f request-decompress-ic.yaml
Send a gzip-compressed body to the route:
echo -n '{"name":"world"}' | gzip | curl "http://127.0.0.1:9080/anything" -X POST \
-H "Content-Type: application/json" \
-H "Content-Encoding: gzip" \
--data-binary @-
The upstream receives the body still compressed, with the Content-Encoding: gzip header restored. When no plugin modified the body, the bytes are forwarded exactly as the client sent them. When a plugin rewrote the body, it is compressed again with the coding the client used.