137 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
| # 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,
 | |
|     gaierror,
 | |
|     getaddrinfo,
 | |
| )
 | |
| 
 | |
| from zope.interface import implementer
 | |
| 
 | |
| from twisted.internet.address import IPv4Address, IPv6Address
 | |
| from twisted.internet.interfaces import IHostnameResolver, IHostResolution
 | |
| from twisted.internet.threads import deferToThreadPool
 | |
| 
 | |
| 
 | |
| @implementer(IHostResolution)
 | |
| class HostResolution:
 | |
|     """
 | |
|     The in-progress resolution of a given hostname.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, name):
 | |
|         """
 | |
|         Create a L{HostResolution} with the given name.
 | |
|         """
 | |
|         self.name = name
 | |
| 
 | |
|     def cancel(self):
 | |
|         # 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",
 | |
| }
 | |
| 
 | |
| 
 | |
| @implementer(IHostnameResolver)
 | |
| class GAIResolver:
 | |
|     """
 | |
|     L{IHostnameResolver} implementation that resolves hostnames by calling
 | |
|     L{getaddrinfo} in a thread.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, reactor, getThreadPool=None, getaddrinfo=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
 | |
| 
 | |
|     def resolveHostName(
 | |
|         self,
 | |
|         resolutionReceiver,
 | |
|         hostName,
 | |
|         portNumber=0,
 | |
|         addressTypes=None,
 | |
|         transportSemantics="TCP",
 | |
|     ):
 | |
|         """
 | |
|         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():
 | |
|             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):
 | |
|             for family, socktype, _proto, _cannoname, sockaddr in result:
 | |
|                 addrType = _afToType[family]
 | |
|                 resolutionReceiver.addressResolved(
 | |
|                     addrType(_socktypeToType.get(socktype, "TCP"), *sockaddr)
 | |
|                 )
 | |
|             resolutionReceiver.resolutionComplete()
 | |
| 
 | |
|         return resolution
 |