123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- # This is a direct lift from
- # https://github.com/twisted/twisted/blob/release-21.2.0-10091/src/twisted/internet/_resolver.py.
- # We copy it here as we need to instantiate `GAIResolver` manually, but it is a
- # private class.
- from socket import (
- AF_INET,
- AF_INET6,
- AF_UNSPEC,
- SOCK_DGRAM,
- SOCK_STREAM,
- AddressFamily,
- SocketKind,
- gaierror,
- getaddrinfo,
- )
- from typing import (
- TYPE_CHECKING,
- Callable,
- List,
- NoReturn,
- Optional,
- Sequence,
- Tuple,
- Type,
- Union,
- )
- from zope.interface import implementer
- from twisted.internet.address import IPv4Address, IPv6Address
- from twisted.internet.interfaces import (
- IAddress,
- IHostnameResolver,
- IHostResolution,
- IReactorThreads,
- IResolutionReceiver,
- )
- from twisted.internet.threads import deferToThreadPool
- if TYPE_CHECKING:
- # The types below are copied from
- # https://github.com/twisted/twisted/blob/release-21.2.0-10091/src/twisted/internet/interfaces.py
- # so that the type hints can match the interfaces.
- from twisted.python.runtime import platform
- if platform.supportsThreads():
- from twisted.python.threadpool import ThreadPool
- else:
- ThreadPool = object # type: ignore[misc, assignment]
- @implementer(IHostResolution)
- class HostResolution:
- """
- The in-progress resolution of a given hostname.
- """
- def __init__(self, name: str):
- """
- Create a L{HostResolution} with the given name.
- """
- self.name = name
- def cancel(self) -> NoReturn:
- # IHostResolution.cancel
- raise NotImplementedError()
- _any = frozenset([IPv4Address, IPv6Address])
- _typesToAF = {
- frozenset([IPv4Address]): AF_INET,
- frozenset([IPv6Address]): AF_INET6,
- _any: AF_UNSPEC,
- }
- _afToType = {
- AF_INET: IPv4Address,
- AF_INET6: IPv6Address,
- }
- _transportToSocket = {
- "TCP": SOCK_STREAM,
- "UDP": SOCK_DGRAM,
- }
- _socktypeToType = {
- SOCK_STREAM: "TCP",
- SOCK_DGRAM: "UDP",
- }
- _GETADDRINFO_RESULT = List[
- Tuple[
- AddressFamily,
- SocketKind,
- int,
- str,
- Union[Tuple[str, int], Tuple[str, int, int, int]],
- ]
- ]
- @implementer(IHostnameResolver)
- class GAIResolver:
- """
- L{IHostnameResolver} implementation that resolves hostnames by calling
- L{getaddrinfo} in a thread.
- """
- def __init__(
- self,
- reactor: IReactorThreads,
- getThreadPool: Optional[Callable[[], "ThreadPool"]] = None,
- getaddrinfo: Callable[[str, int, int, int], _GETADDRINFO_RESULT] = getaddrinfo,
- ):
- """
- Create a L{GAIResolver}.
- @param reactor: the reactor to schedule result-delivery on
- @type reactor: L{IReactorThreads}
- @param getThreadPool: a function to retrieve the thread pool to use for
- scheduling name resolutions. If not supplied, the use the given
- C{reactor}'s thread pool.
- @type getThreadPool: 0-argument callable returning a
- L{twisted.python.threadpool.ThreadPool}
- @param getaddrinfo: a reference to the L{getaddrinfo} to use - mainly
- parameterized for testing.
- @type getaddrinfo: callable with the same signature as L{getaddrinfo}
- """
- self._reactor = reactor
- self._getThreadPool = (
- reactor.getThreadPool if getThreadPool is None else getThreadPool
- )
- self._getaddrinfo = getaddrinfo
- # The types on IHostnameResolver is incorrect in Twisted, see
- # https://twistedmatrix.com/trac/ticket/10276
- def resolveHostName(
- self,
- resolutionReceiver: IResolutionReceiver,
- hostName: str,
- portNumber: int = 0,
- addressTypes: Optional[Sequence[Type[IAddress]]] = None,
- transportSemantics: str = "TCP",
- ) -> IHostResolution:
- """
- See L{IHostnameResolver.resolveHostName}
- @param resolutionReceiver: see interface
- @param hostName: see interface
- @param portNumber: see interface
- @param addressTypes: see interface
- @param transportSemantics: see interface
- @return: see interface
- """
- pool = self._getThreadPool()
- addressFamily = _typesToAF[
- _any if addressTypes is None else frozenset(addressTypes)
- ]
- socketType = _transportToSocket[transportSemantics]
- def get() -> _GETADDRINFO_RESULT:
- try:
- return self._getaddrinfo(
- hostName, portNumber, addressFamily, socketType
- )
- except gaierror:
- return []
- d = deferToThreadPool(self._reactor, pool, get)
- resolution = HostResolution(hostName)
- resolutionReceiver.resolutionBegan(resolution)
- @d.addCallback
- def deliverResults(result: _GETADDRINFO_RESULT) -> None:
- for family, socktype, _proto, _cannoname, sockaddr in result:
- addrType = _afToType[family]
- resolutionReceiver.addressResolved(
- addrType(_socktypeToType.get(socktype, "TCP"), *sockaddr)
- )
- resolutionReceiver.resolutionComplete()
- return resolution
|