From: Ben Darnell Date: Sun, 24 Feb 2013 17:25:04 +0000 (-0500) Subject: Add a c-ares-based resolver implementation. X-Git-Tag: v3.0.0~92 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b02c202b92e648b4c50a905507c401764ea5c57e;p=thirdparty%2Ftornado.git Add a c-ares-based resolver implementation. --- diff --git a/tornado/platform/caresresolver.py b/tornado/platform/caresresolver.py new file mode 100644 index 000000000..11dafd4a2 --- /dev/null +++ b/tornado/platform/caresresolver.py @@ -0,0 +1,72 @@ +import pycares +import socket + +from tornado.concurrent import return_future +from tornado import gen +from tornado.ioloop import IOLoop +from tornado.netutil import Resolver, is_valid_ip + +class CaresResolver(Resolver): + """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. + """ + def initialize(self, io_loop=None): + self.io_loop = io_loop or IOLoop.instance() + self.channel = pycares.Channel(sock_state_cb=self._sock_state_cb) + self.fds = {} + + def _sock_state_cb(self, fd, readable, writable): + state = ((IOLoop.READ if readable else 0) | + (IOLoop.WRITE if writable else 0)) + if not state: + self.io_loop.remove_handler(fd) + del self.fds[fd] + elif fd in self.fds: + self.io_loop.update_handler(fd, state) + self.fds[fd] = state + else: + self.io_loop.add_handler(fd, self._handle_events, state) + self.fds[fd] = state + + def _handle_events(self, fd, events): + read_fd = pycares.ARES_SOCKET_BAD + write_fd = pycares.ARES_SOCKET_BAD + if events & IOLoop.READ: + read_fd = fd + if events & IOLoop.WRITE: + write_fd = fd + self.channel.process_fd(read_fd, write_fd) + + @return_future + @gen.engine + def getaddrinfo(self, host, port, family=0, socktype=0, proto=0, + flags=0, callback=None): + if is_valid_ip(host): + addresses = [host] + else: + # gethostbyname doesn't take callback as a kwarg + self.channel.gethostbyname(host, family, (yield gen.Callback(1))) + callback_args = yield gen.Wait(1) + assert isinstance(callback_args, gen.Arguments) + assert not callback_args.kwargs + result, error = callback_args.args + if error: + raise Exception('C-Ares returned error %s: %s while resolving %s' % + (error, pycares.errno.strerror(error), host)) + addresses = result.addresses + addrinfo = [] + for address in addresses: + if '.' in address: + address_family = socket.AF_INET + elif ':' in address: + address_family = socket.AF_INET6 + else: + address_family = socket.AF_UNSPEC + if family != socket.AF_UNSPEC and family != address_family: + raise Exception('Requested socket family %d but got %d' % + (family, address_family)) + addrinfo.append((address_family, socktype, proto, '', (address, port))) + callback(addrinfo) diff --git a/tox.ini b/tox.ini index 7fea5bea7..e84a50e22 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ [tox] # "-full" variants include optional dependencies, to ensure # that things work both in a bare install and with all the extras. -envlist = py27-full, py27-curl, py32-full, pypy, py26, py26-full, py27, py32, py32-utf8, py33, py27-opt, py32-opt, pypy-full, py27-select, py27-monotonic, py33-monotonic, py27-twisted, py27-threadedresolver, py27-twistedresolver, py27-twistedlayered +envlist = py27-full, py27-curl, py32-full, pypy, py26, py26-full, py27, py32, py32-utf8, py33, py27-opt, py32-opt, pypy-full, py27-select, py27-monotonic, py33-monotonic, py27-twisted, py27-threadedresolver, py27-twistedresolver, py27-twistedlayered, py27-caresresolver, py32-caresresolver [testenv] commands = python -m tornado.test.runtests {posargs:} @@ -118,6 +118,16 @@ deps = twisted commands = python -m tornado.test.runtests --ioloop=tornado.test.twisted_test.LayeredTwistedIOLoop --resolver=tornado.platform.twisted.TwistedResolver {posargs:} +[testenv:py27-caresresolver] +basepython = python2.7 +deps = + futures + mock + pycares + pycurl + twisted +commands = python -m tornado.test.runtests --resolver=tornado.platform.caresresolver.CaresResolver {posargs:} + [testenv:pypy-full] # This configuration works with pypy 1.9. pycurl installs ok but # curl_httpclient doesn't work. Twisted works most of the time, but @@ -157,6 +167,13 @@ basepython = python3.3 basepython = python3.3 commands = python -m tornado.test.runtests --ioloop_time_monotonic {posargs:} +[testenv:py32-caresresolver] +basepython = python3.2 +deps = + pycares +commands = python -m tornado.test.runtests --resolver=tornado.platform.caresresolver.CaresResolver {posargs:} + + # Python's optimized mode disables the assert statement, so run the # tests in this mode to ensure we haven't fallen into the trap of relying # on an assertion's side effects or using them for things that should be