Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 252e9ed

Browse files
committed
Issue #27392: Add loop.connect_accepted_socket().
Patch by Jim Fulton.
1 parent 6f379f4 commit 252e9ed

3 files changed

Lines changed: 106 additions & 4 deletions

File tree

Lib/asyncio/base_events.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -707,8 +707,6 @@ def create_connection(self, protocol_factory, host=None, port=None, *,
707707
raise ValueError(
708708
'host and port was not specified and no sock specified')
709709

710-
sock.setblocking(False)
711-
712710
transport, protocol = yield from self._create_connection_transport(
713711
sock, protocol_factory, ssl, server_hostname)
714712
if self._debug:
@@ -721,14 +719,17 @@ def create_connection(self, protocol_factory, host=None, port=None, *,
721719

722720
@coroutine
723721
def _create_connection_transport(self, sock, protocol_factory, ssl,
724-
server_hostname):
722+
server_hostname, server_side=False):
723+
724+
sock.setblocking(False)
725+
725726
protocol = protocol_factory()
726727
waiter = self.create_future()
727728
if ssl:
728729
sslcontext = None if isinstance(ssl, bool) else ssl
729730
transport = self._make_ssl_transport(
730731
sock, protocol, sslcontext, waiter,
731-
server_side=False, server_hostname=server_hostname)
732+
server_side=server_side, server_hostname=server_hostname)
732733
else:
733734
transport = self._make_socket_transport(sock, protocol, waiter)
734735

@@ -979,6 +980,25 @@ def create_server(self, protocol_factory, host=None, port=None,
979980
logger.info("%r is serving", server)
980981
return server
981982

983+
@coroutine
984+
def connect_accepted_socket(self, protocol_factory, sock, *, ssl=None):
985+
"""Handle an accepted connection.
986+
987+
This is used by servers that accept connections outside of
988+
asyncio but that use asyncio to handle connections.
989+
990+
This method is a coroutine. When completed, the coroutine
991+
returns a (transport, protocol) pair.
992+
"""
993+
transport, protocol = yield from self._create_connection_transport(
994+
sock, protocol_factory, ssl, '', server_side=True)
995+
if self._debug:
996+
# Get the socket from the transport because SSL transport closes
997+
# the old socket and creates a new SSL socket
998+
sock = transport.get_extra_info('socket')
999+
logger.debug("%r handled: (%r, %r)", sock, transport, protocol)
1000+
return transport, protocol
1001+
9821002
@coroutine
9831003
def connect_read_pipe(self, protocol_factory, pipe):
9841004
protocol = protocol_factory()

Lib/test/test_asyncio/test_events.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,85 @@ def test_create_connection_local_addr_in_use(self):
744744
self.assertEqual(cm.exception.errno, errno.EADDRINUSE)
745745
self.assertIn(str(httpd.address), cm.exception.strerror)
746746

747+
def test_connect_accepted_socket(self, server_ssl=None, client_ssl=None):
748+
loop = self.loop
749+
750+
class MyProto(MyBaseProto):
751+
752+
def connection_lost(self, exc):
753+
super().connection_lost(exc)
754+
loop.call_soon(loop.stop)
755+
756+
def data_received(self, data):
757+
super().data_received(data)
758+
self.transport.write(expected_response)
759+
760+
lsock = socket.socket()
761+
lsock.bind(('127.0.0.1', 0))
762+
lsock.listen(1)
763+
addr = lsock.getsockname()
764+
765+
message = b'test data'
766+
reponse = None
767+
expected_response = b'roger'
768+
769+
def client():
770+
global response
771+
try:
772+
csock = socket.socket()
773+
if client_ssl is not None:
774+
csock = client_ssl.wrap_socket(csock)
775+
csock.connect(addr)
776+
csock.sendall(message)
777+
response = csock.recv(99)
778+
csock.close()
779+
except Exception as exc:
780+
print(
781+
"Failure in client thread in test_connect_accepted_socket",
782+
exc)
783+
784+
thread = threading.Thread(target=client, daemon=True)
785+
thread.start()
786+
787+
conn, _ = lsock.accept()
788+
proto = MyProto(loop=loop)
789+
proto.loop = loop
790+
f = loop.create_task(
791+
loop.connect_accepted_socket(
792+
(lambda : proto), conn, ssl=server_ssl))
793+
loop.run_forever()
794+
conn.close()
795+
lsock.close()
796+
797+
thread.join(1)
798+
self.assertFalse(thread.is_alive())
799+
self.assertEqual(proto.state, 'CLOSED')
800+
self.assertEqual(proto.nbytes, len(message))
801+
self.assertEqual(response, expected_response)
802+
803+
@unittest.skipIf(ssl is None, 'No ssl module')
804+
def test_ssl_connect_accepted_socket(self):
805+
if (sys.platform == 'win32' and
806+
sys.version_info < (3, 5) and
807+
isinstance(self.loop, proactor_events.BaseProactorEventLoop)
808+
):
809+
raise unittest.SkipTest(
810+
'SSL not supported with proactor event loops before Python 3.5'
811+
)
812+
813+
server_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
814+
server_context.load_cert_chain(ONLYCERT, ONLYKEY)
815+
if hasattr(server_context, 'check_hostname'):
816+
server_context.check_hostname = False
817+
server_context.verify_mode = ssl.CERT_NONE
818+
819+
client_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
820+
if hasattr(server_context, 'check_hostname'):
821+
client_context.check_hostname = False
822+
client_context.verify_mode = ssl.CERT_NONE
823+
824+
self.test_connect_accepted_socket(server_context, client_context)
825+
747826
@mock.patch('asyncio.base_events.socket')
748827
def create_server_multiple_hosts(self, family, hosts, mock_sock):
749828
@asyncio.coroutine

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ Library
7272

7373
- Issue #26930: Update Windows builds to use OpenSSL 1.0.2h.
7474

75+
- Issue #27392: Add loop.connect_accepted_socket().
76+
Patch by Jim Fulton.
77+
7578
IDLE
7679
----
7780

0 commit comments

Comments
 (0)