10000 bpo-23749: Implement loop.start_tls() (#5039) · python/cpython@f111b3d · GitHub
[go: up one dir, main page]

Skip to content

Commit f111b3d

Browse files
authored
bpo-23749: Implement loop.start_tls() (#5039)
1 parent bbdb17d commit f111b3d

File tree

10 files changed

+580
-54
lines changed

10 files changed

+580
-54
lines changed

Doc/library/asyncio-eventloop.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,38 @@ Creating listening connections
537537
.. versionadded:: 3.5.3
538538

539539

540+
TLS Upgrade
541+
-----------
542+
543+
.. coroutinemethod:: AbstractEventLoop.start_tls(transport, protocol, sslcontext, \*, server_side=False, server_hostname=None, ssl_handshake_timeout=None)
544+
545+
Upgrades an existing connection to TLS.
546+
547+
Returns a new transport instance, that the *protocol* must start using
548+
immediately after the *await*. The *transport* instance passed to
549+
the *start_tls* method should never be used again.
550+
551+
Parameters:
552+
553+
* *transport* and *protocol* instances that methods like
554+
:meth:`~AbstractEventLoop.create_server` and
555+
:meth:`~AbstractEventLoop.create_connection` return.
556+
557+
* *sslcontext*: a configured instance of :class:`~ssl.SSLContext`.
558+
559+
* *server_side* pass ``True`` when a server-side connection is being
560+
upgraded (like the one created by :meth:`~AbstractEventLoop.create_server`).
561+
562+
* *server_hostname*: sets or overrides the host name that the target
563+
server's certificate will be matched against.
564+
565+
* *ssl_handshake_timeout* is (for an SSL connection) the time in seconds to
566+
wait for the SSL handshake to complete before aborting the connection.
567+
``10.0`` seconds if ``None`` (default).
568+
569+
.. versionadded:: 3.7
570+
571+
540572
Watch file descriptors
541573
----------------------
542574

Lib/asyncio/base_events.py

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@
2929
import warnings
3030
import weakref
3131

32+
try:
33+
import ssl
34+
except ImportError: # pragma: no cover
35+
ssl = None
36+
3237
from . import coroutines
3338
from . import events
3439
from . import futures
40+
from . import sslproto
3541
from . import tasks
3642
from .log import logger
3743

@@ -279,7 +285,8 @@ def _make_ssl_transport(
279285
self, rawsock, protocol, sslcontext, waiter=None,
280286
*, server_side=False, server_hostname=None,
281287
extra=None, server=None,
282-
ssl_handshake_timeout=None):
288+
ssl_handshake_timeout=None,
289+
call_connection_made=True):
283290
"""Create SSL transport."""
284291
raise NotImplementedError
285292

@@ -795,6 +802,42 @@ async def _create_connection_transport(
795802

796803
return transport, protocol
797804

805+
async def start_tls(self, transport, protocol, sslcontext, *,
806+
server_side=False,
807+
server_hostname=None,
808+
ssl_handshake_timeout=None):
809+
"""Upgrade transport to TLS.
810+
811+
Return a new transport that *protocol* should start using
812+
immediately.
813+
"""
814+
if ssl is None:
815+
raise RuntimeError('Python ssl module is not available')
816+
817+
if not isinstance(sslcontext, ssl.SSLContext):
818+
raise TypeError(
819+
f'sslcontext is expected to be an instance of ssl.SSLContext, '
820+
f'got {sslcontext!r}')
821+
822+
if not getattr(transport, '_start_tls_compatible', False):
823+
raise TypeError(
824+
f'transport {self!r} is not supported by start_tls()')
825+
826+
waiter = self.create_future()
827+
ssl_protocol = sslproto.SSLProtocol(
828+
self, protocol, sslcontext, waiter,
829+
server_side, server_hostname,
830+
ssl_handshake_timeout=ssl_handshake_timeout,
831+
call_connection_made=False)
832+
833+
transport.set_protocol(ssl_protocol)
834+
self.call_soon(ssl_protocol.connection_made, transport)
835+
if not transport.is_reading():
836+
self.call_soon(transport.resume_reading)
837+
838+
await waiter
839+
return ssl_protocol._app_transport
840+
798841
async def create_datagram_endpoint(self, protocol_factory,
799842
local_addr=None, remote_addr=None, *,
800843
family=0, proto=0, flags=0,

Lib/asyncio/events.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,17 @@ async def create_server(
305305
"""
306306
raise NotImplementedError
307307

308+
async def start_tls(self, transport, protocol, sslcontext, *,
309+
server_side=False,
310+
server_hostname=None,
311+
ssl_handshake_timeout=None):
312+
"""Upgrade a transport to TLS.
313+
314+
Return a new transport that *protocol* should start using
315+
immediately.
316+
"""
317+
raise NotImplementedError
318+
308319
async def create_unix_connection(
309320
self, protocol_factory, path=None, *,
310321
ssl=None, sock=None,

Lib/asyncio/proactor_events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,8 @@ class _ProactorBaseWritePipeTransport(_ProactorBasePipeTransport,
223223
transports.WriteTransport):
224224
"""Transport for write pipes."""
225225

226+
_start_tls_compatible = True
227+
226228
def write(self, data):
227229
if not isinstance(data, (bytes, bytearray, memoryview)):
228230
raise TypeError(

Lib/asyncio/selector_events.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -694,6 +694,8 @@ def get_write_buffer_size(self):
694694

695695
class _SelectorSocketTransport(_SelectorTransport):
696696

697+
_start_tls_compatible = True
698+
697699
def __init__(self, loop, sock, protocol, waiter=None,
698700
extra=None, server=None):
699701
super().__init__(loop, sock, protocol, extra, server)

0 commit comments

Comments
 (0)
0