Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/resolver.py: 36%
50 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:52 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:52 +0000
1import asyncio
2import socket
3from typing import Any, Dict, List, Type, Union
5from .abc import AbstractResolver
7__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver")
9try:
10 import aiodns
12 # aiodns_default = hasattr(aiodns.DNSResolver, 'gethostbyname')
13except ImportError: # pragma: no cover
14 aiodns = None
16aiodns_default = False
19class ThreadedResolver(AbstractResolver):
20 """Threaded resolver.
22 Uses an Executor for synchronous getaddrinfo() calls.
23 concurrent.futures.ThreadPoolExecutor is used by default.
24 """
26 def __init__(self) -> None:
27 self._loop = asyncio.get_running_loop()
29 async def resolve(
30 self, hostname: str, port: int = 0, family: int = socket.AF_INET
31 ) -> List[Dict[str, Any]]:
32 infos = await self._loop.getaddrinfo(
33 hostname,
34 port,
35 type=socket.SOCK_STREAM,
36 family=family,
37 flags=socket.AI_ADDRCONFIG,
38 )
40 hosts = []
41 for family, _, proto, _, address in infos:
42 if family == socket.AF_INET6:
43 if len(address) < 3:
44 # IPv6 is not supported by Python build,
45 # or IPv6 is not enabled in the host
46 continue
47 if address[3]: # type: ignore[misc]
48 # This is essential for link-local IPv6 addresses.
49 # LL IPv6 is a VERY rare case. Strictly speaking, we should use
50 # getnameinfo() unconditionally, but performance makes sense.
51 host, _port = socket.getnameinfo(
52 address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV
53 )
54 port = int(_port)
55 else:
56 host, port = address[:2]
57 else: # IPv4
58 assert family == socket.AF_INET
59 host, port = address # type: ignore[misc]
60 hosts.append(
61 {
62 "hostname": hostname,
63 "host": host,
64 "port": port,
65 "family": family,
66 "proto": proto,
67 "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV,
68 }
69 )
71 return hosts
73 async def close(self) -> None:
74 pass
77class AsyncResolver(AbstractResolver):
78 """Use the `aiodns` package to make asynchronous DNS lookups"""
80 def __init__(self, *args: Any, **kwargs: Any) -> None:
81 if aiodns is None:
82 raise RuntimeError("Resolver requires aiodns library")
84 self._loop = asyncio.get_running_loop()
85 self._resolver = aiodns.DNSResolver(*args, loop=self._loop, **kwargs)
87 async def resolve(
88 self, host: str, port: int = 0, family: int = socket.AF_INET
89 ) -> List[Dict[str, Any]]:
90 try:
91 resp = await self._resolver.gethostbyname(host, family)
92 except aiodns.error.DNSError as exc:
93 msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
94 raise OSError(msg) from exc
95 hosts = []
96 for address in resp.addresses:
97 hosts.append(
98 {
99 "hostname": host,
100 "host": address,
101 "port": port,
102 "family": family,
103 "proto": 0,
104 "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV,
105 }
106 )
108 if not hosts:
109 raise OSError("DNS lookup failed")
111 return hosts
113 async def close(self) -> None:
114 self._resolver.cancel()
117_DefaultType = Type[Union[AsyncResolver, ThreadedResolver]]
118DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver