request-decompress
request-decompress 插件解压 Content-Encoding 为 gzip 或 deflate 的请求体(RFC 9110 允许客户端这样发送),并用解压后的请求体改写请求。在它之后运行的每个插件读取的都是解压后的内容,因此 oas-validator 和 request-validation 处理压缩请求的方式与处理未压缩请求相同。日志插件同样记录解压后的请求体,除非开启了 forward_compressed。
该插件自 API7 企业版 3.10.9 起可用。
不带 Content-Encoding 请求头的请求原样通过,其请求体不会被读取,因此同一路由可以同时服务发送压缩和未压缩请求的客户端。gzip 请求体可以由多个成员拼接而成(RFC 1952 允许),每个成员都会被解压,max_req_body_size 限制它们的总大小。
默认情况下,上游同样收到解压后的请求体,Content-Encoding 请求头会被移除。将 forward_compressed 设为 true 可以改为向上游发送压缩的请求体。
插件会拒绝以下请求:
| 条件 | 状态码 | 说明 |
|---|---|---|
使用 gzip、deflate 或 identity 以外的编码,例如 br | 415 | 响应带有 Accept-Encoding: gzip, deflate 响应头。 |
| 压缩数据格式错误,或最后一个 gzip 成员之后还有多余字节 | 400 | |
收到的压缩请求体或解压后的请求体超过 max_req_body_size | 413 | 不带 Content-Encoding 的请求不做该检查。 |
插件执行顺序
request-decompress 在 rewrite 阶段运行,优先级为 2850。因此它排在能以较低代价拒绝请求的插件之后,被拦截的请求不会被解压;同时排在所有读取请求体的插件之前。路由上配置的该插件优先于全局规则中配置的同一插件,因此请求体只按路由的配置处理一次。
以下两点都可以通过相关插件的 _meta.priority 配置调整:
- 在同时启用了本插件的路由上,开启
validate_request_body的hmac-auth对解压后的请求体计算摘要。如果客户端对压缩后的字节签名,需要将hmac-auth的优先级配置为高于2850。 - 解压发生在认证之前,因此未认证的客户端也能触发解压开销,该开销受
max_req_body_size和网关的client_max_body_size限制。
开启 forward_compressed 时,压缩的请求体在请求被代理前才恢复,因此在 log 阶段读取请求体的插件看到的是压缩后的字节。
升级期间,请在网关组的所有网关实例都升级到 3.10.9 或更高版本后再启用该插件。旧版本的网关实例没有该插件,会丢弃使用它的配置。
示例
以下示例演示如何在不同场景下配置 request-decompress。
如果启用了 Admin API 认证,请先导出具有 admin 角色的 key:
export ADMIN_API_KEY=replace-with-admin-api-key
解压请求体
以下示例演示如何解压 gzip 请求体并将解压后的请求体转发给上游。
- Admin API
- ADC
- Ingress Controller
创建启用了 request-decompress 插件的路由:
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
}
}
}'
创建启用了 request-decompress 插件的路由:
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
将配置同步到网关:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector example=request-decompress
- Gateway API
- APISIX CRD
创建启用了 request-decompress 插件的路由:
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
创建启用了 request-decompress 插件的路由:
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: {}
将配置应用到集群:
kubectl apply -f request-decompress-ic.yaml
向路由发送 gzip 压缩的请求体:
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 @-
你应该看到类似以下的响应,表明上游收到了解压后的请求体,且没有 Content-Encoding 请求头:
{
"data": "{\"name\":\"world\"}",
"headers": {
"Content-Type": "application/json",
...
},
...
}
发送使用不支持的编码压缩的请求体:
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 @-
你应该收到 HTTP/1.1 415 Unsupported Media Type 响应,并带有 Accept-Encoding: gzip, deflate 响应头。
向上游转发压缩的请求体
以下示例演示如何让上游继续收到压缩的请求体,同时路由上的插件仍读取解压后的请求体。
- Admin API
- ADC
- Ingress Controller
创建启用了 request-decompress 插件的路由:
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
}
}
}'
创建启用了 request-decompress 插件的路由:
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
将配置同步到网关:
adc sync -f adc.yaml \
--include-resource-type service \
--label-selector example=request-decompress
- Gateway API
- APISIX CRD
创建启用了 request-decompress 插件的路由:
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
创建启用了 request-decompress 插件的路由:
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
将配置应用到集群:
kubectl apply -f request-decompress-ic.yaml
向路由发送 gzip 压缩的请求体:
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 @-
上游收到的请求体仍是压缩的,Content-Encoding: gzip 请求头也会恢复。如果没有插件修改请求体,转发的字节与客户端发送的完全相同;如果有插件改写了请求体,会使用客户端所用的编码重新压缩。