Ticket #4362: getaddrinfo-4362-3-1.patch

File getaddrinfo-4362-3-1.patch, 14.4 KB (added by rwall, 19 months ago)

Merged forward and resolved conflicts with trunk - contains broken tests

  • twisted/internet/base.py

    === modified file 'twisted/internet/base.py'
     
    1717 
    1818import traceback 
    1919 
     20from twisted.python.util import FancyEqMixin 
    2021from twisted.python.compat import set 
    2122from twisted.internet.interfaces import IReactorCore, IReactorTime, IReactorThreads 
    2223from twisted.internet.interfaces import IResolverSimple, IReactorPluggableResolver 
    2324from twisted.internet.interfaces import IConnector, IDelayedCall 
     25from twisted.internet.interfaces import INameResolver 
    2426from twisted.internet import fdesc, main, error, abstract, defer, threads 
    2527from twisted.python import log, failure, _reflectpy3 as reflect 
    2628from twisted.python.runtime import seconds as runtimeSeconds, platform 
     
    209211        return "".join(L) 
    210212 
    211213 
     214@implementer(INameResolver) 
     215class _ResolverComplexifier(object): 
     216    def __init__(self, resolver): 
     217        self._resolver = resolver 
     218 
     219 
     220    def getAddressInformation(self, name, service, *args): 
     221        d = self._resolver.getHostByName(name) 
     222        def cbResolved(address): 
     223            family = socket.getaddrinfo(address, 0)[0][0] 
     224            return [ 
     225                AddressInformation( 
     226                    family, 
     227                    socket.SOCK_STREAM, 
     228                    socket.IPPROTO_TCP, 
     229                    "", 
     230                    (address, service))] 
     231        d.addCallback(cbResolved) 
     232        return d 
     233 
     234 
    212235 
    213236@implementer(IResolverSimple) 
    214237class ThreadedResolver(object): 
     
    278301 
    279302 
    280303 
     304class AddressInformation(object, FancyEqMixin): 
     305    compareAttributes = ('family', 'type', 'protocol', 'canonicalName', 'address') 
     306 
     307    def __init__(self, family, type, protocol, canonicalName, address): 
     308        self.family = family 
     309        self.type = type 
     310        self.protocol = protocol 
     311        self.canonicalName = canonicalName 
     312        self.address = address 
     313 
     314 
     315 
    281316@implementer(IResolverSimple) 
    282317class BlockingResolver: 
    283318 
     
    507542            reflect.qual(self.__class__) + " did not implement installWaker") 
    508543 
    509544    def installResolver(self, resolver): 
    510         assert IResolverSimple.providedBy(resolver) 
     545        resolver = INameResolver(resolver) 
    511546        oldResolver = self.resolver 
    512547        self.resolver = resolver 
    513548        return oldResolver 
     
    567602            return defer.succeed('0.0.0.0') 
    568603        if abstract.isIPAddress(name): 
    569604            return defer.succeed(name) 
    570         return self.resolver.getHostByName(name, timeout) 
     605        d = self.resolver.getAddressInformation(name, 0) 
     606        def cbGotInfo(addresses): 
     607            for info in addresses: 
     608                if info.family == socket.AF_INET: 
     609                    return info.address[0] 
     610            # XXX Test me 
     611        d.addCallback(cbGotInfo) 
     612        return d 
     613 
    571614 
    572615    # Installation. 
    573616 
  • twisted/internet/interfaces.py

    === modified file 'twisted/internet/interfaces.py'
     
    5454        """ 
    5555 
    5656 
     57 
     58class INameResolver(Interface): 
     59    """ 
     60    XXX Write me. 
     61 
     62    RFC 3484. 
     63    """ 
     64    def getAddressInformation(name, service, family=None, socktype=None, 
     65                              proto=None, flags=None): 
     66        """ 
     67        Get the address information associated with the given name. 
     68 
     69        @param name: A hostname to resolve. 
     70        @type name: C{str} 
     71 
     72        @param service: A port number or the name of the service for which to find address 
     73            information.  For example, C{22} or C{"ssh"}. 
     74        @type service: C{int} or C{str} 
     75 
     76        @param family: If specified, limit results to addresses from this family.  Must be one 
     77            of the address family constants from the socket module.  For example, 
     78            L{socket.AF_INET}. 
     79 
     80        @param socktype: If specified, limit results to addresses for this socket type.  Must be 
     81            one of the socket type constants from the socket module.  For example, 
     82            L{socket.SOCK_STREAM}. 
     83 
     84        @param proto: If specified, limit results to addresses for this socket protocol.  Must 
     85            be one of the protocol constants from the socket module.  For example, 
     86            L{socket.IPPROTO_TCP}. 
     87 
     88        @param flags: A bitvector specifying zero or more of the following:: 
     89            - Yea right.  Go read the `getaddrinfo(3)` man page. 
     90 
     91        @raise ValueError: If one of the specified flags is not supported by the 
     92            implementation.  All flags are optional. 
     93 
     94        @return: A L{Deferred} which will fire when the resolution completes.  If resolution is 
     95            successful, the result will be a list of objects with the following attributes: 
     96 
     97            - C{family}: the family of this address 
     98            - C{type}: the type of this address 
     99            - C{protocol}: the protocol of this address 
     100            - C{canonicalName}: the canonical name associated with this address, or an empty 
     101                                string. 
     102            - C{address}: The actual address itself, including a port number.  This is suitable 
     103                          to be passed directly to L{socket.socket.connect}. 
     104        """ 
     105 
     106 
    57107class IResolverSimple(Interface): 
    58108 
    59109    def getHostByName(name, timeout = (1, 3, 11, 45)): 
  • twisted/internet/test/test_base.py

    === modified file 'twisted/internet/test/test_base.py'
     
    1212    from queue import Queue 
    1313 
    1414from zope.interface import implementer 
     15from zope.interface.verify import verifyClass 
    1516 
    1617from twisted.python.threadpool import ThreadPool 
    1718from twisted.internet.interfaces import IReactorTime, IReactorThreads 
     19from twisted.internet.interfaces import INameResolver 
    1820from twisted.internet.error import DNSLookupError 
    19 from twisted.internet.base import ThreadedResolver, DelayedCall 
     21from twisted.internet.base import ( 
     22    ThreadedNameResolver, ThreadedResolver, AddressInformation, 
     23    _ResolverComplexifier, DelayedCall) 
    2024from twisted.internet.task import Clock 
    2125from twisted.trial.unittest import TestCase 
    2226 
     27from twisted.internet.test.test_tcp import FakeResolver 
     28 
    2329 
    2430@implementer(IReactorTime, IReactorThreads) 
    2531class FakeReactor(object): 
     
    5359 
    5460 
    5561 
     62class NameResolverAdapterTests(TestCase): 
     63    """ 
     64    L{_ResolverComplexifier} adapters an L{IResolverSimple} provider 
     65    to L{INameResolver}. 
     66    """ 
     67    def test_interface(self): 
     68        """ 
     69        L{_ResolverComplexifier} implements L{INameResolver}. 
     70        """ 
     71        self.assertTrue(verifyClass(INameResolver, _ResolverComplexifier)) 
     72 
     73 
     74    def _successTest(self, address, family): 
     75        simple = FakeResolver({'example.com': address}) 
     76        resolver = _ResolverComplexifier(simple) 
     77        d = resolver.getAddressInformation('example.com', 1234) 
     78        d.addCallback( 
     79            self.assertEquals, [ 
     80                AddressInformation( 
     81                    family, 
     82                    socket.SOCK_STREAM, 
     83                    socket.IPPROTO_TCP, 
     84                    "", 
     85                    (address, 1234))]) 
     86        return d 
     87 
     88 
     89    def test_ipv4Success(self): 
     90        """ 
     91        L{_ResolverComplexifier} calls the wrapped object's 
     92        C{getHostByName} method and returns a L{Deferred} which fires 
     93        with a list of one element containing an AF_INET element with 
     94        the IPv4 address which C{getHostByName}'s L{Deferred} fired 
     95        with. 
     96        """ 
     97        return self._successTest('192.168.1.12', socket.AF_INET) 
     98 
     99 
     100    def test_ipv6Success(self): 
     101        """ 
     102        L{_ResolverComplexifier} calls the wrapped object's 
     103        C{getHostByName} method and returns a L{Deferred} which fires 
     104        with a list of one element containing an AF_INET6 element with 
     105        the IPv6 address which C{getHostByName}'s L{Deferred} fired 
     106        with. 
     107        """ 
     108        return self._successTest('::1', socket.AF_INET6) 
     109 
     110 
     111    def test_failure(self): 
     112        """ 
     113        The L{Deferred} L{_ResolverComplexifier.getAddressInformation} 
     114        returns fails if the wrapped resolver's C{getHostByName} 
     115        L{Deferred} fails. 
     116        """ 
     117        error = DNSLookupError("Problems abound") 
     118        simple = FakeResolver({'example.com': error}) 
     119        resolver = _ResolverComplexifier(simple) 
     120        d = resolver.getAddressInformation('example.com', 1234) 
     121        return self.assertFailure(d, DNSLookupError) 
     122 
     123 
     124 
     125class ThreadedNameResolverTests(TestCase): 
     126    """ 
     127    Tests for L{ThreadedNameResolver}. 
     128    """ 
     129    def test_interface(self): 
     130        """ 
     131        L{ThreadedNameResolver} implements L{INameResolver}. 
     132        """ 
     133        self.assertTrue(verifyClass(INameResolver, ThreadedNameResolver)) 
     134 
     135 
     136    def test_success(self): 
     137        """ 
     138        If the underlying C{getaddrinfo} library call completes 
     139        successfully and returns results, the L{Deferred} returned by 
     140        L{ThreadedNameResolver.getAddressInformation} fires with a 
     141        list of L{AddressInformation} instances representing those 
     142        results. 
     143        """ 
     144 
     145 
    56146class ThreadedResolverTests(TestCase): 
    57147    """ 
    58148    Tests for L{ThreadedResolver}. 
  • twisted/internet/test/test_core.py

    === modified file 'twisted/internet/test/test_core.py'
     
    1212import signal 
    1313import time 
    1414import inspect 
    15  
     15import socket 
     16 
     17from zope.interface import implementer 
     18 
     19from twisted.internet.interfaces import INameResolver 
    1620from twisted.internet.abstract import FileDescriptor 
    1721from twisted.internet.error import ReactorAlreadyRunning, ReactorNotRestartable 
    18 from twisted.internet.defer import Deferred 
     22from twisted.internet.defer import Deferred, succeed 
     23from twisted.internet.base import AddressInformation 
    1924from twisted.internet.test.reactormixins import ReactorBuilder 
    2025 
    2126 
     27 
    2228class ObjectModelIntegrationMixin(object): 
    2329    """ 
    2430    Helpers for tests about the object model of reactor-related objects. 
     
    4147 
    4248 
    4349 
     50@implementer(INameResolver) 
     51class MemoryNameResolver(object): 
     52    def __init__(self, names): 
     53        self._names = names 
     54 
     55    def getAddressInformation(self, name, service, family=None, type=None, 
     56                              protocol=None, flags=None): 
     57        return succeed([ 
     58                address 
     59                for address 
     60                in self._names[name, service] 
     61                if family is None or family == address.family 
     62                and type is None or type == address.type 
     63                and protocol is None or protocol == address.protocol]) 
     64 
     65 
     66 
    4467class ObjectModelIntegrationTest(ReactorBuilder, ObjectModelIntegrationMixin): 
    4568    """ 
    4669    Test details of object model integration against all reactors. 
     
    328351        self.assertEqual(events, ['tested']) 
    329352 
    330353 
     354    def test_resolve(self): 
     355        """ 
     356        C{reactor.resolve(name)} calls the C{getAddressInformation} 
     357        method of the installed resolver and returns a L{Deferred} 
     358        which fires with the first C{AF_INET} family element from the 
     359        result of C{getAddressInformation}. 
     360        """ 
     361        resolver = MemoryNameResolver({ 
     362                ('example.com', 0): [ 
     363                    AddressInformation( 
     364                        socket.AF_INET6, 
     365                        socket.SOCK_STREAM, 
     366                        socket.IPPROTO_TCP, 
     367                        "", 
     368                        ("::1", 0)), 
     369                    AddressInformation( 
     370                        socket.AF_INET, 
     371                        socket.SOCK_STREAM, 
     372                        socket.IPPROTO_TCP, 
     373                        "", 
     374                        ("127.0.0.1", 22))]}) 
     375 
     376        reactor = self.buildReactor() 
     377        reactor.installResolver(resolver) 
     378        d = reactor.resolve("example.com") 
     379        d.addCallback(self.assertEquals, "127.0.0.1") 
     380        return d 
     381 
    331382 
    332383globals().update(SystemEventTestsBuilder.makeTestCaseClasses()) 
    333384globals().update(ObjectModelIntegrationTest.makeTestCaseClasses()) 
  • twisted/internet/test/test_tcp.py

    === modified file 'twisted/internet/test/test_tcp.py'
     
    2525    ConnectionLost, UserError, ConnectionRefusedError, ConnectionDone, 
    2626    ConnectionAborted) 
    2727from twisted.internet.interfaces import ( 
    28     ILoggingContext, IConnector, IReactorFDSet, IReactorSocket, IReactorTCP) 
     28    ILoggingContext, IConnector, IReactorFDSet, IReactorSocket, IReactorTCP, IResolverSimple) 
    2929from twisted.internet.address import IPv4Address, IPv6Address 
    3030from twisted.internet.defer import ( 
    31     Deferred, DeferredList, maybeDeferred, gatherResults) 
     31    Deferred, DeferredList, maybeDeferred, gatherResults, succeed, fail) 
    3232from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint 
     33from twisted.internet.error import DNSLookupError 
    3334from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol 
    3435from twisted.internet.interfaces import ( 
    3536    IPushProducer, IPullProducer, IHalfCloseableProtocol) 
     
    108109    client.connect(address) 
    109110 
    110111 
     112class Stop(ClientFactory): 
     113    """ 
     114    A client factory which stops a reactor when a connection attempt fails. 
     115    """ 
     116    def __init__(self, reactor): 
     117        self.reactor = reactor 
     118 
     119 
     120    def clientConnectionFailed(self, connector, reason): 
     121        self.reactor.stop() 
     122 
     123 
     124@implementer(IResolverSimple) 
     125class FakeResolver: 
     126    """ 
     127    A resolver implementation based on a C{dict} mapping names to addresses. 
     128    """ 
     129 
     130    def __init__(self, names): 
     131        self.names = names 
     132 
     133 
     134    def getHostByName(self, name, timeout=None): 
     135        try: 
     136            return succeed(self.names[name]) 
     137        except KeyError: 
     138            return fail(DNSLookupError("FakeResolver couldn't find " + name)) 
     139 
     140 
     141 
     142class TCPClientTestsBuilder(ReactorBuilder): 
     143    """ 
     144    Builder defining tests relating to L{IReactorTCP.connectTCP}. 
     145    """ 
     146    def _freePort(self, interface='127.0.0.1'): 
     147        probe = socket.socket() 
     148        try: 
     149            probe.bind((interface, 0)) 
     150            return probe.getsockname() 
     151        finally: 
     152            probe.close() 
     153 
     154    def test_clientConnectionFailedStopsReactor(self): 
     155        """ 
     156        The reactor can be stopped by a client factory's 
     157        C{clientConnectionFailed} method. 
     158        """ 
     159        host, port = self._freePort() 
     160        reactor = self.buildReactor() 
     161        reactor.connectTCP(host, port, Stop(reactor)) 
     162 
    111163 
    112164class FakeSocket(object): 
    113165    """