Skip to content

Commit dd4cb7a

Browse files
1st1mosquito
andcommitted
Handle unix sockets in UDP
Fixes #269. Co-authored-by: Dmitry Orlov <[email protected]>
1 parent 0814a4f commit dd4cb7a

File tree

4 files changed

+94
-7
lines changed

4 files changed

+94
-7
lines changed

Diff for: tests/test_udp.py

+58-6
Original file line numberDiff line numberDiff line change
@@ -161,18 +161,70 @@ def test_create_datagram_endpoint_sock(self):
161161
@unittest.skipIf(sys.version_info < (3, 5, 1),
162162
"asyncio in 3.5.0 doesn't have the 'sock' argument")
163163
def test_create_datagram_endpoint_sock_unix_domain(self):
164+
165+
class Proto(asyncio.DatagramProtocol):
166+
done = None
167+
168+
def __init__(self, loop):
169+
self.state = 'INITIAL'
170+
self.addrs = set()
171+
self.done = asyncio.Future(loop=loop)
172+
self.data = b''
173+
174+
def connection_made(self, transport):
175+
self.transport = transport
176+
assert self.state == 'INITIAL', self.state
177+
self.state = 'INITIALIZED'
178+
179+
def datagram_received(self, data, addr):
180+
assert self.state == 'INITIALIZED', self.state
181+
self.addrs.add(addr)
182+
self.data += data
183+
if self.data == b'STOP' and not self.done.done():
184+
self.done.set_result(True)
185+
186+
def error_received(self, exc):
187+
assert self.state == 'INITIALIZED', self.state
188+
if not self.done.done():
189+
self.done.set_exception(exc or RuntimeError())
190+
191+
def connection_lost(self, exc):
192+
assert self.state == 'INITIALIZED', self.state
193+
self.state = 'CLOSED'
194+
if self.done and not self.done.done():
195+
self.done.set_result(None)
196+
164197
tmp_file = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
165198
sock = socket.socket(socket.AF_UNIX, type=socket.SOCK_DGRAM)
166199
sock.bind(tmp_file)
167200

168201
with sock:
202+
pr = Proto(loop=self.loop)
169203
f = self.loop.create_datagram_endpoint(
170-
lambda: MyDatagramProto(loop=self.loop), sock=sock)
171-
tr, pr = self.loop.run_until_complete(f)
172-
self.assertIsInstance(pr, MyDatagramProto)
173-
tr.sendto(b'HELLO', tmp_file)
174-
tr.close()
175-
self.loop.run_until_complete(pr.done)
204+
lambda: pr, sock=sock)
205+
tr, pr_prime = self.loop.run_until_complete(f)
206+
self.assertIs(pr, pr_prime)
207+
208+
tmp_file2 = os.path.join(tempfile.gettempdir(), str(uuid.uuid4()))
209+
sock2 = socket.socket(socket.AF_UNIX, type=socket.SOCK_DGRAM)
210+
sock2.bind(tmp_file2)
211+
212+
with sock2:
213+
f2 = self.loop.create_datagram_endpoint(
214+
asyncio.DatagramProtocol, sock=sock2)
215+
tr2, pr2 = self.loop.run_until_complete(f2)
216+
217+
tr2.sendto(b'STOP', tmp_file)
218+
219+
self.loop.run_until_complete(pr.done)
220+
221+
tr.close()
222+
tr2.close()
223+
224+
# Let transports close
225+
self.loop.run_until_complete(asyncio.sleep(0.2))
226+
227+
self.assertIn(tmp_file2, pr.addrs)
176228

177229

178230
class Test_UV_UDP(_TestUDP, tb.UVTestCase):

Diff for: uvloop/dns.pyx

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
3333
int err
3434
system.sockaddr_in *addr4
3535
system.sockaddr_in6 *addr6
36+
system.sockaddr_un *addr_un
3637

3738
if addr.sa_family == uv.AF_INET:
3839
addr4 = <system.sockaddr_in*>addr
@@ -60,6 +61,10 @@ cdef __convert_sockaddr_to_pyaddr(const system.sockaddr* addr):
6061
addr6.sin6_scope_id
6162
)
6263

64+
elif addr.sa_family == uv.AF_UNIX:
65+
addr_un = <system.sockaddr_un*>addr
66+
return system.MakeUnixSockPyAddr(addr_un)
67+
6368
raise RuntimeError("cannot convert sockaddr into Python object")
6469

6570

Diff for: uvloop/includes/compat.h

+30-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
#include <errno.h>
2+
#include <stddef.h>
3+
#include <sys/socket.h>
4+
#include <sys/un.h>
25
#include "Python.h"
36
#include "uv.h"
47

@@ -21,10 +24,36 @@
2124
# define PLATFORM_IS_LINUX 0
2225
# define EPOLL_CTL_DEL 2
2326
struct epoll_event {};
24-
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {};
27+
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) {
28+
return 0;
29+
};
2530
#endif
2631

2732

33+
PyObject *
34+
MakeUnixSockPyAddr(struct sockaddr_un *addr)
35+
{
36+
if (addr->sun_family != AF_UNIX) {
37+
PyErr_SetString(
38+
PyExc_ValueError, "a UNIX socket addr was expected");
39+
return NULL;
40+
}
41+
42+
#ifdef __linux__
43+
int addrlen = sizeof (struct sockaddr_un);
44+
size_t linuxaddrlen = addrlen - offsetof(struct sockaddr_un, sun_path);
45+
if (linuxaddrlen > 0 && addr->sun_path[0] == 0) {
46+
return PyBytes_FromStringAndSize(addr->sun_path, linuxaddrlen);
47+
}
48+
else
49+
#endif /* linux */
50+
{
51+
/* regular NULL-terminated string */
52+
return PyUnicode_DecodeFSDefault(addr->sun_path);
53+
}
54+
}
55+
56+
2857
#if PY_VERSION_HEX < 0x03070000
2958

3059
PyObject * Context_CopyCurrent(void) {

Diff for: uvloop/includes/system.pxd

+1
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,4 @@ cdef extern from "includes/compat.h" nogil:
8080

8181
int EPOLL_CTL_DEL
8282
int epoll_ctl(int epfd, int op, int fd, epoll_event *event)
83+
object MakeUnixSockPyAddr(sockaddr_un *addr)

0 commit comments

Comments
 (0)