Skip to content

Commit 1d9267a

Browse files
committed
Allow sendto the same addr if remote_addr is set
* Fixes #319.
1 parent e8eb502 commit 1d9267a

File tree

4 files changed

+54
-1
lines changed

4 files changed

+54
-1
lines changed

tests/test_udp.py

+36-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ def datagram_received(self, data, addr):
5656

5757
s_transport, server = self.loop.run_until_complete(coro)
5858

59-
host, port, *_ = s_transport.get_extra_info('sockname')
59+
remote_addr = s_transport.get_extra_info('sockname')
60+
host, port, *_ = remote_addr
6061

6162
self.assertIsInstance(server, TestMyDatagramProto)
6263
self.assertEqual('INITIALIZED', server.state)
@@ -86,6 +87,36 @@ def datagram_received(self, data, addr):
8687
# received
8788
self.assertEqual(8, client.nbytes)
8889

90+
# https://github.com/MagicStack/uvloop/issues/319
91+
# uvloop should behave the same as asyncio when given remote_addr
92+
transport.sendto(b'xxx', remote_addr)
93+
tb.run_until(
94+
self.loop, lambda: server.nbytes > 3 or client.done.done())
95+
self.assertEqual(6, server.nbytes)
96+
tb.run_until(self.loop, lambda: client.nbytes > 8)
97+
98+
# received
99+
self.assertEqual(16, client.nbytes)
100+
101+
# reject sendto with a different port
102+
with self.assertRaisesRegex(
103+
ValueError, "Invalid address.*" + repr(remote_addr)
104+
):
105+
bad_addr = list(remote_addr)
106+
bad_addr[1] += 1
107+
bad_addr = tuple(bad_addr)
108+
transport.sendto(b"xxx", bad_addr)
109+
110+
# reject sento with unresolved hostname
111+
if remote_addr[0] != lc_addr[0]:
112+
with self.assertRaisesRegex(
113+
ValueError, "Invalid address.*" + repr(remote_addr)
114+
):
115+
bad_addr = list(remote_addr)
116+
bad_addr[0] = lc_addr[0]
117+
bad_addr = tuple(bad_addr)
118+
transport.sendto(b"xxx", bad_addr)
119+
89120
# extra info is available
90121
self.assertIsNotNone(transport.get_extra_info('sockname'))
91122

@@ -100,6 +131,10 @@ def test_create_datagram_endpoint_addrs_ipv4(self):
100131
self._test_create_datagram_endpoint_addrs(
101132
socket.AF_INET, ('127.0.0.1', 0))
102133

134+
def test_create_datagram_endpoint_addrs_ipv4_nameaddr(self):
135+
self._test_create_datagram_endpoint_addrs(
136+
socket.AF_INET, ('localhost', 0))
137+
103138
@unittest.skipUnless(tb.has_IPv6, 'no IPv6')
104139
def test_create_datagram_endpoint_addrs_ipv6(self):
105140
self._test_create_datagram_endpoint_addrs(

uvloop/handles/udp.pxd

+2
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ cdef class UDPTransport(UVBaseTransport):
22
cdef:
33
bint __receiving
44
int _family
5+
object _address
56

67
cdef _init(self, Loop loop, unsigned int family)
8+
cdef _set_address(self, system.addrinfo *addr)
79

810
cdef _connect(self, system.sockaddr* addr, size_t addr_len)
911

uvloop/handles/udp.pyx

+14
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ cdef class UDPTransport(UVBaseTransport):
5555
def __cinit__(self):
5656
self._family = uv.AF_UNSPEC
5757
self.__receiving = 0
58+
self._address = None
5859

5960
cdef _init(self, Loop loop, unsigned int family):
6061
cdef int err
@@ -78,6 +79,9 @@ cdef class UDPTransport(UVBaseTransport):
7879

7980
self._finish_init()
8081

82+
cdef _set_address(self, system.addrinfo *addr):
83+
self._address = __convert_sockaddr_to_pyaddr(addr.ai_addr)
84+
8185
cdef _connect(self, system.sockaddr* addr, size_t addr_len):
8286
cdef int err
8387
err = uv.uv_udp_connect(<uv.uv_udp_t*>self._handle, addr)
@@ -279,6 +283,16 @@ cdef class UDPTransport(UVBaseTransport):
279283
# Replicating asyncio logic here.
280284
return
281285

286+
if self._address:
287+
if addr not in (None, self._address):
288+
# Replicating asyncio logic here.
289+
raise ValueError(
290+
'Invalid address: must be None or %s' % (self._address,))
291+
292+
# Instead of setting addr to self._address below like what asyncio
293+
# does, we depend on previous uv_udp_connect() to set the address
294+
addr = None
295+
282296
if self._conn_lost:
283297
# Replicating asyncio logic here.
284298
if self._conn_lost >= LOG_THRESHOLD_FOR_CONNLOST_WRITES:

uvloop/loop.pyx

+2
Original file line numberDiff line numberDiff line change
@@ -3004,6 +3004,7 @@ cdef class Loop:
30043004
rai = (<AddrInfo>rads).data
30053005
udp._init(self, rai.ai_family)
30063006
udp._connect(rai.ai_addr, rai.ai_addrlen)
3007+
udp._set_address(rai)
30073008
else:
30083009
if family not in (uv.AF_INET, uv.AF_INET6):
30093010
raise ValueError('unexpected address family')
@@ -3047,6 +3048,7 @@ cdef class Loop:
30473048
rai = rai.ai_next
30483049
continue
30493050
udp._connect(rai.ai_addr, rai.ai_addrlen)
3051+
udp._set_address(rai)
30503052
break
30513053
else:
30523054
raise OSError(

0 commit comments

Comments
 (0)