Preserve the response when a redirect has an invalid Location and redirects are not followed

Merged
DE
4d ago
+47-23

Description

When follow_redirects=False and a 3XX response carries a malformed Location header (e.g. a data: URI), _send_handling_redirects builds the redirect request before checking follow_redirects, so _redirect_url raises RemoteProtocolError and the response — along with its headers — is discarded. A caller that only wants to inspect the redirect without following it never gets the response.

This is a real-world need: the Wapiti web scanner reads the Location header of such responses to detect reflected payloads, and currently cannot — see wapiti-scanner/wapiti#690. A malformed Location is still part of an otherwise valid HTTP response; not following the redirect shouldn't mean losing the response.

The same fix was proposed upstream in encode/httpx#3706 (still open, refs discussion encode/httpx#3179) and is already shipped by the httpxyz fork.

The fix

Defer building the redirect request to the branch that actually follows redirects. When not following, building next_request is best-effort: a malformed Location leaves next_request=None and the response is returned intact. Applied to both the sync and async _send_handling_redirects.

response = httpx2.get(url, follow_redirects=False)  # url 302s to a data: URI
assert response.status_code == 302
assert response.headers["location"] == "data:..."   # header preserved
assert response.next_request is None

Following redirects (follow_redirects=True) still raises RemoteProtocolError on an invalid Location, unchanged.

Tests

Adds sync and async regression tests (test_invalid_redirect_not_followed) reusing the existing /invalid_redirect mock endpoint. scripts/check (ruff format, mypy, ruff check, unasync) and the redirect test suite pass.