diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dec308b..9c81df57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Improve logging with per-module logger names. (#690) - Resolve race condition during import of `anyio` package. (#692) - Enable TCP_NODELAY for all synchronous sockets. (#651) +- Add `sni_hostname` request extension. (#696) ## 0.17.1 (May 17th, 2023) diff --git a/docs/extensions.md b/docs/extensions.md index e7bb9a7d..923fe789 100644 --- a/docs/extensions.md +++ b/docs/extensions.md @@ -147,6 +147,23 @@ The following event types are currently exposed... * `"http2.receive_response_body"` * `"http2.response_closed"` +### `"sni_hostname"` + +The server's hostname, which is used to confirm the hostname supplied by the SSL certificate. + +For example: + +``` python +headers = {"Host": "www.encode.io"} +extensions = {"sni_hostname": "www.encode.io"} +response = httpcore.request( + "GET", + "https://185.199.108.153", + headers=headers, + extensions=extensions +) +``` + ## Response Extensions ### `"http_version"` diff --git a/httpcore/_async/connection.py b/httpcore/_async/connection.py index 34e69737..0c8127a0 100644 --- a/httpcore/_async/connection.py +++ b/httpcore/_async/connection.py @@ -95,6 +95,7 @@ async def handle_async_request(self, request: Request) -> Response: async def _connect(self, request: Request) -> AsyncNetworkStream: timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) timeout = timeouts.get("connect", None) retries_left = self._retries @@ -136,7 +137,8 @@ async def _connect(self, request: Request) -> AsyncNetworkStream: kwargs = { "ssl_context": ssl_context, - "server_hostname": self._origin.host.decode("ascii"), + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), "timeout": timeout, } async with Trace("start_tls", logger, request, kwargs) as trace: diff --git a/httpcore/_sync/connection.py b/httpcore/_sync/connection.py index ae8c6e2c..2068a040 100644 --- a/httpcore/_sync/connection.py +++ b/httpcore/_sync/connection.py @@ -95,6 +95,7 @@ def handle_request(self, request: Request) -> Response: def _connect(self, request: Request) -> NetworkStream: timeouts = request.extensions.get("timeout", {}) + sni_hostname = request.extensions.get("sni_hostname", None) timeout = timeouts.get("connect", None) retries_left = self._retries @@ -136,7 +137,8 @@ def _connect(self, request: Request) -> NetworkStream: kwargs = { "ssl_context": ssl_context, - "server_hostname": self._origin.host.decode("ascii"), + "server_hostname": sni_hostname + or self._origin.host.decode("ascii"), "timeout": timeout, } with Trace("start_tls", logger, request, kwargs) as trace: