mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
fix(passthrough): stop forwarding client Accept-Encoding upstream
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
f03df1bb42
commit
367f00315f
2 changed files with 78 additions and 10 deletions
|
|
@ -18,12 +18,26 @@ _PASS_THROUGH_PROTECTED_HEADERS: Final[frozenset] = frozenset(
|
|||
"x-goog-api-key",
|
||||
"host",
|
||||
"content-length",
|
||||
"accept-encoding",
|
||||
}
|
||||
)
|
||||
|
||||
# Header name prefix used to block AWS SigV4 signing headers from being overridden.
|
||||
_PASS_THROUGH_PROTECTED_HEADER_PREFIXES: Final[tuple] = ("x-amz-",)
|
||||
|
||||
# Client headers that must never reach the upstream provider. `accept-encoding` is
|
||||
# dropped so httpx negotiates a content coding it actually has a decoder for: an
|
||||
# encoding it cannot decode (e.g. brotli, absent the optional `brotli` package)
|
||||
# leaves the body compressed while get_response_headers strips Content-Encoding,
|
||||
# handing the client bytes it has no way to read.
|
||||
_NON_FORWARDED_REQUEST_HEADERS: Final[frozenset[str]] = frozenset(
|
||||
{
|
||||
"content-length",
|
||||
"host",
|
||||
"accept-encoding",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class BasePassthroughUtils:
|
||||
@staticmethod
|
||||
|
|
@ -66,17 +80,14 @@ class BasePassthroughUtils:
|
|||
e.g., 'x-pass-anthropic-beta: value' becomes 'anthropic-beta: value'
|
||||
"""
|
||||
if forward_headers is True:
|
||||
# Header We Should NOT forward
|
||||
request_headers.pop("content-length", None)
|
||||
request_headers.pop("host", None)
|
||||
|
||||
custom_header_names: Final = {header_name.lower() for header_name in headers}
|
||||
for header_name in list(request_headers.keys()):
|
||||
if header_name.lower() in custom_header_names:
|
||||
request_headers.pop(header_name, None)
|
||||
|
||||
# Combine request headers with custom headers
|
||||
headers = {**request_headers, **headers}
|
||||
forwardable_headers: Final = {
|
||||
header_name: header_value
|
||||
for header_name, header_value in request_headers.items()
|
||||
if header_name.lower() not in _NON_FORWARDED_REQUEST_HEADERS
|
||||
and header_name.lower() not in custom_header_names
|
||||
}
|
||||
headers = {**forwardable_headers, **headers}
|
||||
|
||||
# Process x-pass- prefixed headers (strip prefix and forward)
|
||||
# Credential and protocol-level headers are excluded from this mechanism.
|
||||
|
|
|
|||
57
tests/test_litellm/passthrough/test_passthrough_utils.py
Normal file
57
tests/test_litellm/passthrough/test_passthrough_utils.py
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
import httpx
|
||||
from httpx._decoders import SUPPORTED_DECODERS
|
||||
|
||||
from litellm.passthrough.utils import BasePassthroughUtils
|
||||
|
||||
ANTHROPIC_MESSAGES_URL = "https://api.anthropic.com/v1/messages"
|
||||
|
||||
|
||||
def _build_upstream_request(forwarded_headers: dict) -> httpx.Request:
|
||||
with httpx.Client(headers={"user-agent": "litellm/test"}) as client:
|
||||
return client.build_request("POST", ANTHROPIC_MESSAGES_URL, headers=forwarded_headers)
|
||||
|
||||
|
||||
def test_client_accept_encoding_is_not_forwarded_upstream():
|
||||
headers = BasePassthroughUtils.forward_headers_from_request(
|
||||
request_headers={
|
||||
"accept-encoding": "br",
|
||||
"anthropic-version": "2023-06-01",
|
||||
"host": "localhost:4000",
|
||||
"content-length": "123",
|
||||
},
|
||||
headers={"x-api-key": "sk-anthropic"},
|
||||
forward_headers=True,
|
||||
)
|
||||
|
||||
assert "accept-encoding" not in {name.lower() for name in headers}
|
||||
assert headers["anthropic-version"] == "2023-06-01"
|
||||
assert "host" not in headers
|
||||
assert "content-length" not in headers
|
||||
|
||||
|
||||
def test_client_accept_encoding_is_not_forwarded_via_x_pass_prefix():
|
||||
headers = BasePassthroughUtils.forward_headers_from_request(
|
||||
request_headers={"x-pass-accept-encoding": "br"},
|
||||
headers={},
|
||||
forward_headers=False,
|
||||
)
|
||||
|
||||
assert "accept-encoding" not in headers
|
||||
|
||||
|
||||
def test_upstream_request_only_advertises_decodable_encodings():
|
||||
"""A content coding httpx cannot decode would reach the client still compressed,
|
||||
with Content-Encoding stripped by get_response_headers (LIT-5613)."""
|
||||
forwarded_headers = BasePassthroughUtils.forward_headers_from_request(
|
||||
request_headers={"accept-encoding": "br, zstd, exotic"},
|
||||
headers={"x-api-key": "sk-anthropic"},
|
||||
forward_headers=True,
|
||||
)
|
||||
|
||||
advertised = {
|
||||
value.strip().lower()
|
||||
for value in _build_upstream_request(forwarded_headers).headers["accept-encoding"].split(",")
|
||||
}
|
||||
|
||||
assert advertised
|
||||
assert advertised <= set(SUPPORTED_DECODERS)
|
||||
Loading…
Add table
Reference in a new issue