Skip to content

Commit ced645a

Browse files
committed
Don't cleanup UNIX server sockets paths when server is closing
Fixes issue #28.
1 parent dc0d2d1 commit ced645a

File tree

2 files changed

+50
-11
lines changed

2 files changed

+50
-11
lines changed

tests/test_unix.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
class _TestUnix:
10-
def test_create_server_1(self):
10+
def test_create_unix_server_1(self):
1111
CNT = 0 # number of clients that were successful
1212
TOTAL_CNT = 100 # total number of clients that test will create
1313
TIMEOUT = 5.0 # timeout for this test
@@ -72,12 +72,16 @@ async def start_server():
7272
TIMEOUT, loop=self.loop)
7373

7474
finally:
75-
srv.close()
75+
self.loop.call_soon(srv.close)
76+
await srv.wait_closed()
7677

7778
# Check that the server cleaned-up proxy-sockets
7879
for srv_sock in srv_socks:
7980
self.assertEqual(srv_sock.fileno(), -1)
8081

82+
# asyncio doesn't cleanup the sock file
83+
self.assertTrue(os.path.exists(sock_name))
84+
8185
async def start_server_sock():
8286
nonlocal CNT
8387
CNT = 0
@@ -106,18 +110,35 @@ async def start_server_sock():
106110
TIMEOUT, loop=self.loop)
107111

108112
finally:
109-
srv.close()
113+
self.loop.call_soon(srv.close)
114+
await srv.wait_closed()
110115

111116
# Check that the server cleaned-up proxy-sockets
112117
for srv_sock in srv_socks:
113118
self.assertEqual(srv_sock.fileno(), -1)
114119

120+
# asyncio doesn't cleanup the sock file
121+
self.assertTrue(os.path.exists(sock_name))
122+
115123
self.loop.run_until_complete(start_server())
116124
self.assertEqual(CNT, TOTAL_CNT)
117125

118126
self.loop.run_until_complete(start_server_sock())
119127
self.assertEqual(CNT, TOTAL_CNT)
120128

129+
def test_create_unix_server_2(self):
130+
with tempfile.TemporaryDirectory() as td:
131+
sock_name = os.path.join(td, 'sock')
132+
with open(sock_name, 'wt') as f:
133+
f.write('x')
134+
135+
with self.assertRaisesRegex(
136+
OSError, "Address '{}' is already in use".format(
137+
sock_name)):
138+
139+
self.loop.run_until_complete(
140+
self.loop.create_unix_server(object, sock_name))
141+
121142
def test_create_unix_connection_1(self):
122143
CNT = 0
123144
TOTAL_CNT = 100

uvloop/loop.pyx

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,10 +1441,27 @@ cdef class Loop:
14411441
raise ValueError(
14421442
'path and sock can not be specified at the same time')
14431443

1444+
# We use Python sockets to create a UNIX server socket because
1445+
# when UNIX sockets are created by libuv, libuv removes the path
1446+
# they were bound to. This is different from asyncio, which
1447+
# doesn't cleanup the socket path.
1448+
sock = socket_socket(uv.AF_UNIX)
1449+
14441450
try:
1445-
pipe.bind(path)
1451+
sock.bind(path)
1452+
except OSError as exc:
1453+
pipe._close()
1454+
sock.close()
1455+
if exc.errno == errno.EADDRINUSE:
1456+
# Let's improve the error message by adding
1457+
# with what exact address it occurs.
1458+
msg = 'Address {!r} is already in use'.format(path)
1459+
raise OSError(errno.EADDRINUSE, msg) from None
1460+
else:
1461+
raise
14461462
except:
14471463
pipe._close()
1464+
sock.close()
14481465
raise
14491466

14501467
else:
@@ -1456,14 +1473,15 @@ cdef class Loop:
14561473
raise ValueError(
14571474
'A UNIX Domain Socket was expected, got {!r}'.format(sock))
14581475

1459-
try:
1460-
fileno = os_dup(sock.fileno())
1461-
pipe.open(sock.fileno())
1462-
except:
1463-
pipe._close()
1464-
raise
1476+
try:
1477+
fileno = os_dup(sock.fileno())
1478+
pipe.open(fileno)
1479+
except:
1480+
pipe._close()
1481+
sock.close()
1482+
raise
14651483

1466-
pipe._attach_fileobj(sock)
1484+
pipe._attach_fileobj(sock)
14671485

14681486
try:
14691487
pipe.listen(backlog)

0 commit comments

Comments
 (0)