Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/aiohttp/resolver.py: 31%
68 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:40 +0000
« prev ^ index » next coverage.py v7.3.2, created at 2023-12-08 06:40 +0000
1import asyncio
2import socket
3from typing import Any, Dict, List, Optional, Type, Union
5from .abc import AbstractResolver
6from .helpers import get_running_loop
8__all__ = ("ThreadedResolver", "AsyncResolver", "DefaultResolver")
10try:
11 import aiodns
13 # aiodns_default = hasattr(aiodns.DNSResolver, 'gethostbyname')
14except ImportError: # pragma: no cover
15 aiodns = None
17aiodns_default = False
20class ThreadedResolver(AbstractResolver):
21 """Threaded resolver.
23 Uses an Executor for synchronous getaddrinfo() calls.
24 concurrent.futures.ThreadPoolExecutor is used by default.
25 """
27 def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
28 self._loop = get_running_loop(loop)
30 async def resolve(
31 self, hostname: str, port: int = 0, family: int = socket.AF_INET
32 ) -> List[Dict[str, Any]]:
33 infos = await self._loop.getaddrinfo(
34 hostname,
35 port,
36 type=socket.SOCK_STREAM,
37 family=family,
38 flags=socket.AI_ADDRCONFIG,
39 )
41 hosts = []
42 for family, _, proto, _, address in infos:
43 if family == socket.AF_INET6:
44 if len(address) < 3:
45 # IPv6 is not supported by Python build,
46 # or IPv6 is not enabled in the host
47 continue
48 if address[3]:
49 # This is essential for link-local IPv6 addresses.
50 # LL IPv6 is a VERY rare case. Strictly speaking, we should use
51 # getnameinfo() unconditionally, but performance makes sense.
52 host, _port = socket.getnameinfo(
53 address, socket.NI_NUMERICHOST | socket.NI_NUMERICSERV
54 )
55 port = int(_port)
56 else:
57 host, port = address[:2]
58 else: # IPv4
59 assert family == socket.AF_INET
60 host, port = address # type: ignore[misc]
61 hosts.append(
62 {
63 "hostname": hostname,
64 "host": host,
65 "port": port,
66 "family": family,
67 "proto": proto,
68 "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV,
69 }
70 )
72 return hosts
74 async def close(self) -> None:
75 pass
78class AsyncResolver(AbstractResolver):
79 """Use the `aiodns` package to make asynchronous DNS lookups"""
81 def __init__(
82 self,
83 loop: Optional[asyncio.AbstractEventLoop] = None,
84 *args: Any,
85 **kwargs: Any
86 ) -> None:
87 if aiodns is None:
88 raise RuntimeError("Resolver requires aiodns library")
90 self._loop = get_running_loop(loop)
91 self._resolver = aiodns.DNSResolver(*args, loop=loop, **kwargs)
93 if not hasattr(self._resolver, "gethostbyname"):
94 # aiodns 1.1 is not available, fallback to DNSResolver.query
95 self.resolve = self._resolve_with_query # type: ignore
97 async def resolve(
98 self, host: str, port: int = 0, family: int = socket.AF_INET
99 ) -> List[Dict[str, Any]]:
100 try:
101 resp = await self._resolver.gethostbyname(host, family)
102 except aiodns.error.DNSError as exc:
103 msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
104 raise OSError(msg) from exc
105 hosts = []
106 for address in resp.addresses:
107 hosts.append(
108 {
109 "hostname": host,
110 "host": address,
111 "port": port,
112 "family": family,
113 "proto": 0,
114 "flags": socket.AI_NUMERICHOST | socket.AI_NUMERICSERV,
115 }
116 )
118 if not hosts:
119 raise OSError("DNS lookup failed")
121 return hosts
123 async def _resolve_with_query(
124 self, host: str, port: int = 0, family: int = socket.AF_INET
125 ) -> List[Dict[str, Any]]:
126 if family == socket.AF_INET6:
127 qtype = "AAAA"
128 else:
129 qtype = "A"
131 try:
132 resp = await self._resolver.query(host, qtype)
133 except aiodns.error.DNSError as exc:
134 msg = exc.args[1] if len(exc.args) >= 1 else "DNS lookup failed"
135 raise OSError(msg) from exc
137 hosts = []
138 for rr in resp:
139 hosts.append(
140 {
141 "hostname": host,
142 "host": rr.host,
143 "port": port,
144 "family": family,
145 "proto": 0,
146 "flags": socket.AI_NUMERICHOST,
147 }
148 )
150 if not hosts:
151 raise OSError("DNS lookup failed")
153 return hosts
155 async def close(self) -> None:
156 self._resolver.cancel()
159_DefaultType = Type[Union[AsyncResolver, ThreadedResolver]]
160DefaultResolver: _DefaultType = AsyncResolver if aiodns_default else ThreadedResolver