Handle redirect with malformed Location headers missing host. (#774)

This commit is contained in:
Tom Christie 2020-01-17 11:42:51 +00:00 committed by GitHub
parent 5a63540e8a
commit 1f8fb28d93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 2 deletions

View File

@ -330,6 +330,11 @@ class BaseClient:
url = URL(location, allow_relative=True)
# Handle malformed 'Location' headers that are "absolute" form, have no host.
# See: https://github.com/encode/httpx/issues/771
if url.scheme and not url.host:
url = url.copy_with(host=request.url.host)
# Facilitate relative 'Location' headers, as allowed by RFC 7231.
# (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')
if url.is_relative_url:

View File

@ -201,7 +201,7 @@ class URL:
or "port" in kwargs
):
host = kwargs.pop("host", self.host)
port = kwargs.pop("port", self.port)
port = kwargs.pop("port", None if self.is_relative_url else self.port)
username = kwargs.pop("username", self.username)
password = kwargs.pop("password", self.password)
@ -216,7 +216,10 @@ class URL:
kwargs["authority"] = authority
return URL(self._uri_reference.copy_with(**kwargs).unsplit())
return URL(
self._uri_reference.copy_with(**kwargs).unsplit(),
allow_relative=self.is_relative_url,
)
def join(self, relative_url: URLTypes) -> "URL":
"""

View File

@ -56,6 +56,10 @@ class MockDispatch(AsyncDispatcher):
headers = {"location": "/"}
return Response(codes.SEE_OTHER, headers=headers, request=request)
elif request.url.path == "/malformed_redirect":
headers = {"location": "https://:443/"}
return Response(codes.SEE_OTHER, headers=headers, request=request)
elif request.url.path == "/no_scheme_redirect":
headers = {"location": "//example.org/"}
return Response(codes.SEE_OTHER, headers=headers, request=request)
@ -176,6 +180,16 @@ async def test_relative_redirect():
assert len(response.history) == 1
@pytest.mark.usefixtures("async_environment")
async def test_malformed_redirect():
# https://github.com/encode/httpx/issues/771
client = AsyncClient(dispatch=MockDispatch())
response = await client.get("http://example.org/malformed_redirect")
assert response.status_code == codes.OK
assert response.url == URL("https://example.org/")
assert len(response.history) == 1
@pytest.mark.usefixtures("async_environment")
async def test_no_scheme_redirect():
client = AsyncClient(dispatch=MockDispatch())