from __future__ import absolute_import, division, print_function, with_statement
-import functools
import datetime
+import functools
+import socket
+import twisted.internet.abstract
from twisted.internet.posixbase import PosixReactorBase
from twisted.internet.interfaces import \
IReactorFDSet, IDelayedCall, IReactorTime, IReadDescriptor, IWriteDescriptor
from twisted.python import failure, log
from twisted.internet import error
+import twisted.names.cache
+import twisted.names.client
+import twisted.names.hosts
+import twisted.names.resolve
from zope.interface import implementer
-import tornado
+from tornado.concurrent import return_future
+from tornado.escape import utf8
+from tornado import gen
import tornado.ioloop
from tornado.log import app_log
+from tornado.netutil import Resolver
from tornado.stack_context import NullContext, wrap
from tornado.ioloop import IOLoop
def add_callback_from_signal(self, callback, *args, **kwargs):
self.add_callback(callback, *args, **kwargs)
+
+
+class TwistedResolver(Resolver):
+ """Twisted-based asynchronous resolver.
+
+ This is a non-blocking and non-threaded resolver. It is
+ recommended only when threads cannot be used, since it has
+ limitations compared to the standard ``getaddrinfo``-based
+ `~tornado.netutil.Resolver` and
+ `~tornado.netutil.ThreadedResolver`. Specifically, it returns at
+ most one result, and arguments other than ``host`` and ``family``
+ are ignored. It may fail to resolve when ``family`` is not
+ ``socket.AF_UNSPEC``.
+ """
+ def initialize(self, io_loop):
+ self.io_loop = io_loop
+ # partial copy of twisted.names.client.createResolver, which doesn't
+ # allow for a reactor to be passed in.
+ self.reactor = tornado.platform.twisted.TornadoReactor(io_loop)
+
+ host_resolver = twisted.names.hosts.Resolver('/etc/hosts')
+ cache_resolver = twisted.names.cache.CacheResolver(reactor=self.reactor)
+ real_resolver = twisted.names.client.Resolver('/etc/resolv.conf',
+ reactor=self.reactor)
+ self.resolver = twisted.names.resolve.ResolverChain(
+ [host_resolver, cache_resolver, real_resolver])
+
+ @return_future
+ @gen.engine
+ def getaddrinfo(self, host, port, family=0, socktype=0, proto=0,
+ flags=0, callback=None):
+ # getHostByName doesn't accept IP addresses, so if the input
+ # looks like an IP address just return it immediately.
+ if twisted.internet.abstract.isIPAddress(host):
+ resolved = host
+ resolved_family = socket.AF_INET
+ elif twisted.internet.abstract.isIPv6Address(host):
+ resolved = host
+ resolved_family = socket.AF_INET6
+ else:
+ deferred = self.resolver.getHostByName(utf8(host))
+ resolved = yield gen.Task(deferred.addCallback)
+ if twisted.internet.abstract.isIPAddress(resolved):
+ resolved_family = socket.AF_INET
+ elif twisted.internet.abstract.isIPv6Address(resolved):
+ resolved_family = socket.AF_INET6
+ else:
+ resolved_family = socket.AF_UNSPEC
+ if family != socket.AF_UNSPEC and family != resolved_family:
+ raise Exception('Requested socket family %d but got %d' %
+ (family, resolved_family))
+ result = [
+ (resolved_family, socktype, proto, '', (resolved, port)),
+ ]
+ self.io_loop.add_callback(callback, result)
[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
+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
[testenv]
commands = python -m tornado.test.runtests {posargs:}
twisted
commands = python -m tornado.test.runtests --resolver=tornado.netutil.ThreadedResolver
+[testenv:py27-twistedresolver]
+basepython = python2.7
+deps =
+ futures
+ mock
+ pycurl
+ twisted
+commands = python -m tornado.test.runtests --resolver=tornado.platform.twisted.TwistedResolver
+
[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