跳到主要内容

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 以外的编码,例如 br415响应带有 Accept-Encoding: gzip, deflate 响应头。
压缩数据格式错误,或最后一个 gzip 成员之后还有多余字节400
收到的压缩请求体或解压后的请求体超过 max_req_body_size413不带 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 请求体并将解压后的请求体转发给上游。

创建启用了 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
}
}
}'

向路由发送 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 响应头。

向上游转发压缩的请求体​

以下示例演示如何让上游继续收到压缩的请求体,同时路由上的插件仍读取解压后的请求体。

创建启用了 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
}
}
}'

向路由发送 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 请求头也会恢复。如果没有插件修改请求体,转发的字节与客户端发送的完全相同;如果有插件改写了请求体,会使用客户端所用的编码重新压缩。