Skip to content

Commit 0d42774

Browse files
Martiuswebasvetlov
authored andcommitted
use aiodns.DNSResolver.gethostbyname() if available (#1136)
1 parent 77fa3b5 commit 0d42774

File tree

2 files changed

+95
-15
lines changed

2 files changed

+95
-15
lines changed

aiohttp/resolver.py

+18
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,25 @@ def __init__(self, loop=None, *args, **kwargs):
5252
self._loop = loop
5353
self._resolver = aiodns.DNSResolver(*args, loop=loop, **kwargs)
5454

55+
if not hasattr(self._resolver, 'gethostbyname'):
56+
# aiodns 1.1 is not available, fallback to DNSResolver.query
57+
self.resolve = self.resolve_with_query
58+
5559
@asyncio.coroutine
5660
def resolve(self, host, port=0, family=socket.AF_INET):
61+
hosts = []
62+
resp = yield from self._resolver.gethostbyname(host, family)
63+
64+
for address in resp.addresses:
65+
hosts.append(
66+
{'hostname': host,
67+
'host': address, 'port': port,
68+
'family': family, 'proto': 0,
69+
'flags': socket.AI_NUMERICHOST})
70+
return hosts
71+
72+
@asyncio.coroutine
73+
def resolve_with_query(self, host, port=0, family=socket.AF_INET):
5774
if family == socket.AF_INET6:
5875
qtype = 'AAAA'
5976
else:
@@ -68,6 +85,7 @@ def resolve(self, host, port=0, family=socket.AF_INET):
6885
'host': rr.host, 'port': port,
6986
'family': family, 'proto': 0,
7087
'flags': socket.AI_NUMERICHOST})
88+
7189
return hosts
7290

7391
@asyncio.coroutine

tests/test_resolver.py

+77-15
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,30 @@
99

1010
try:
1111
import aiodns
12+
gethostbyname = hasattr(aiodns.DNSResolver, 'gethostbyname')
1213
except ImportError:
1314
aiodns = None
15+
gethostbyname = False
1416

1517

1618
class FakeResult:
19+
def __init__(self, addresses):
20+
self.addresses = addresses
21+
22+
23+
class FakeQueryResult:
1724
def __init__(self, host):
1825
self.host = host
1926

2027

2128
@asyncio.coroutine
22-
def fake_result(result):
23-
return [FakeResult(host=h)
29+
def fake_result(addresses):
30+
return FakeResult(addresses=tuple(addresses))
31+
32+
33+
@asyncio.coroutine
34+
def fake_query_result(result):
35+
return [FakeQueryResult(host=h)
2436
for h in result]
2537

2638

@@ -36,23 +48,36 @@ def fake(*args, **kwargs):
3648
return fake
3749

3850

39-
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
51+
@pytest.mark.skipif(not gethostbyname, reason="aiodns 1.1 required")
4052
@asyncio.coroutine
4153
def test_async_resolver_positive_lookup(loop):
42-
with patch('aiodns.DNSResolver.query') as mock_query:
43-
mock_query.return_value = fake_result(['127.0.0.1'])
54+
with patch('aiodns.DNSResolver') as mock:
55+
mock().gethostbyname.return_value = fake_result(['127.0.0.1'])
4456
resolver = AsyncResolver(loop=loop)
4557
real = yield from resolver.resolve('www.python.org')
4658
ipaddress.ip_address(real[0]['host'])
47-
mock_query.assert_called_with('www.python.org', 'A')
59+
mock().gethostbyname.assert_called_with('www.python.org',
60+
socket.AF_INET)
4861

4962

5063
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
5164
@asyncio.coroutine
65+
def test_async_resolver_query_positive_lookup(loop):
66+
with patch('aiodns.DNSResolver') as mock:
67+
del mock().gethostbyname
68+
mock().query.return_value = fake_query_result(['127.0.0.1'])
69+
resolver = AsyncResolver(loop=loop)
70+
real = yield from resolver.resolve('www.python.org')
71+
ipaddress.ip_address(real[0]['host'])
72+
mock().query.assert_called_with('www.python.org', 'A')
73+
74+
75+
@pytest.mark.skipif(not gethostbyname, reason="aiodns 1.1 required")
76+
@asyncio.coroutine
5277
def test_async_resolver_multiple_replies(loop):
53-
with patch('aiodns.DNSResolver.query') as mock_query:
78+
with patch('aiodns.DNSResolver') as mock:
5479
ips = ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4']
55-
mock_query.return_value = fake_result(ips)
80+
mock().gethostbyname.return_value = fake_result(ips)
5681
resolver = AsyncResolver(loop=loop)
5782
real = yield from resolver.resolve('www.google.com')
5883
ips = [ipaddress.ip_address(x['host']) for x in real]
@@ -61,9 +86,32 @@ def test_async_resolver_multiple_replies(loop):
6186

6287
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
6388
@asyncio.coroutine
64-
def test_async_negative_lookup(loop):
65-
with patch('aiodns.DNSResolver.query') as mock_query:
66-
mock_query.side_effect = aiodns.error.DNSError()
89+
def test_async_resolver_query_multiple_replies(loop):
90+
with patch('aiodns.DNSResolver') as mock:
91+
del mock().gethostbyname
92+
ips = ['127.0.0.1', '127.0.0.2', '127.0.0.3', '127.0.0.4']
93+
mock().query.return_value = fake_query_result(ips)
94+
resolver = AsyncResolver(loop=loop)
95+
real = yield from resolver.resolve('www.google.com')
96+
ips = [ipaddress.ip_address(x['host']) for x in real]
97+
98+
99+
@pytest.mark.skipif(not gethostbyname, reason="aiodns 1.1 required")
100+
@asyncio.coroutine
101+
def test_async_resolver_negative_lookup(loop):
102+
with patch('aiodns.DNSResolver') as mock:
103+
mock().gethostbyname.side_effect = aiodns.error.DNSError()
104+
resolver = AsyncResolver(loop=loop)
105+
with pytest.raises(aiodns.error.DNSError):
106+
yield from resolver.resolve('doesnotexist.bla')
107+
108+
109+
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
110+
@asyncio.coroutine
111+
def test_async_resolver_query_negative_lookup(loop):
112+
with patch('aiodns.DNSResolver') as mock:
113+
del mock().gethostbyname
114+
mock().query.side_effect = aiodns.error.DNSError()
67115
resolver = AsyncResolver(loop=loop)
68116
with pytest.raises(aiodns.error.DNSError):
69117
yield from resolver.resolve('doesnotexist.bla')
@@ -122,16 +170,30 @@ def test_default_loop_for_async_resolver(loop):
122170
assert resolver._loop is loop
123171

124172

125-
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
173+
@pytest.mark.skipif(not gethostbyname, reason="aiodns 1.1 required")
126174
@asyncio.coroutine
127175
def test_async_resolver_ipv6_positive_lookup(loop):
128-
with patch('aiodns.DNSResolver.query') as mock_query:
129-
mock_query.return_value = fake_result(['::1'])
176+
with patch('aiodns.DNSResolver') as mock:
177+
mock().gethostbyname.return_value = fake_result(['::1'])
178+
resolver = AsyncResolver(loop=loop)
179+
real = yield from resolver.resolve('www.python.org',
180+
family=socket.AF_INET6)
181+
ipaddress.ip_address(real[0]['host'])
182+
mock().gethostbyname.assert_called_with('www.python.org',
183+
socket.AF_INET6)
184+
185+
186+
@pytest.mark.skipif(aiodns is None, reason="aiodns required")
187+
@asyncio.coroutine
188+
def test_async_resolver_query_ipv6_positive_lookup(loop):
189+
with patch('aiodns.DNSResolver') as mock:
190+
del mock().gethostbyname
191+
mock().query.return_value = fake_query_result(['::1'])
130192
resolver = AsyncResolver(loop=loop)
131193
real = yield from resolver.resolve('www.python.org',
132194
family=socket.AF_INET6)
133195
ipaddress.ip_address(real[0]['host'])
134-
mock_query.assert_called_with('www.python.org', 'AAAA')
196+
mock().query.assert_called_with('www.python.org', 'AAAA')
135197

136198

137199
def test_async_resolver_aiodns_not_present(loop, monkeypatch):

0 commit comments

Comments
 (0)