Skip to content

Commit f20bf82

Browse files
committed
Allow create_connection & create_server accept AF_UNIX
Internally, they will delegate to create_unix_connection and create_unix_server.
1 parent 7474d96 commit f20bf82

File tree

3 files changed

+73
-26
lines changed

3 files changed

+73
-26
lines changed

tests/test_tcp.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -808,20 +808,20 @@ def client():
808808

809809
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'no Unix sockets')
810810
def test_create_connection_wrong_sock(self):
811-
sock = socket.socket(socket.AF_UNIX)
811+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
812812
with sock:
813813
coro = self.loop.create_connection(MyBaseProto, sock=sock)
814814
with self.assertRaisesRegex(ValueError,
815-
'A TCP Stream Socket was expected'):
815+
'A Stream Socket was expected'):
816816
self.loop.run_until_complete(coro)
817817

818818
@unittest.skipUnless(hasattr(socket, 'AF_UNIX'), 'no Unix sockets')
819819
def test_create_server_wrong_sock(self):
820-
sock = socket.socket(socket.AF_UNIX)
820+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
821821
with sock:
822822
coro = self.loop.create_server(MyBaseProto, sock=sock)
823823
with self.assertRaisesRegex(ValueError,
824-
'A TCP Stream Socket was expected'):
824+
'A Stream Socket was expected'):
825825
self.loop.run_until_complete(coro)
826826

827827
@unittest.skipUnless(hasattr(socket, 'SOCK_NONBLOCK'),

tests/test_unix.py

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ async def start_server():
8383
# asyncio doesn't cleanup the sock file
8484
self.assertTrue(os.path.exists(sock_name))
8585

86-
async def start_server_sock():
86+
async def start_server_sock(start_server):
8787
nonlocal CNT
8888
CNT = 0
8989

@@ -92,11 +92,7 @@ async def start_server_sock():
9292
sock = socket.socket(socket.AF_UNIX)
9393
sock.bind(sock_name)
9494

95-
srv = await asyncio.start_unix_server(
96-
handle_client,
97-
None,
98-
loop=self.loop,
99-
sock=sock)
95+
srv = await start_server(sock)
10096

10197
try:
10298
srv_socks = srv.sockets
@@ -121,11 +117,27 @@ async def start_server_sock():
121117
# asyncio doesn't cleanup the sock file
122118
self.assertTrue(os.path.exists(sock_name))
123119

124-
self.loop.run_until_complete(start_server())
125-
self.assertEqual(CNT, TOTAL_CNT)
120+
with self.subTest(func='start_unix_server(host, port)'):
121+
self.loop.run_until_complete(start_server())
122+
self.assertEqual(CNT, TOTAL_CNT)
126123

127-
self.loop.run_until_complete(start_server_sock())
128-
self.assertEqual(CNT, TOTAL_CNT)
124+
with self.subTest(func='start_unix_server(sock)'):
125+
self.loop.run_until_complete(start_server_sock(
126+
lambda sock: asyncio.start_unix_server(
127+
handle_client,
128+
None,
129+
loop=self.loop,
130+
sock=sock)))
131+
self.assertEqual(CNT, TOTAL_CNT)
132+
133+
with self.subTest(func='start_server(sock)'):
134+
self.loop.run_until_complete(start_server_sock(
135+
lambda sock: asyncio.start_server(
136+
handle_client,
137+
None, None,
138+
loop=self.loop,
139+
sock=sock)))
140+
self.assertEqual(CNT, TOTAL_CNT)
129141

130142
def test_create_unix_server_2(self):
131143
with tempfile.TemporaryDirectory() as td:
@@ -187,6 +199,24 @@ async def client_2(addr):
187199

188200
writer.close()
189201

202+
async def client_3(addr):
203+
sock = socket.socket(socket.AF_UNIX)
204+
sock.connect(addr)
205+
reader, writer = await asyncio.open_connection(
206+
sock=sock,
207+
loop=self.loop)
208+
209+
writer.write(b'AAAA')
210+
self.assertEqual(await reader.readexactly(2), b'OK')
211+
212+
writer.write(b'BBBB')
213+
self.assertEqual(await reader.readexactly(4), b'SPAM')
214+
215+
nonlocal CNT
216+
CNT += 1
217+
218+
writer.close()
219+
190220
def run(coro):
191221
nonlocal CNT
192222
CNT = 0
@@ -208,6 +238,7 @@ def run(coro):
208238

209239
run(client)
210240
run(client_2)
241+
run(client_3)
211242

212243
def test_create_unix_connection_2(self):
213244
with tempfile.NamedTemporaryFile() as tmp:

uvloop/loop.pyx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# cython: language_level=3, embedsignature=True
22

3-
3+
import asyncio
44
cimport cython
55

66
from .includes.debug cimport UVLOOP_DEBUG
@@ -38,10 +38,6 @@ include "includes/stdlib.pxi"
3838
include "errors.pyx"
3939

4040

41-
cdef _is_sock_ip(sock_family):
42-
return sock_family == uv.AF_INET or sock_family == uv.AF_INET6
43-
44-
4541
cdef _is_sock_stream(sock_type):
4642
# Linux's socket.type is a bitmask that can include extra info
4743
# about socket, therefore we can't do simple
@@ -1298,7 +1294,16 @@ cdef class Loop:
12981294
cdef:
12991295
TCPServer tcp
13001296
system.addrinfo *addrinfo
1301-
Server server = Server(self)
1297+
Server server
1298+
1299+
if sock is not None and sock.family == uv.AF_UNIX:
1300+
if host is not None or port is not None:
1301+
raise ValueError(
1302+
'host/port and sock can not be specified at the same time')
1303+
return await self.create_unix_server(
1304+
protocol_factory, sock=sock, ssl=ssl)
1305+
1306+
server = Server(self)
13021307

13031308
if ssl is not None and not isinstance(ssl, ssl_SSLContext):
13041309
raise TypeError('ssl argument must be an SSLContext or None')
@@ -1349,10 +1354,10 @@ cdef class Loop:
13491354
else:
13501355
if sock is None:
13511356
raise ValueError('Neither host/port nor sock were specified')
1352-
if (not _is_sock_stream(sock.type) or
1353-
not _is_sock_ip(sock.family)):
1357+
if not _is_sock_stream(sock.type):
13541358
raise ValueError(
1355-
'A TCP Stream Socket was expected, got {!r}'.format(sock))
1359+
'A Stream Socket was expected, got {!r}'.format(sock))
1360+
13561361
tcp = TCPServer.new(self, protocol_factory, server, ssl,
13571362
uv.AF_UNSPEC)
13581363

@@ -1404,6 +1409,14 @@ cdef class Loop:
14041409
object protocol
14051410
object ssl_waiter
14061411

1412+
if sock is not None and sock.family == uv.AF_UNIX:
1413+
if host is not None or port is not None:
1414+
raise ValueError(
1415+
'host/port and sock can not be specified at the same time')
1416+
return await self.create_unix_connection(
1417+
protocol_factory, None,
1418+
sock=sock, ssl=ssl, server_hostname=server_hostname)
1419+
14071420
app_protocol = protocol = protocol_factory()
14081421
ssl_waiter = None
14091422
if ssl:
@@ -1423,6 +1436,10 @@ cdef class Loop:
14231436
raise ValueError('server_hostname is only meaningful with ssl')
14241437

14251438
if host is not None or port is not None:
1439+
if sock is not None:
1440+
raise ValueError(
1441+
'host/port and sock can not be specified at the same time')
1442+
14261443
fs = []
14271444
f1 = f2 = None
14281445

@@ -1536,10 +1553,9 @@ cdef class Loop:
15361553
if sock is None:
15371554
raise ValueError(
15381555
'host and port was not specified and no sock specified')
1539-
if (not _is_sock_stream(sock.type) or
1540-
not _is_sock_ip(sock.family)):
1556+
if not _is_sock_stream(sock.type):
15411557
raise ValueError(
1542-
'A TCP Stream Socket was expected, got {!r}'.format(sock))
1558+
'A Stream Socket was expected, got {!r}'.format(sock))
15431559

15441560
waiter = self._new_future()
15451561
tr = TCPTransport.new(self, protocol, None, waiter)

0 commit comments

Comments
 (0)