]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Handle redirect with malformed Location headers missing host. (#774)
authorTom Christie <tom@tomchristie.com>
Fri, 17 Jan 2020 11:42:51 +0000 (11:42 +0000)
committerGitHub <noreply@github.com>
Fri, 17 Jan 2020 11:42:51 +0000 (11:42 +0000)
httpx/client.py
httpx/models.py
tests/client/test_redirects.py

index 590d5d0ed2e517fee636c3fe9f82cbd6a24d05c3..49914787a88e168ad45fb7f6e695fc47b3eee258 100644 (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:
index acaaf66d4a60dd34533a067510453bc945a48f32..8d22738b14d2341beb643000a6eafeda0809924a 100644 (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":
         """
index eab44f01623c495ba3bf48bc4f40b3c05dad46c2..1717d782a55bf7f437f427e5f2fb0ec40db0c47a 100644 (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())