]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Header refinements (#1248)
authorTom Christie <tom@tomchristie.com>
Wed, 2 Sep 2020 20:32:48 +0000 (21:32 +0100)
committerGitHub <noreply@github.com>
Wed, 2 Sep 2020 20:32:48 +0000 (21:32 +0100)
Co-authored-by: Florimond Manca <florimond.manca@gmail.com>
httpx/_client.py
httpx/_models.py
tests/client/test_headers.py
tests/models/test_requests.py

index b78c92ea10232657a0067466aa33edf16619893f..d6a0caf0855b5ab097ca0498234947122cc54b55 100644 (file)
@@ -5,6 +5,7 @@ from types import TracebackType
 
 import httpcore
 
+from .__version__ import __version__
 from ._auth import Auth, BasicAuth, FunctionAuth
 from ._config import (
     DEFAULT_LIMITS,
@@ -18,6 +19,7 @@ from ._config import (
     create_ssl_context,
 )
 from ._content_streams import ContentStream
+from ._decoders import SUPPORTED_DECODERS
 from ._exceptions import (
     HTTPCORE_EXC_MAP,
     InvalidURL,
@@ -55,6 +57,10 @@ from ._utils import (
 logger = get_logger(__name__)
 
 KEEPALIVE_EXPIRY = 5.0
+USER_AGENT = f"python-httpx/{__version__}"
+ACCEPT_ENCODING = ", ".join(
+    [key for key in SUPPORTED_DECODERS.keys() if key != "identity"]
+)
 
 
 class BaseClient:
@@ -74,7 +80,7 @@ class BaseClient:
 
         self._auth = self._build_auth(auth)
         self._params = QueryParams(params)
-        self._headers = Headers(headers)
+        self.headers = Headers(headers)
         self._cookies = Cookies(cookies)
         self._timeout = Timeout(timeout)
         self.max_redirects = max_redirects
@@ -161,7 +167,16 @@ class BaseClient:
 
     @headers.setter
     def headers(self, headers: HeaderTypes) -> None:
-        self._headers = Headers(headers)
+        client_headers = Headers(
+            {
+                b"Accept": b"*/*",
+                b"Accept-Encoding": ACCEPT_ENCODING.encode("ascii"),
+                b"Connection": b"keep-alive",
+                b"User-Agent": USER_AGENT.encode("ascii"),
+            }
+        )
+        client_headers.update(headers)
+        self._headers = client_headers
 
     @property
     def cookies(self) -> Cookies:
@@ -299,11 +314,9 @@ class BaseClient:
         Merge a headers argument together with any headers on the client,
         to create the headers used for the outgoing request.
         """
-        if headers or self.headers:
-            merged_headers = Headers(self.headers)
-            merged_headers.update(headers)
-            return merged_headers
-        return headers
+        merged_headers = Headers(self.headers)
+        merged_headers.update(headers)
+        return merged_headers
 
     def _merge_queryparams(
         self, params: QueryParamTypes = None
index 4a4026326646f0e187e6e0a56951c42ea251588a..17ba955890ecb395ed84e34604fdc350a3cd7e07 100644 (file)
@@ -14,7 +14,6 @@ import chardet
 import rfc3986
 import rfc3986.exceptions
 
-from .__version__ import __version__
 from ._content_streams import ByteStream, ContentStream, encode
 from ._decoders import (
     SUPPORTED_DECODERS,
@@ -103,13 +102,11 @@ class URL:
 
     @property
     def username(self) -> str:
-        userinfo = self._uri_reference.userinfo or ""
-        return unquote(userinfo.partition(":")[0])
+        return unquote(self.userinfo.partition(":")[0])
 
     @property
     def password(self) -> str:
-        userinfo = self._uri_reference.userinfo or ""
-        return unquote(userinfo.partition(":")[2])
+        return unquote(self.userinfo.partition(":")[2])
 
     @property
     def host(self) -> str:
@@ -580,12 +577,6 @@ class Headers(typing.MutableMapping[str, str]):
         return self.get_list(key, split_commas=split_commas)
 
 
-USER_AGENT = f"python-httpx/{__version__}"
-ACCEPT_ENCODING = ", ".join(
-    [key for key in SUPPORTED_DECODERS.keys() if key != "identity"]
-)
-
-
 class Request:
     def __init__(
         self,
@@ -627,26 +618,12 @@ class Request:
         has_content_length = (
             "content-length" in self.headers or "transfer-encoding" in self.headers
         )
-        has_user_agent = "user-agent" in self.headers
-        has_accept = "accept" in self.headers
-        has_accept_encoding = "accept-encoding" in self.headers
-        has_connection = "connection" in self.headers
-
-        if not has_host:
-            url = self.url
-            if url.userinfo:
-                url = url.copy_with(username=None, password=None)
-            auto_headers.append((b"host", url.authority.encode("ascii")))
+
+        if not has_host and self.url.authority:
+            host = self.url.copy_with(username=None, password=None).authority
+            auto_headers.append((b"host", host.encode("ascii")))
         if not has_content_length and self.method in ("POST", "PUT", "PATCH"):
             auto_headers.append((b"content-length", b"0"))
-        if not has_user_agent:
-            auto_headers.append((b"user-agent", USER_AGENT.encode("ascii")))
-        if not has_accept:
-            auto_headers.append((b"accept", b"*/*"))
-        if not has_accept_encoding:
-            auto_headers.append((b"accept-encoding", ACCEPT_ENCODING.encode()))
-        if not has_connection:
-            auto_headers.append((b"connection", b"keep-alive"))
 
         self.headers = Headers(auto_headers + self.headers.raw)
 
index 34a17c3916287edd7491bee7383c0b28da1983cc..c86eae33c17a1d93407a47c85bafa2ac0b8935e7 100755 (executable)
@@ -124,6 +124,28 @@ def test_header_update():
     }
 
 
+def test_remove_default_header():
+    """
+    Remove a default header from the Client.
+    """
+    url = "http://example.org/echo_headers"
+
+    client = httpx.Client(transport=MockTransport())
+    del client.headers["User-Agent"]
+
+    response = client.get(url)
+
+    assert response.status_code == 200
+    assert response.json() == {
+        "headers": {
+            "accept": "*/*",
+            "accept-encoding": "gzip, deflate, br",
+            "connection": "keep-alive",
+            "host": "example.org",
+        }
+    }
+
+
 def test_header_does_not_exist():
     headers = httpx.Headers({"foo": "bar"})
     with pytest.raises(KeyError):
index f2ee98edb6f7fb7f93a0bae8075e8ddbd6cb2c9f..8b8325e314a45fef4cbf8688659520b6fd009f6f 100644 (file)
@@ -34,6 +34,18 @@ def test_json_encoded_data():
     assert request.content == b'{"test": 123}'
 
 
+def test_headers():
+    request = httpx.Request("POST", "http://example.org", json={"test": 123})
+
+    assert request.headers == httpx.Headers(
+        {
+            "Host": "example.org",
+            "Content-Type": "application/json",
+            "Content-Length": "13",
+        }
+    )
+
+
 def test_read_and_stream_data():
     # Ensure a request may still be streamed if it has been read.
     # Needed for cases such as authentication classes that read the request body.