Add support for proxy tunnels for Python 3.6 + asyncio. (#521)

* Backport start_tls() support for Python 3.6

* Remove version check in start_tls test
This commit is contained in:
Seth Michael Larson 2019-11-17 05:50:54 -06:00 committed by Florimond Manca
parent 6045ee242f
commit 331be99cbf
2 changed files with 38 additions and 10 deletions

View File

@ -58,16 +58,49 @@ class SocketStream(BaseSocketStream):
self, hostname: str, ssl_context: ssl.SSLContext, timeout: TimeoutConfig
) -> "SocketStream":
loop = asyncio.get_event_loop()
if not hasattr(loop, "start_tls"): # pragma: no cover
raise NotImplementedError(
"asyncio.AbstractEventLoop.start_tls() is only available in Python 3.7+"
)
stream_reader = asyncio.StreamReader()
protocol = asyncio.StreamReaderProtocol(stream_reader)
transport = self.stream_writer.transport
loop_start_tls = loop.start_tls # type: ignore
if hasattr(loop, "start_tls"):
loop_start_tls = loop.start_tls # type: ignore
else:
async def loop_start_tls(
transport: asyncio.BaseTransport,
protocol: asyncio.BaseProtocol,
sslcontext: ssl.SSLContext = None,
*,
server_side: bool = False,
server_hostname: str = None,
ssl_handshake_timeout: float = None,
) -> asyncio.Transport:
"""Python 3.6 asyncio doesn't have a start_tls() method on the loop
so we use this function in place of the loop's start_tls() method.
Adapted from this comment:
https://github.com/urllib3/urllib3/issues/1323#issuecomment-362494839
"""
import asyncio.sslproto
waiter = loop.create_future()
ssl_protocol = asyncio.sslproto.SSLProtocol(
loop,
protocol,
sslcontext,
waiter,
server_side=False,
server_hostname=server_hostname,
call_connection_made=False,
)
transport.set_protocol(ssl_protocol)
loop.call_soon(ssl_protocol.connection_made, transport)
loop.call_soon(transport.resume_reading) # type: ignore
await waiter
return ssl_protocol._app_transport
transport = await asyncio.wait_for(
loop_start_tls(
transport=transport,

View File

@ -1,5 +1,3 @@
import sys
import pytest
import trio
@ -31,9 +29,6 @@ async def test_start_tls_on_socket_stream(https_server, backend, get_cipher):
See that the concurrency backend can make a connection without TLS then
start TLS on an existing connection.
"""
if isinstance(backend, AsyncioBackend) and sys.version_info < (3, 7):
pytest.xfail(reason="Requires Python 3.7+ for AbstractEventLoop.start_tls()")
ctx = SSLConfig().load_ssl_context_no_verify(HTTPVersionConfig())
timeout = TimeoutConfig(5)