Skip to content

Commit 598b16f

Browse files
authored
Fix loop.getaddrinfo() and tests (#495)
* ai_canonname always follows the flag AI_CANONNAME in static resolving (#494) * AddressFamily and SocketKind can be enums * Also fixed failing test
1 parent d6a2b59 commit 598b16f

File tree

5 files changed

+85
-15
lines changed

5 files changed

+85
-15
lines changed

tests/test_dns.py

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,32 @@
55
from uvloop import _testbase as tb
66

77

8+
def patched_getaddrinfo(*args, **kwargs):
9+
# corrected socket.getaddrinfo() behavior: ai_canonname always follows the
10+
# flag AI_CANONNAME, even if `host` is an IP
11+
rv = []
12+
result = socket.getaddrinfo(*args, **kwargs)
13+
for af, sk, proto, canon_name, addr in result:
14+
if kwargs.get('flags', 0) & socket.AI_CANONNAME:
15+
if not canon_name:
16+
canon_name = args[0]
17+
if not isinstance(canon_name, str):
18+
canon_name = canon_name.decode('ascii')
19+
elif canon_name:
20+
canon_name = ''
21+
rv.append((af, sk, proto, canon_name, addr))
22+
return rv
23+
24+
825
class BaseTestDNS:
926

10-
def _test_getaddrinfo(self, *args, **kwargs):
27+
def _test_getaddrinfo(self, *args, _patch=False, **kwargs):
1128
err = None
1229
try:
13-
a1 = socket.getaddrinfo(*args, **kwargs)
30+
if _patch:
31+
a1 = patched_getaddrinfo(*args, **kwargs)
32+
else:
33+
a1 = socket.getaddrinfo(*args, **kwargs)
1434
except socket.gaierror as ex:
1535
err = ex
1636

@@ -100,20 +120,36 @@ def test_getaddrinfo_11(self):
100120
self._test_getaddrinfo(b'example.com', '80', type=socket.SOCK_STREAM)
101121

102122
def test_getaddrinfo_12(self):
123+
# musl always returns ai_canonname but we don't
124+
patch = self.implementation != 'asyncio'
125+
103126
self._test_getaddrinfo('127.0.0.1', '80')
104-
self._test_getaddrinfo('127.0.0.1', '80', type=socket.SOCK_STREAM)
127+
self._test_getaddrinfo('127.0.0.1', '80', type=socket.SOCK_STREAM,
128+
_patch=patch)
105129

106130
def test_getaddrinfo_13(self):
131+
# musl always returns ai_canonname but we don't
132+
patch = self.implementation != 'asyncio'
133+
107134
self._test_getaddrinfo(b'127.0.0.1', b'80')
108-
self._test_getaddrinfo(b'127.0.0.1', b'80', type=socket.SOCK_STREAM)
135+
self._test_getaddrinfo(b'127.0.0.1', b'80', type=socket.SOCK_STREAM,
136+
_patch=patch)
109137

110138
def test_getaddrinfo_14(self):
139+
# musl always returns ai_canonname but we don't
140+
patch = self.implementation != 'asyncio'
141+
111142
self._test_getaddrinfo(b'127.0.0.1', b'http')
112-
self._test_getaddrinfo(b'127.0.0.1', b'http', type=socket.SOCK_STREAM)
143+
self._test_getaddrinfo(b'127.0.0.1', b'http', type=socket.SOCK_STREAM,
144+
_patch=patch)
113145

114146
def test_getaddrinfo_15(self):
147+
# musl always returns ai_canonname but we don't
148+
patch = self.implementation != 'asyncio'
149+
115150
self._test_getaddrinfo('127.0.0.1', 'http')
116-
self._test_getaddrinfo('127.0.0.1', 'http', type=socket.SOCK_STREAM)
151+
self._test_getaddrinfo('127.0.0.1', 'http', type=socket.SOCK_STREAM,
152+
_patch=patch)
117153

118154
def test_getaddrinfo_16(self):
119155
self._test_getaddrinfo('localhost', 'http')
@@ -128,12 +164,26 @@ def test_getaddrinfo_18(self):
128164
self._test_getaddrinfo('localhost', b'http', type=socket.SOCK_STREAM)
129165

130166
def test_getaddrinfo_19(self):
167+
# musl always returns ai_canonname while macOS never return for IPs,
168+
# but we strictly follow the docs to use the AI_CANONNAME flag
169+
patch = self.implementation != 'asyncio'
170+
131171
self._test_getaddrinfo('::1', 80)
132-
self._test_getaddrinfo('::1', 80, type=socket.SOCK_STREAM)
172+
self._test_getaddrinfo('::1', 80, type=socket.SOCK_STREAM,
173+
_patch=patch)
174+
self._test_getaddrinfo('::1', 80, type=socket.SOCK_STREAM,
175+
flags=socket.AI_CANONNAME, _patch=patch)
133176

134177
def test_getaddrinfo_20(self):
178+
# musl always returns ai_canonname while macOS never return for IPs,
179+
# but we strictly follow the docs to use the AI_CANONNAME flag
180+
patch = self.implementation != 'asyncio'
181+
135182
self._test_getaddrinfo('127.0.0.1', 80)
136-
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM)
183+
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM,
184+
_patch=patch)
185+
self._test_getaddrinfo('127.0.0.1', 80, type=socket.SOCK_STREAM,
186+
flags=socket.AI_CANONNAME, _patch=patch)
137187

138188
######
139189

tests/test_tcp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def test_create_server_4(self):
222222

223223
with self.assertRaisesRegex(OSError,
224224
r"error while attempting.*\('127.*: "
225-
r"address already in use"):
225+
r"address( already)? in use"):
226226

227227
self.loop.run_until_complete(
228228
self.loop.create_server(object, *addr))

uvloop/dns.pyx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,21 @@ cdef __static_getaddrinfo_pyaddr(object host, object port,
245245
except Exception:
246246
return
247247

248-
return af, type, proto, '', pyaddr
248+
if flags & socket_AI_CANONNAME:
249+
if isinstance(host, str):
250+
canon_name = host
251+
else:
252+
canon_name = host.decode('ascii')
253+
else:
254+
canon_name = ''
255+
256+
return (
257+
_intenum_converter(af, socket_AddressFamily),
258+
_intenum_converter(type, socket_SocketKind),
259+
proto,
260+
canon_name,
261+
pyaddr,
262+
)
249263

250264

251265
@cython.freelist(DEFAULT_FREELIST_SIZE)
@@ -276,8 +290,8 @@ cdef class AddrInfo:
276290
while ptr != NULL:
277291
if ptr.ai_addr.sa_family in (uv.AF_INET, uv.AF_INET6):
278292
result.append((
279-
ptr.ai_family,
280-
ptr.ai_socktype,
293+
_intenum_converter(ptr.ai_family, socket_AddressFamily),
294+
_intenum_converter(ptr.ai_socktype, socket_SocketKind),
281295
ptr.ai_protocol,
282296
('' if ptr.ai_canonname is NULL else
283297
(<bytes>ptr.ai_canonname).decode()),
@@ -370,6 +384,13 @@ cdef class NameInfoRequest(UVRequest):
370384
self.callback(convert_error(err))
371385

372386

387+
cdef _intenum_converter(value, enum_klass):
388+
try:
389+
return enum_klass(value)
390+
except ValueError:
391+
return value
392+
393+
373394
cdef void __on_addrinfo_resolved(uv.uv_getaddrinfo_t *resolver,
374395
int status, system.addrinfo *res) with gil:
375396

uvloop/includes/stdlib.pxi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ cdef int has_SO_REUSEPORT = hasattr(socket, 'SO_REUSEPORT')
7272
cdef int SO_REUSEPORT = getattr(socket, 'SO_REUSEPORT', 0)
7373
cdef int SO_BROADCAST = getattr(socket, 'SO_BROADCAST')
7474
cdef int SOCK_NONBLOCK = getattr(socket, 'SOCK_NONBLOCK', -1)
75+
cdef int socket_AI_CANONNAME = getattr(socket, 'AI_CANONNAME')
7576

7677
cdef socket_gaierror = socket.gaierror
7778
cdef socket_error = socket.error

uvloop/loop.pyx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,9 +1527,7 @@ cdef class Loop:
15271527
addr = __static_getaddrinfo_pyaddr(host, port, family,
15281528
type, proto, flags)
15291529
if addr is not None:
1530-
fut = self._new_future()
1531-
fut.set_result([addr])
1532-
return await fut
1530+
return [addr]
15331531

15341532
return await self._getaddrinfo(
15351533
host, port, family, type, proto, flags, 1)

0 commit comments

Comments
 (0)