Skip to content

Commit d112833

Browse files
committed
Fix SSL certificate verification failure for hostnames with trailing dots
Python's ssl module does not handle trailing dots in server_hostname, causing CERTIFICATE_VERIFY_FAILED errors for fully qualified domain names (FQDNs) like 'example.com.'. This fix strips the trailing dot from server_hostname at the connection level before passing it to SSL backends, following the same approach used by urllib3. The underlying DNS resolution still uses the original hostname with the trailing dot. Fixes #1063
1 parent 10a6582 commit d112833

6 files changed

Lines changed: 14 additions & 10 deletions

File tree

httpcore/_async/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,9 @@ async def _connect(self, request: Request) -> AsyncNetworkStream:
148148

149149
kwargs = {
150150
"ssl_context": ssl_context,
151-
"server_hostname": sni_hostname
152-
or self._origin.host.decode("ascii"),
151+
"server_hostname": (
152+
sni_hostname or self._origin.host.decode("ascii")
153+
).rstrip("."),
153154
"timeout": timeout,
154155
}
155156
async with Trace("start_tls", logger, request, kwargs) as trace:

httpcore/_async/http_proxy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ async def handle_async_request(self, request: Request) -> Response:
309309

310310
kwargs = {
311311
"ssl_context": ssl_context,
312-
"server_hostname": self._remote_origin.host.decode("ascii"),
312+
"server_hostname": self._remote_origin.host.decode("ascii").rstrip("."),
313313
"timeout": timeout,
314314
}
315315
async with Trace("start_tls", logger, request, kwargs) as trace:

httpcore/_async/socks_proxy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,9 @@ async def handle_async_request(self, request: Request) -> Response:
258258

259259
kwargs = {
260260
"ssl_context": ssl_context,
261-
"server_hostname": sni_hostname
262-
or self._remote_origin.host.decode("ascii"),
261+
"server_hostname": (
262+
sni_hostname or self._remote_origin.host.decode("ascii")
263+
).rstrip("."),
263264
"timeout": timeout,
264265
}
265266
async with Trace("start_tls", logger, request, kwargs) as trace:

httpcore/_sync/connection.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,9 @@ def _connect(self, request: Request) -> NetworkStream:
148148

149149
kwargs = {
150150
"ssl_context": ssl_context,
151-
"server_hostname": sni_hostname
152-
or self._origin.host.decode("ascii"),
151+
"server_hostname": (
152+
sni_hostname or self._origin.host.decode("ascii")
153+
).rstrip("."),
153154
"timeout": timeout,
154155
}
155156
with Trace("start_tls", logger, request, kwargs) as trace:

httpcore/_sync/http_proxy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ def handle_request(self, request: Request) -> Response:
309309

310310
kwargs = {
311311
"ssl_context": ssl_context,
312-
"server_hostname": self._remote_origin.host.decode("ascii"),
312+
"server_hostname": self._remote_origin.host.decode("ascii").rstrip("."),
313313
"timeout": timeout,
314314
}
315315
with Trace("start_tls", logger, request, kwargs) as trace:

httpcore/_sync/socks_proxy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -258,8 +258,9 @@ def handle_request(self, request: Request) -> Response:
258258

259259
kwargs = {
260260
"ssl_context": ssl_context,
261-
"server_hostname": sni_hostname
262-
or self._remote_origin.host.decode("ascii"),
261+
"server_hostname": (
262+
sni_hostname or self._remote_origin.host.decode("ascii")
263+
).rstrip("."),
263264
"timeout": timeout,
264265
}
265266
with Trace("start_tls", logger, request, kwargs) as trace:

0 commit comments

Comments
 (0)