| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- # Author: Ovidiu Predescu
- # Date: July 2011
- #
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
- # not use this file except in compliance with the License. You may obtain
- # a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- # License for the specific language governing permissions and limitations
- # under the License.
- """Bridges between the Twisted reactor and Tornado IOLoop.
- This module lets you run applications and libraries written for
- Twisted in a Tornado application. It can be used in two modes,
- depending on which library's underlying event loop you want to use.
- This module has been tested with Twisted versions 11.0.0 and newer.
- """
- import socket
- import sys
- import twisted.internet.abstract # type: ignore
- import twisted.internet.asyncioreactor # type: ignore
- from twisted.internet.defer import Deferred # type: ignore
- from twisted.python import failure # type: ignore
- import twisted.names.cache # type: ignore
- import twisted.names.client # type: ignore
- import twisted.names.hosts # type: ignore
- import twisted.names.resolve # type: ignore
- from tornado.concurrent import Future, future_set_exc_info
- from tornado.escape import utf8
- from tornado import gen
- from tornado.netutil import Resolver
- import typing
- if typing.TYPE_CHECKING:
- from typing import Generator, Any, List, Tuple # noqa: F401
- 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.DefaultExecutorResolver`. 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``.
- Requires Twisted 12.1 or newer.
- .. versionchanged:: 5.0
- The ``io_loop`` argument (deprecated since version 4.1) has been removed.
- """
- def initialize(self) -> None:
- # partial copy of twisted.names.client.createResolver, which doesn't
- # allow for a reactor to be passed in.
- self.reactor = twisted.internet.asyncioreactor.AsyncioSelectorReactor()
- 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]
- )
- @gen.coroutine
- def resolve(
- self, host: str, port: int, family: int = 0
- ) -> "Generator[Any, Any, List[Tuple[int, Any]]]":
- # 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))
- fut = Future() # type: Future[Any]
- deferred.addBoth(fut.set_result)
- resolved = yield fut
- if isinstance(resolved, failure.Failure):
- try:
- resolved.raiseException()
- except twisted.names.error.DomainError as e:
- raise IOError(e)
- elif 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 = [(typing.cast(int, resolved_family), (resolved, port))]
- return result
- if hasattr(gen.convert_yielded, "register"):
- @gen.convert_yielded.register(Deferred) # type: ignore
- def _(d: Deferred) -> Future:
- f = Future() # type: Future[Any]
- def errback(failure: failure.Failure) -> None:
- try:
- failure.raiseException()
- # Should never happen, but just in case
- raise Exception("errback called without error")
- except:
- future_set_exc_info(f, sys.exc_info())
- d.addCallbacks(f.set_result, errback)
- return f
|