refactor(compression): 请求压缩策略改为跟随客户端行为,响应支持gzip压缩

- 移除全局 ENABLE_REQUEST_COMPRESSION 配置,改为根据客户端 Content-Encoding
  决定是否对上游请求体进行 gzip 压缩
- 非流式响应根据客户端 Accept-Encoding 返回 gzip 压缩的 JSON
- ApiRequestContext 记录客户端编码偏好并透传至 handler 链路
- 新增 http_compression 模块统一处理压缩相关判断逻辑
- 上游请求头丢弃列表新增 content-encoding 防止客户端值泄露
- ensure_json_body 支持解压 gzip 编码的请求体
This commit is contained in:
fawney19
2026-03-01 15:52:32 +08:00
parent 2dcccc9820
commit 2c1e51a490
16 changed files with 469 additions and 47 deletions

View File

@@ -0,0 +1,48 @@
from __future__ import annotations
import gzip
import json
from src.services.proxy_node.resolver import build_post_kwargs, build_stream_kwargs
class TestProxyResolverCompression:
def test_build_post_kwargs_compresses_when_client_sent_gzip(self) -> None:
payload = {"message": "hello", "tokens": [1, 2, 3]}
kwargs = build_post_kwargs(
None,
url="https://example.com/v1/messages",
headers={"Content-Type": "application/json"},
payload=payload,
timeout=10.0,
client_content_encoding="gzip",
)
assert kwargs["headers"]["Content-Encoding"] == "gzip"
assert json.loads(gzip.decompress(kwargs["content"]).decode("utf-8")) == payload
def test_build_post_kwargs_keeps_plain_body_without_client_gzip(self) -> None:
payload = {"message": "plain"}
kwargs = build_post_kwargs(
None,
url="https://example.com/v1/messages",
headers={"Content-Type": "application/json"},
payload=payload,
timeout=10.0,
client_content_encoding=None,
)
assert "Content-Encoding" not in kwargs["headers"]
assert json.loads(kwargs["content"].decode("utf-8")) == payload
def test_build_stream_kwargs_drops_stale_content_encoding_header(self) -> None:
kwargs = build_stream_kwargs(
None,
url="https://example.com/v1/messages",
headers={"content-encoding": "gzip", "Content-Type": "application/json"},
payload={"message": "no-gzip"},
timeout=10.0,
client_content_encoding=None,
)
assert all(key.lower() != "content-encoding" for key in kwargs["headers"])