| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 426b5b4 commit 2c03e74
5 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -778,7 +778,7 @@ Creating network servers | |||
| 778 | 778 | *, sock=None, backlog=100, ssl=None, \ | |
| 779 | 779 | ssl_handshake_timeout=None, \ | |
| 780 | 780 | ssl_shutdown_timeout=None, \ | |
| 781 | - start_serving=True) | ||
| 781 | + start_serving=True, cleanup_socket=True) | ||
| 782 | 782 | ||
| 783 | 783 | Similar to :meth:`loop.create_server` but works with the | |
| 784 | 784 | :py:const:`~socket.AF_UNIX` socket family. | |
@@ -788,6 +788,10 @@ Creating network servers | |||
| 788 | 788 | :class:`str`, :class:`bytes`, and :class:`~pathlib.Path` paths | |
| 789 | 789 | are supported. | |
| 790 | 790 | ||
| 791 | + If *cleanup_socket* is True then the Unix socket will automatically | ||
| 792 | + be removed from the filesystem when the server is closed, unless the | ||
| 793 | + socket has been replaced after the server has been created. | ||
| 794 | + | ||
| 791 | 795 | See the documentation of the :meth:`loop.create_server` method | |
| 792 | 796 | for information about arguments to this method. | |
| 793 | 797 | ||
@@ -802,6 +806,10 @@ Creating network servers | |||
| 802 | 806 | ||
| 803 | 807 | Added the *ssl_shutdown_timeout* parameter. | |
| 804 | 808 | ||
| 809 | + .. versionchanged:: 3.13 | ||
| 810 | + | ||
| 811 | + Added the *cleanup_socket* parameter. | ||
| 812 | + | ||
| 805 | 813 | ||
| 806 | 814 | .. coroutinemethod:: loop.connect_accepted_socket(protocol_factory, \ | |
| 807 | 815 | sock, *, ssl=None, ssl_handshake_timeout=None, \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -149,6 +149,13 @@ array | |||
| 149 | 149 | It can be used instead of ``'u'`` type code, which is deprecated. | |
| 150 | 150 | (Contributed by Inada Naoki in :gh:`80480`.) | |
| 151 | 151 | ||
| 152 | + asyncio | ||
| 153 | + ------- | ||
| 154 | + | ||
| 155 | + * :meth:`asyncio.loop.create_unix_server` will now automatically remove | ||
| 156 | + the Unix socket when the server is closed. | ||
| 157 | + (Contributed by Pierre Ossman in :gh:`111246`.) | ||
| 158 | + | ||
| 152 | 159 | copy | |
| 153 | 160 | ---- | |
| 154 | 161 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -64,6 +64,7 @@ class _UnixSelectorEventLoop(selector_events.BaseSelectorEventLoop): | |||
| 64 | 64 | def __init__(self, selector=None): | |
| 65 | 65 | super().__init__(selector) | |
| 66 | 66 | self._signal_handlers = {} | |
| 67 | + self._unix_server_sockets = {} | ||
| 67 | 68 | ||
| 68 | 69 | def close(self): | |
| 69 | 70 | super().close() | |
@@ -284,7 +285,7 @@ async def create_unix_server( | |||
| 284 | 285 | sock=None, backlog=100, ssl=None, | |
| 285 | 286 | ssl_handshake_timeout=None, | |
| 286 | 287 | ssl_shutdown_timeout=None, | |
| 287 | - start_serving=True): | ||
| 288 | + start_serving=True, cleanup_socket=True): | ||
| 288 | 289 | if isinstance(ssl, bool): | |
| 289 | 290 | raise TypeError('ssl argument must be an SSLContext or None') | |
| 290 | 291 | ||
@@ -340,6 +341,15 @@ async def create_unix_server( | |||
| 340 | 341 | raise ValueError( | |
| 341 | 342 | f'A UNIX Domain Stream Socket was expected, got {sock!r}') | |
| 342 | 343 | ||
| 344 | + if cleanup_socket: | ||
| 345 | + path = sock.getsockname() | ||
| 346 | + # Check for abstract socket. `str` and `bytes` paths are supported. | ||
| 347 | + if path[0] not in (0, '\x00'): | ||
| 348 | + try: | ||
| 349 | + self._unix_server_sockets[sock] = os.stat(path).st_ino | ||
| 350 | + except FileNotFoundError: | ||
| 351 | + pass | ||
| 352 | + | ||
| 343 | 353 | sock.setblocking(False) | |
| 344 | 354 | server = base_events.Server(self, [sock], protocol_factory, | |
| 345 | 355 | ssl, backlog, ssl_handshake_timeout, | |
@@ -460,6 +470,27 @@ def cb(fut): | |||
| 460 | 470 | self.remove_writer(fd) | |
| 461 | 471 | fut.add_done_callback(cb) | |
| 462 | 472 | ||
| 473 | + def _stop_serving(self, sock): | ||
| 474 | + # Is this a unix socket that needs cleanup? | ||
| 475 | + if sock in self._unix_server_sockets: | ||
| 476 | + path = sock.getsockname() | ||
| 477 | + else: | ||
| 478 | + path = None | ||
| 479 | + | ||
| 480 | + super()._stop_serving(sock) | ||
| 481 | + | ||
| 482 | + if path is not None: | ||
| 483 | + prev_ino = self._unix_server_sockets[sock] | ||
| 484 | + del self._unix_server_sockets[sock] | ||
| 485 | + try: | ||
| 486 | + if os.stat(path).st_ino == prev_ino: | ||
| 487 | + os.unlink(path) | ||
| 488 | + except FileNotFoundError: | ||
| 489 | + pass | ||
| 490 | + except OSError as err: | ||
| 491 | + logger.error('Unable to clean up listening UNIX socket ' | ||
| 492 | + '%r: %r', path, err) | ||
| 493 | + | ||
| 463 | 494 | ||
| 464 | 495 | class _UnixReadPipeTransport(transports.ReadTransport): | |
| 465 | 496 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,4 +1,6 @@ | |||
| 1 | 1 | import asyncio | |
| 2 | + import os | ||
| 3 | + import socket | ||
| 2 | 4 | import time | |
| 3 | 5 | import threading | |
| 4 | 6 | import unittest | |
@@ -177,6 +179,80 @@ async def serve(*args): | |||
| 177 | 179 | ||
| 178 | 180 | ||
| 179 | 181 | ||
| 182 | + # Test the various corner cases of Unix server socket removal | ||
| 183 | + class UnixServerCleanupTests(unittest.IsolatedAsyncioTestCase): | ||
| 184 | + @socket_helper.skip_unless_bind_unix_socket | ||
| 185 | + async def test_unix_server_addr_cleanup(self): | ||
| 186 | + # Default scenario | ||
| 187 | + with test_utils.unix_socket_path() as addr: | ||
| 188 | + async def serve(*args): | ||
| 189 | + pass | ||
| 190 | + | ||
| 191 | + srv = await asyncio.start_unix_server(serve, addr) | ||
| 192 | + | ||
| 193 | + srv.close() | ||
| 194 | + self.assertFalse(os.path.exists(addr)) | ||
| 195 | + | ||
| 196 | + @socket_helper.skip_unless_bind_unix_socket | ||
| 197 | + async def test_unix_server_sock_cleanup(self): | ||
| 198 | + # Using already bound socket | ||
| 199 | + with test_utils.unix_socket_path() as addr: | ||
| 200 | + async def serve(*args): | ||
| 201 | + pass | ||
| 202 | + | ||
| 203 | + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 204 | + sock.bind(addr) | ||
| 205 | + | ||
| 206 | + srv = await asyncio.start_unix_server(serve, sock=sock) | ||
| 207 | + | ||
| 208 | + srv.close() | ||
| 209 | + self.assertFalse(os.path.exists(addr)) | ||
| 210 | + | ||
| 211 | + @socket_helper.skip_unless_bind_unix_socket | ||
| 212 | + async def test_unix_server_cleanup_gone(self): | ||
| 213 | + # Someone else has already cleaned up the socket | ||
| 214 | + with test_utils.unix_socket_path() as addr: | ||
| 215 | + async def serve(*args): | ||
| 216 | + pass | ||
| 217 | + | ||
| 218 | + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 219 | + sock.bind(addr) | ||
| 220 | + | ||
| 221 | + srv = await asyncio.start_unix_server(serve, sock=sock) | ||
| 222 | + | ||
| 223 | + os.unlink(addr) | ||
| 224 | + | ||
| 225 | + srv.close() | ||
| 226 | + | ||
| 227 | + @socket_helper.skip_unless_bind_unix_socket | ||
| 228 | + async def test_unix_server_cleanup_replaced(self): | ||
| 229 | + # Someone else has replaced the socket with their own | ||
| 230 | + with test_utils.unix_socket_path() as addr: | ||
| 231 | + async def serve(*args): | ||
| 232 | + pass | ||
| 233 | + | ||
| 234 | + srv = await asyncio.start_unix_server(serve, addr) | ||
| 235 | + | ||
| 236 | + os.unlink(addr) | ||
| 237 | + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) | ||
| 238 | + sock.bind(addr) | ||
| 239 | + | ||
| 240 | + srv.close() | ||
| 241 | + self.assertTrue(os.path.exists(addr)) | ||
| 242 | + | ||
| 243 | + @socket_helper.skip_unless_bind_unix_socket | ||
| 244 | + async def test_unix_server_cleanup_prevented(self): | ||
| 245 | + # Automatic cleanup explicitly disabled | ||
| 246 | + with test_utils.unix_socket_path() as addr: | ||
| 247 | + async def serve(*args): | ||
| 248 | + pass | ||
| 249 | + | ||
| 250 | + srv = await asyncio.start_unix_server(serve, addr, cleanup_socket=False) | ||
| 251 | + | ||
| 252 | + srv.close() | ||
| 253 | + self.assertTrue(os.path.exists(addr)) | ||
| 254 | + | ||
| 255 | + | ||
| 180 | 256 | @unittest.skipUnless(hasattr(asyncio, 'ProactorEventLoop'), 'Windows only') | |
| 181 | 257 | class ProactorStartServerTests(BaseStartServer, unittest.TestCase): | |
| 182 | 258 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1,2 @@ | |||
| 1 | + :meth:`asyncio.loop.create_unix_server` will now automatically remove the | ||
| 2 | + Unix socket when the server is closed. | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments