Name resolver based on the c-ares library. This is a non-blocking and non-threaded resolver. It may not produce the same results as the system resolver, but can be used for non-blocking resolution when threads cannot be used. c-ares fails to resolve some names when ``family`` is `
| 13 | |
| 14 | |
| 15 | class CaresResolver(Resolver): |
| 16 | """Name resolver based on the c-ares library. |
| 17 | |
| 18 | This is a non-blocking and non-threaded resolver. It may not produce |
| 19 | the same results as the system resolver, but can be used for non-blocking |
| 20 | resolution when threads cannot be used. |
| 21 | |
| 22 | c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``, |
| 23 | so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is |
| 24 | the default for ``tornado.simple_httpclient``, but other libraries |
| 25 | may default to ``AF_UNSPEC``. |
| 26 | |
| 27 | .. versionchanged:: 5.0 |
| 28 | The ``io_loop`` argument (deprecated since version 4.1) has been removed. |
| 29 | |
| 30 | .. deprecated:: 6.2 |
| 31 | This class is deprecated and will be removed in Tornado 7.0. Use the default |
| 32 | thread-based resolver instead. |
| 33 | """ |
| 34 | |
| 35 | def initialize(self) -> None: |
| 36 | self.io_loop = IOLoop.current() |
| 37 | self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb) |
| 38 | self.fds = {} # type: Dict[int, int] |
| 39 | |
| 40 | def _sock_state_cb(self, fd: int, readable: bool, writable: bool) -> None: |
| 41 | state = (IOLoop.READ if readable else 0) | (IOLoop.WRITE if writable else 0) |
| 42 | if not state: |
| 43 | self.io_loop.remove_handler(fd) |
| 44 | del self.fds[fd] |
| 45 | elif fd in self.fds: |
| 46 | self.io_loop.update_handler(fd, state) |
| 47 | self.fds[fd] = state |
| 48 | else: |
| 49 | self.io_loop.add_handler(fd, self._handle_events, state) |
| 50 | self.fds[fd] = state |
| 51 | |
| 52 | def _handle_events(self, fd: int, events: int) -> None: |
| 53 | read_fd = pycares.ARES_SOCKET_BAD |
| 54 | write_fd = pycares.ARES_SOCKET_BAD |
| 55 | if events & IOLoop.READ: |
| 56 | read_fd = fd |
| 57 | if events & IOLoop.WRITE: |
| 58 | write_fd = fd |
| 59 | self.channel.process_fd(read_fd, write_fd) |
| 60 | |
| 61 | @gen.coroutine |
| 62 | def resolve( |
| 63 | self, host: str, port: int, family: int = 0 |
| 64 | ) -> "Generator[Any, Any, List[Tuple[int, Any]]]": |
| 65 | if is_valid_ip(host): |
| 66 | addresses = [host] |
| 67 | else: |
| 68 | # gethostbyname doesn't take callback as a kwarg |
| 69 | fut = Future() # type: Future[Tuple[Any, Any]] |
| 70 | self.channel.gethostbyname( |
| 71 | host, family, lambda result, error: fut.set_result((result, error)) |
| 72 | ) |