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

@@ -1,11 +1,17 @@
"""测试 handler 基础工具函数"""
import gzip
import json
import pytest
from src.api.handlers.base.utils import (
build_json_response_for_client,
build_sse_headers,
extract_cache_creation_tokens,
filter_proxy_response_headers,
resolve_client_accept_encoding,
resolve_client_content_encoding,
)
@@ -135,3 +141,51 @@ class TestFilterProxyResponseHeaders:
assert result["X-Request-Id"] == "abc"
assert result["Anthropic-RateLimit-Requests-Remaining"] == "10"
class TestResolveClientEncoding:
def test_content_encoding_prefers_hint(self) -> None:
headers = {"content-encoding": "gzip"}
result = resolve_client_content_encoding(headers, hinted_content_encoding="br")
assert result == "br"
def test_content_encoding_fallback_to_headers(self) -> None:
headers = {"Content-Encoding": "gzip"}
result = resolve_client_content_encoding(headers)
assert result == "gzip"
def test_accept_encoding_prefers_hint(self) -> None:
headers = {"accept-encoding": "gzip"}
result = resolve_client_accept_encoding(headers, hinted_accept_encoding="br")
assert result == "br"
def test_accept_encoding_fallback_to_headers(self) -> None:
headers = {"Accept-Encoding": "gzip, deflate"}
result = resolve_client_accept_encoding(headers)
assert result == "gzip, deflate"
class TestBuildJsonResponseForClient:
def test_returns_gzip_response_when_client_accepts_gzip(self) -> None:
response = build_json_response_for_client(
status_code=200,
content={"ok": True},
headers={"content-type": "application/json"},
client_accept_encoding="gzip, deflate",
)
assert response.headers.get("content-encoding") == "gzip"
assert "accept-encoding" in response.headers.get("vary", "").lower()
decompressed = gzip.decompress(bytes(response.body))
assert json.loads(decompressed.decode("utf-8")) == {"ok": True}
def test_returns_plain_json_when_gzip_not_accepted(self) -> None:
response = build_json_response_for_client(
status_code=200,
content={"ok": True},
headers={"content-type": "application/json"},
client_accept_encoding="gzip;q=0, deflate",
)
assert response.headers.get("content-encoding") is None
assert json.loads(bytes(response.body).decode("utf-8")) == {"ok": True}