Go to the documentation of this file.00001 from __future__ import absolute_import, division, print_function, with_statement
00002 import pycares
00003 import socket
00004
00005 from tornado import gen
00006 from tornado.ioloop import IOLoop
00007 from tornado.netutil import Resolver, is_valid_ip
00008
00009
00010 class CaresResolver(Resolver):
00011 """Name resolver based on the c-ares library.
00012
00013 This is a non-blocking and non-threaded resolver. It may not produce
00014 the same results as the system resolver, but can be used for non-blocking
00015 resolution when threads cannot be used.
00016
00017 c-ares fails to resolve some names when ``family`` is ``AF_UNSPEC``,
00018 so it is only recommended for use in ``AF_INET`` (i.e. IPv4). This is
00019 the default for ``tornado.simple_httpclient``, but other libraries
00020 may default to ``AF_UNSPEC``.
00021 """
00022 def initialize(self, io_loop=None):
00023 self.io_loop = io_loop or IOLoop.current()
00024 self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb)
00025 self.fds = {}
00026
00027 def _sock_state_cb(self, fd, readable, writable):
00028 state = ((IOLoop.READ if readable else 0) |
00029 (IOLoop.WRITE if writable else 0))
00030 if not state:
00031 self.io_loop.remove_handler(fd)
00032 del self.fds[fd]
00033 elif fd in self.fds:
00034 self.io_loop.update_handler(fd, state)
00035 self.fds[fd] = state
00036 else:
00037 self.io_loop.add_handler(fd, self._handle_events, state)
00038 self.fds[fd] = state
00039
00040 def _handle_events(self, fd, events):
00041 read_fd = pycares.ARES_SOCKET_BAD
00042 write_fd = pycares.ARES_SOCKET_BAD
00043 if events & IOLoop.READ:
00044 read_fd = fd
00045 if events & IOLoop.WRITE:
00046 write_fd = fd
00047 self.channel.process_fd(read_fd, write_fd)
00048
00049 @gen.coroutine
00050 def resolve(self, host, port, family=0):
00051 if is_valid_ip(host):
00052 addresses = [host]
00053 else:
00054
00055 self.channel.gethostbyname(host, family, (yield gen.Callback(1)))
00056 callback_args = yield gen.Wait(1)
00057 assert isinstance(callback_args, gen.Arguments)
00058 assert not callback_args.kwargs
00059 result, error = callback_args.args
00060 if error:
00061 raise Exception('C-Ares returned error %s: %s while resolving %s' %
00062 (error, pycares.errno.strerror(error), host))
00063 addresses = result.addresses
00064 addrinfo = []
00065 for address in addresses:
00066 if '.' in address:
00067 address_family = socket.AF_INET
00068 elif ':' in address:
00069 address_family = socket.AF_INET6
00070 else:
00071 address_family = socket.AF_UNSPEC
00072 if family != socket.AF_UNSPEC and family != address_family:
00073 raise Exception('Requested socket family %d but got %d' %
00074 (family, address_family))
00075 addrinfo.append((address_family, (address, port)))
00076 raise gen.Return(addrinfo)