FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

gh-156400: Close the socket or pipe when transport creation fails in asyncio datagram/pipe endpoints by tonghuaroot · Pull Request #156401 · python/cpython · GitHub

/ cpython Public
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (2) .rst  (1) All 2 file types selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
35 changes: 25 additions & 10 deletions Lib/asyncio/base_events.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -1497,10 +1497,15 @@ async def create_datagram_endpoint(self, protocol_factory,
else:
raise exceptions[0]

protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_datagram_transport(
sock, protocol, r_addr, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_datagram_transport(
sock, protocol, r_addr, waiter)
except:
# gh-156400: close the socket if the transport is never created.
sock.close()
raise
if self._debug:
if local_addr:
logger.info("Datagram endpoint local_addr=%r remote_addr=%r "
Expand Down Expand Up @@ -1714,9 +1719,14 @@ async def connect_accepted_socket(
return transport, protocol

async def connect_read_pipe(self, protocol_factory, pipe):
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_read_pipe_transport(pipe, protocol, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_read_pipe_transport(pipe, protocol, waiter)
except:
# gh-156400: close the pipe if the transport is never created.
pipe.close()
raise

try:
await waiter
Expand All @@ -1730,9 +1740,14 @@ async def connect_read_pipe(self, protocol_factory, pipe):
return transport, protocol

async def connect_write_pipe(self, protocol_factory, pipe):
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_write_pipe_transport(pipe, protocol, waiter)
try:
protocol = protocol_factory()
waiter = self.create_future()
transport = self._make_write_pipe_transport(pipe, protocol, waiter)
except:
# gh-156400: close the pipe if the transport is never created.
pipe.close()
raise

try:
await waiter
Expand Down
37 changes: 37 additions & 0 deletions Lib/test/test_asyncio/test_base_events.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,43 @@ def test_create_datagram_endpoint_sock(self):
self.loop.run_until_complete(protocol.done)
self.assertEqual('CLOSED', protocol.state)

def test_create_datagram_endpoint_transport_error_closes_sock(self):
# gh-156400: the socket is closed if the transport is never created.
sock = mock.Mock()
sock.type = socket.SOCK_DGRAM

def factory():
raise ZeroDivisionError

coro = self.loop.create_datagram_endpoint(factory, sock=sock)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(sock.close.called)

def test_connect_read_pipe_transport_error_closes_pipe(self):
# gh-156400: the pipe is closed if the transport is never created.
pipe = mock.Mock()

def factory():
raise ZeroDivisionError

coro = self.loop.connect_read_pipe(factory, pipe)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(pipe.close.called)

def test_connect_write_pipe_transport_error_closes_pipe(self):
# gh-156400: the pipe is closed if the transport is never created.
pipe = mock.Mock()

def factory():
raise ZeroDivisionError

coro = self.loop.connect_write_pipe(factory, pipe)
with self.assertRaises(ZeroDivisionError):
self.loop.run_until_complete(coro)
self.assertTrue(pipe.close.called)

@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'No UNIX Sockets')
def test_create_datagram_endpoint_sock_unix(self):
fut = self.loop.create_datagram_endpoint(
Expand Down
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Fix socket and pipe leaks in :mod:`asyncio` when ``protocol_factory()`` or
transport creation fails in :meth:`loop.create_datagram_endpoint
<asyncio.loop.create_datagram_endpoint>`, :meth:`loop.connect_read_pipe
<asyncio.loop.connect_read_pipe>`, and :meth:`loop.connect_write_pipe
<asyncio.loop.connect_write_pipe>`. The socket or pipe is now closed instead
of leaking until garbage collection.
Loading

Back | FazBrowse Home | New Git URL