test(proxy): drop transfer encoding on replay

This commit is contained in:
Alex Schapiro
2026-07-21 01:49:35 +00:00
parent 5bfe6604d4
commit 511397eb90
2 changed files with 28 additions and 5 deletions
+12 -5
View File
@@ -167,6 +167,9 @@ async def get_request_with_client(
return await client.request.get(request_id, opts)
_FRAMING_HEADERS = frozenset({"content-length", "transfer-encoding"})
def build_raw_request(
*,
method: str,
@@ -187,11 +190,15 @@ def build_raw_request(
final_headers = {**headers}
final_headers.setdefault("Host", parsed.netloc)
final_headers.setdefault("User-Agent", "strix")
# A Content-Length inherited from the captured request describes the ORIGINAL
# body; once the body is modified for replay it is stale. Drop any inherited
# value (case-insensitively) and recompute it from the body actually being
# sent, so the replayed request is never desynced (truncated / smuggled).
final_headers = {k: v for k, v in final_headers.items() if k.title() != "Content-Length"}
# Framing headers inherited from the captured request describe the ORIGINAL
# body; once the body is modified for replay they are stale. We always send a
# plain (non-chunked) body with an explicit Content-Length, so drop any
# inherited Content-Length AND Transfer-Encoding (case-insensitively) and
# recompute the length from the body actually being sent. This keeps the two
# framing mechanisms from conflicting (RFC 7230 3.3.3: a leftover
# Transfer-Encoding would make the target ignore Content-Length and try to
# parse the body as chunked), so the replay is never desynced.
final_headers = {k: v for k, v in final_headers.items() if k.lower() not in _FRAMING_HEADERS}
if body:
final_headers["Content-Length"] = str(len(body.encode("utf-8")))
+16
View File
@@ -166,6 +166,22 @@ def test_build_raw_request_recomputes_content_length_for_modified_body() -> None
assert _headers_named(raw, "Content-Length") == [str(len(body.encode("utf-8")))]
def test_build_raw_request_drops_transfer_encoding_for_modified_body() -> None:
body = '{"user":"updated"}'
_conn, raw = caido_api.build_raw_request(
method="POST",
url="https://example.com/login",
headers={
"tRaNsFeR-EnCoDiNg": "chunked",
"Content-Length": "7",
"Content-Type": "application/json",
},
body=body,
)
assert _headers_named(raw, "Transfer-Encoding") == []
assert _headers_named(raw, "Content-Length") == [str(len(body.encode("utf-8")))]
def test_build_raw_request_drops_stale_content_length_for_empty_body() -> None:
# A body cleared to empty must not keep the inherited (non-zero) length.
_conn, raw = caido_api.build_raw_request(