Skip to content

Commit 35ddb37

Browse files
committed
Add test coverage for proxy TLS handshake failure
1 parent 00b33c3 commit 35ddb37

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

tests/_async/test_http_proxy.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ async def test_proxy_tunneling_with_403():
224224
"""
225225
network_backend = AsyncMockBackend(
226226
[
227-
b"HTTP/1.1 403 Permission Denied\r\n" b"\r\n",
227+
b"HTTP/1.1 403 Permission Denied\r\n\r\n",
228228
]
229229
)
230230

@@ -276,3 +276,45 @@ def test_proxy_headers():
276276
assert proxy.headers == [
277277
(b"Proxy-Authorization", b"Basic dXNlcm5hbWU6cGFzc3dvcmQ=")
278278
]
279+
280+
281+
@pytest.mark.anyio
282+
async def test_proxy_tunneling_tls_error():
283+
"""
284+
Send an HTTPS request via a proxy, but the TLS handshake fails.
285+
"""
286+
287+
class BrokenTLSStream(AsyncMockStream):
288+
async def start_tls(
289+
self,
290+
ssl_context: ssl.SSLContext,
291+
server_hostname: typing.Optional[str] = None,
292+
timeout: typing.Optional[float] = None,
293+
) -> AsyncNetworkStream:
294+
raise OSError("TLS Failure")
295+
296+
class BrokenTLSBackend(AsyncMockBackend):
297+
async def connect_tcp(
298+
self,
299+
host: str,
300+
port: int,
301+
timeout: typing.Optional[float] = None,
302+
local_address: typing.Optional[str] = None,
303+
socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None,
304+
) -> AsyncNetworkStream:
305+
return BrokenTLSStream(list(self._buffer))
306+
307+
network_backend = BrokenTLSBackend(
308+
[
309+
b"HTTP/1.1 200 OK\r\n\r\n",
310+
]
311+
)
312+
313+
async with AsyncConnectionPool(
314+
proxy=Proxy("http://localhost:8080/"),
315+
network_backend=network_backend,
316+
) as proxy:
317+
with pytest.raises(OSError, match="TLS Failure"):
318+
await proxy.request("GET", "https://example.com/")
319+
320+
assert not proxy.connections

tests/_sync/test_http_proxy.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def test_proxy_tunneling_with_403():
224224
"""
225225
network_backend = MockBackend(
226226
[
227-
b"HTTP/1.1 403 Permission Denied\r\n" b"\r\n",
227+
b"HTTP/1.1 403 Permission Denied\r\n\r\n",
228228
]
229229
)
230230

@@ -276,3 +276,45 @@ def test_proxy_headers():
276276
assert proxy.headers == [
277277
(b"Proxy-Authorization", b"Basic dXNlcm5hbWU6cGFzc3dvcmQ=")
278278
]
279+
280+
281+
282+
def test_proxy_tunneling_tls_error():
283+
"""
284+
Send an HTTPS request via a proxy, but the TLS handshake fails.
285+
"""
286+
287+
class BrokenTLSStream(MockStream):
288+
def start_tls(
289+
self,
290+
ssl_context: ssl.SSLContext,
291+
server_hostname: typing.Optional[str] = None,
292+
timeout: typing.Optional[float] = None,
293+
) -> NetworkStream:
294+
raise OSError("TLS Failure")
295+
296+
class BrokenTLSBackend(MockBackend):
297+
def connect_tcp(
298+
self,
299+
host: str,
300+
port: int,
301+
timeout: typing.Optional[float] = None,
302+
local_address: typing.Optional[str] = None,
303+
socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None,
304+
) -> NetworkStream:
305+
return BrokenTLSStream(list(self._buffer))
306+
307+
network_backend = BrokenTLSBackend(
308+
[
309+
b"HTTP/1.1 200 OK\r\n\r\n",
310+
]
311+
)
312+
313+
with ConnectionPool(
314+
proxy=Proxy("http://localhost:8080/"),
315+
network_backend=network_backend,
316+
) as proxy:
317+
with pytest.raises(OSError, match="TLS Failure"):
318+
proxy.request("GET", "https://example.com/")
319+
320+
assert not proxy.connections

0 commit comments

Comments
 (0)