Ticket #5085: connecttcp-ipv6-5085-1.patch

File connecttcp-ipv6-5085-1.patch, 43.5 KB (added by ragzilla, 3 years ago)
  • twisted/internet/address.py

    diff --git twisted/internet/address.py twisted/internet/address.py
    index b9192d4..8e5a483 100644
    class _IPAddress(object, util.FancyEqMixin): 
    1919 
    2020    @ivar type: A string describing the type of transport, either 'TCP' or 
    2121        'UDP'. 
    22     @ivar host: A string containing the dotted-quad IP address. 
     22    @ivar host: A string containing the native IP address. 
    2323    @ivar port: An integer representing the port number. 
    2424    """ 
    2525 
    class _IPAddress(object, util.FancyEqMixin): 
    4343        return hash((self.type, self.host, self.port)) 
    4444 
    4545 
    46  
     46     
    4747class IPv4Address(_IPAddress): 
    4848    """ 
    4949    Object representing an IPv4 socket endpoint. 
     50 
     51    @ivar host: A string containing the dotted-quad IPv4 address.  
    5052    """ 
    5153    def __init__(self, type, host, port, _bwHack=None): 
    5254        _IPAddress.__init__(self, type, host, port) 
    class IPv4Address(_IPAddress): 
    5961class IPv6Address(_IPAddress): 
    6062    """ 
    6163    Object representing an IPv6 socket endpoint. 
     64 
     65    @ivar host: A string containing the hexadecimal formatted IPv6 address.  
    6266    """ 
     67    pass 
    6368 
    6469 
    6570 
    class UNIXAddress(object, util.FancyEqMixin): 
    8590    if getattr(os.path, 'samefile', None) is not None: 
    8691        def __eq__(self, other): 
    8792            """ 
    88             overriding L{util.FancyEqMixin} to ensure the os level samefile check 
    89             is done if the name attributes do not match. 
     93            overriding L{util.FancyEqMixin} to ensure the os level samefile 
     94            check is done if the name attributes do not match. 
    9095            """ 
    9196            res = super(UNIXAddress, self).__eq__(other) 
    9297            if res == False: 
  • twisted/internet/endpoints.py

    diff --git twisted/internet/endpoints.py twisted/internet/endpoints.py
    index 79d7672..e80e1cc 100644
    from twisted.python.filepath import FilePath 
    2525 
    2626__all__ = ["clientFromString", "serverFromString", 
    2727           "TCP4ServerEndpoint", "TCP4ClientEndpoint", 
     28           "TCP6ServerEndpoint", "TCP6ClientEndpoint", 
    2829           "UNIXServerEndpoint", "UNIXClientEndpoint", 
    2930           "SSL4ServerEndpoint", "SSL4ClientEndpoint"] 
    3031 
    class _WrappingFactory(ClientFactory): 
    169170 
    170171 
    171172 
    172 class TCP4ServerEndpoint(object): 
     173class _TCPServerEndpoint(object): 
    173174    """ 
    174     TCP server endpoint with an IPv4 configuration 
     175    TCP server endpoint with a default (IPv4) configuration 
    175176 
    176177    @ivar _reactor: An L{IReactorTCP} provider. 
    177178 
    class TCP4ServerEndpoint(object): 
    212213 
    213214 
    214215 
    215 class TCP4ClientEndpoint(object): 
     216class TCP4ServerEndpoint(_TCPServerEndpoint): 
     217    """ 
     218    TCP server endpoint with an IPv4 configuration 
     219 
     220    @type _interface: str 
     221    @ivar _interface: the hostname to bind to, defaults to '' (all) 
     222    """ 
     223    def __init__(self, reactor, port, backlog=50, interface=''): 
     224        """ 
     225        @param interface: the hostname to bind to, defaults to '' (all) 
     226        """ 
     227        _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface) 
     228 
     229 
     230 
     231class TCP6ServerEndpoint(_TCPServerEndpoint): 
     232    """ 
     233    TCP server endpoint with an IPv6 configuration 
     234 
     235    @type _interface: str 
     236    @ivar _interface: the hostname to bind to, defaults to '::' (all) 
     237    """ 
     238 
     239    def __init__(self, reactor, port, backlog=50, interface='::'): 
     240        """ 
     241        @param interface: the hostname to bind to, defaults to '::' (all) 
     242        """ 
     243        _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface) 
     244 
     245 
     246 
     247class _TCPClientEndpoint(object): 
    216248    """ 
    217     TCP client endpoint with an IPv4 configuration. 
     249    TCP client endpoint with a default (IPv4) configuration. 
    218250 
    219251    @ivar _reactor: An L{IReactorTCP} provider. 
    220252 
    class TCP4ClientEndpoint(object): 
    271303 
    272304 
    273305 
     306class TCP4ClientEndpoint(_TCPClientEndpoint): 
     307    """ 
     308    TCP client endpoint with an IPv4 configuration 
     309    """ 
     310    pass 
     311 
     312 
     313 
     314class TCP6ClientEndpoint(_TCPClientEndpoint): 
     315    """ 
     316    TCP client endpoint with an IPv6 configuration 
     317    """ 
     318    pass 
     319 
     320 
     321 
    274322class SSL4ServerEndpoint(object): 
    275323    """ 
    276324    SSL secured TCP server endpoint with an IPv4 configuration. 
  • twisted/internet/tcp.py

    diff --git twisted/internet/tcp.py twisted/internet/tcp.py
    index 1c81697..31b032b 100644
    class BaseClient(_TLSClientMixin, Connection): 
    320320        else: 
    321321            reactor.callLater(0, self.failIfNotConnected, error) 
    322322 
     323 
    323324    def stopConnecting(self): 
    324325        """Stop attempt to connect.""" 
    325326        self.failIfNotConnected(error.UserError()) 
    326327 
     328 
    327329    def failIfNotConnected(self, err): 
    328330        """ 
    329331        Generic method called when the attemps to connect failed. It basically 
    class BaseClient(_TLSClientMixin, Connection): 
    348350        else: 
    349351            del self.socket, self.fileno 
    350352 
     353 
    351354    def createInternetSocket(self): 
    352355        """(internal) Create a non-blocking socket using 
    353356        self.addressFamily, self.socketType. 
    class BaseClient(_TLSClientMixin, Connection): 
    357360        fdesc._setCloseOnExec(s.fileno()) 
    358361        return s 
    359362 
     363 
    360364    def resolveAddress(self): 
    361365        if abstract.isIPAddress(self.addr[0]): 
    362366            self._setRealAddress(self.addr[0]) 
    class BaseClient(_TLSClientMixin, Connection): 
    364368            d = self.reactor.resolve(self.addr[0]) 
    365369            d.addCallbacks(self._setRealAddress, self.failIfNotConnected) 
    366370 
     371 
    367372    def _setRealAddress(self, address): 
    368373        self.realAddress = (address, self.addr[1]) 
    369374        self.doConnect() 
    370375 
     376 
    371377    def doConnect(self): 
    372378        """I connect the socket. 
    373379 
    class BaseClient(_TLSClientMixin, Connection): 
    383389            self.failIfNotConnected(error.getConnectError((err, strerror(err)))) 
    384390            return 
    385391 
    386  
    387392        # doConnect gets called twice.  The first time we actually need to 
    388393        # start the connection attempt.  The second time we don't really 
    389394        # want to (SO_ERROR above will have taken care of any errors, and if 
    class BaseClient(_TLSClientMixin, Connection): 
    418423        self.stopWriting() 
    419424        self._connectDone() 
    420425 
     426 
    421427    def _connectDone(self): 
    422428        self.protocol = self.connector.buildProtocol(self.getPeer()) 
    423429        self.connected = 1 
    class BaseClient(_TLSClientMixin, Connection): 
    426432        self.startReading() 
    427433        self.protocol.makeConnection(self) 
    428434 
     435 
    429436    def connectionLost(self, reason): 
    430437        if not self.connected: 
    431438            self.failIfNotConnected(error.ConnectError(string=reason)) 
    class BaseClient(_TLSClientMixin, Connection): 
    434441            self.connector.connectionLost(reason) 
    435442 
    436443 
     444 
    437445class Client(BaseClient): 
    438     """A TCP client.""" 
     446    """ 
     447    A TCP client. 
     448 
     449    @type _addressType: L{IPv4Address} or L{IPv6Address} 
     450    @ivar _addressType: The Twisted _IPAddress implementation for this client 
     451    """ 
     452 
     453    _addressType = address.IPv4Address 
    439454 
    440455    def __init__(self, host, port, bindAddress, connector, reactor=None): 
    441456        # BaseClient.__init__ is invoked later 
    442457        self.connector = connector 
    443458        self.addr = (host, port) 
     459        self.bindAddress = bindAddress 
     460        self.reactor = reactor 
     461        # Do outstanding initialization when real address is resolved 
     462        self.resolveAddress() 
     463 
     464 
     465    def resolveAddress(self): 
     466        if abstract.isIPAddress(self.addr[0]) or abstract.isIPv6Address(self.addr[0]): 
     467            self._setRealAddress(self.addr[0]) 
     468        else: 
     469            d = self.reactor.resolve(self.addr[0]) 
     470            d.addCallbacks(self._setRealAddress, self.failIfNotConnected) 
     471     
    444472 
    445         whenDone = self.resolveAddress 
     473    def _setRealAddress(self, addr): 
     474        """ 
     475        Sets the address family to IPv6 if passed an IPv6 literal. 
     476        Sets the real IP address for this client. 
     477        Once the IP is set the socket is created with the correct address 
     478        family. 
     479 
     480        @type addr: str 
     481        @param addr: A string formatted IPv4 or IPv6 literal. 
     482        """ 
     483        if abstract.isIPv6Address(addr): 
     484            self.addressFamily = socket.AF_INET6 
     485            self._addressType = address.IPv6Address 
     486        self.realAddress = (addr, self.addr[1]) 
     487        self.initConnection() 
     488 
     489 
     490    def initConnection(self): 
     491        """ 
     492        Initialize connection by creating appropriate socket. 
     493        """ 
    446494        err = None 
    447495        skt = None 
    448  
     496        result = True 
     497      
    449498        try: 
    450499            skt = self.createInternetSocket() 
    451500        except socket.error, se: 
    452501            err = error.ConnectBindError(se.args[0], se.args[1]) 
    453             whenDone = None 
    454         if whenDone and bindAddress is not None: 
     502            result = None 
     503        if result and self.bindAddress is not None: 
    455504            try: 
    456                 skt.bind(bindAddress) 
     505                skt.bind(self.bindAddress) 
    457506            except socket.error, se: 
    458507                err = error.ConnectBindError(se.args[0], se.args[1]) 
    459                 whenDone = None 
    460         self._finishInit(whenDone, skt, err, reactor) 
     508                result = None 
     509        if result: 
     510            Connection.__init__(self, skt, None, self.reactor) 
     511            self.doWrite = self.doConnect 
     512            self.doRead = self.doConnect 
     513            self.doConnect() 
     514        else: 
     515            self.reactor.callLater(0, self.failIfNotConnected, error) 
     516 
    461517 
    462518    def getHost(self): 
    463         """Returns an IPv4Address. 
     519        """Returns an L{IPv4Address} or L{IPv6Address}. 
    464520 
    465521        This indicates the address from which I am connecting. 
    466522        """ 
    467         return address.IPv4Address('TCP', *self.socket.getsockname()) 
     523        return self._addressType('TCP', *self.socket.getsockname()[:2]) 
     524 
    468525 
    469526    def getPeer(self): 
    470         """Returns an IPv4Address. 
     527        """Returns an L{IPv4Address} or L{IPv6Address}. 
    471528 
    472529        This indicates the address that I am connected to. 
    473530        """ 
    474         return address.IPv4Address('TCP', *self.realAddress) 
     531        return self._addressType('TCP', *self.realAddress) 
     532 
    475533 
    476534    def __repr__(self): 
    477535        s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self)) 
    478536        return s 
    479537 
    480538 
     539 
    481540class Server(_TLSServerMixin, Connection): 
    482541    """ 
    483542    Serverside socket-stream connection class. 
    class Port(base.BasePort, _SocketCloser): 
    612671 
    613672    def __repr__(self): 
    614673        if self._realPortNumber is not None: 
    615             return "<%s of %s on %s>" % (self.__class__, self.factory.__class__, 
    616                                          self._realPortNumber) 
     674            return "<%s of %s on %s (%s)>" % (self.__class__,  
     675                self.factory.__class__, self._realPortNumber,  
     676                self._addressType) 
    617677        else: 
    618678            return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__) 
    619679 
     680 
    620681    def createInternetSocket(self): 
    621682        s = base.BasePort.createInternetSocket(self) 
    622683        if platformType == "posix" and sys.platform != "cygwin": 
    class Port(base.BasePort, _SocketCloser): 
    644705        # reflect what the OS actually assigned us. 
    645706        self._realPortNumber = skt.getsockname()[1] 
    646707 
    647         log.msg("%s starting on %s" % ( 
    648                 self._getLogPrefix(self.factory), self._realPortNumber)) 
     708        log.msg("%s starting on %s (%s)" % ( 
     709                self._getLogPrefix(self.factory), self._realPortNumber,  
     710                self._addressType)) 
    649711 
    650712        # The order of the next 6 lines is kind of bizarre.  If no one 
    651713        # can explain it, perhaps we should re-arrange them. 
    class Port(base.BasePort, _SocketCloser): 
    795857 
    796858 
    797859class Connector(base.BaseConnector): 
     860    _addressType = address.IPv4Address 
     861 
    798862    def __init__(self, host, port, factory, timeout, bindAddress, reactor=None): 
    799         self.host = host 
    800863        if isinstance(port, types.StringTypes): 
    801864            try: 
    802865                port = socket.getservbyname(port, 'tcp') 
    803866            except socket.error, e: 
    804867                raise error.ServiceNameUnknownError(string="%s (%r)" % (e, port)) 
    805         self.port = port 
     868        self.host, self.port = host, port 
     869        if abstract.isIPv6Address(host): 
     870            self._addressType = address.IPv6Address 
    806871        self.bindAddress = bindAddress 
    807872        base.BaseConnector.__init__(self, factory, timeout, reactor) 
    808873 
    class Connector(base.BaseConnector): 
    810875        return Client(self.host, self.port, self.bindAddress, self, self.reactor) 
    811876 
    812877    def getDestination(self): 
    813         return address.IPv4Address('TCP', self.host, self.port) 
     878        return self._addressType('TCP', self.host, self.port) 
  • twisted/internet/test/connectionmixins.py

    diff --git twisted/internet/test/connectionmixins.py twisted/internet/test/connectionmixins.py
    index 2a592f8..2abb80b 100644
     
    55Various helpers for tests for connection-oriented transports. 
    66""" 
    77 
     8import socket 
     9 
    810from gc import collect 
    911from weakref import ref 
    1012 
     13from zope.interface import implements 
     14from zope.interface.verify import verifyObject 
     15 
    1116from twisted.python import context, log 
     17from twisted.python.failure import Failure 
    1218from twisted.python.reflect import fullyQualifiedName 
    1319from twisted.python.log import ILogContext, msg, err 
    14 from twisted.internet.defer import Deferred, gatherResults 
    15 from twisted.internet.protocol import ServerFactory, Protocol 
     20from twisted.internet.defer import Deferred, gatherResults, succeed 
     21from twisted.internet.interfaces import ( 
     22    IConnector, IResolverSimple, IReactorFDSet) 
     23from twisted.internet.protocol import ClientFactory, Protocol, ServerFactory 
     24from twisted.test.test_tcp import ClosingProtocol 
    1625 
    1726def serverFactoryFor(protocol): 
    1827    """ 
    def serverFactoryFor(protocol): 
    2938# ServerFactory is good enough for client endpoints, too. 
    3039factoryFor = serverFactoryFor 
    3140 
     41 
     42 
     43def findFreePort(interface='127.0.0.1', family=socket.AF_INET, 
     44                 type=socket.SOCK_STREAM): 
     45    """ 
     46    Ask the platform to allocate a free port on the specified interface, 
     47    then release the socket and return the address which was allocated. 
     48 
     49    @param interface: The local address to try to bind the port on. 
     50    @type interface: C{str} 
     51 
     52    @param type: The socket type which will use the resulting port. 
     53 
     54    @return: A two-tuple of address and port, like that returned by 
     55        L{socket.getsockname}. 
     56    """ 
     57    probe = socket.socket(family, type) 
     58    try: 
     59        probe.bind((interface, 0)) 
     60        return probe.getsockname() 
     61    finally: 
     62        probe.close() 
     63 
     64 
     65 
     66def _getWriters(reactor): 
     67    """ 
     68    Like L{IReactorFDSet.getWriters}, but with support for IOCP reactor as well. 
     69    """ 
     70    if IReactorFDSet.providedBy(reactor): 
     71        return reactor.getWriters() 
     72    elif 'IOCP' in reactor.__class__.__name__: 
     73        return reactor.handles 
     74    else: 
     75        # Cannot tell what is going on. 
     76        raise Exception("Cannot find writers on %r" % (reactor,)) 
     77 
     78 
     79 
    3280class _AcceptOneClient(ServerFactory): 
    3381    """ 
    3482    This factory fires a L{Deferred} with a protocol instance shortly after it 
    class _AcceptOneClient(ServerFactory): 
    5098 
    5199 
    52100 
     101class _SimplePullProducer(object): 
     102    """ 
     103    A pull producer which writes one byte whenever it is resumed.  For use by 
     104    L{test_unregisterProducerAfterDisconnect}. 
     105    """ 
     106    def __init__(self, consumer): 
     107        self.consumer = consumer 
     108 
     109 
     110    def stopProducing(self): 
     111        pass 
     112 
     113 
     114    def resumeProducing(self): 
     115        log.msg("Producer.resumeProducing") 
     116        self.consumer.write('x') 
     117 
     118 
     119 
     120class Stop(ClientFactory): 
     121    """ 
     122    A client factory which stops a reactor when a connection attempt fails. 
     123    """ 
     124    def __init__(self, reactor): 
     125        self.reactor = reactor 
     126 
     127 
     128    def clientConnectionFailed(self, connector, reason): 
     129        self.reactor.stop() 
     130 
     131 
     132 
     133class FakeResolver: 
     134    """ 
     135    A resolver implementation based on a C{dict} mapping names to addresses. 
     136    """ 
     137    implements(IResolverSimple) 
     138 
     139    def __init__(self, names): 
     140        self.names = names 
     141 
     142 
     143    def getHostByName(self, name, timeout): 
     144        try: 
     145            return succeed(self.names[name]) 
     146        except KeyError: 
     147            return fail(DNSLookupError("FakeResolver couldn't find " + name)) 
     148 
     149 
     150 
    53151class ClosingLaterProtocol(Protocol): 
    54152    """ 
    55153    ClosingLaterProtocol exchanges one byte with its peer and then disconnects 
    class LogObserverMixin(object): 
    254352        log.addObserver(loggedMessages.append) 
    255353        self.addCleanup(log.removeObserver, loggedMessages.append) 
    256354        return loggedMessages 
     355 
     356 
     357 
     358class TCPClientTestsMixin(object): 
     359    """ 
     360    This mixin defines tests applicable to TCP client implementations. 
     361    """ 
     362    def test_interface(self): 
     363        """ 
     364        L{IReactorTCP.connectTCP} returns an object providing L{IConnector}. 
     365        """ 
     366        reactor = self.buildReactor() 
     367        connector = reactor.connectTCP(self.interface, self.port,  
     368            ClientFactory()) 
     369        self.assertTrue(verifyObject(IConnector, connector)) 
     370 
     371     
     372    def test_clientConnectionFailedStopsReactor(self): 
     373        """ 
     374        The reactor can be stopped by a client factory's 
     375        C{clientConnectionFailed} method. 
     376        """ 
     377        host, port = findFreePort(self.interface, self.family)[:2] 
     378        reactor = self.buildReactor() 
     379        reactor.connectTCP(host, port, Stop(reactor)) 
     380        self.runReactor(reactor) 
     381 
     382 
     383    def test_addresses(self): 
     384        """ 
     385        A client's transport's C{getHost} and C{getPeer} return L{IPv4Address} 
     386        instances which give the dotted-quad string form of the local and 
     387        remote endpoints of the connection respectively. 
     388        """ 
     389        host, port = findFreePort(self.interface, self.family)[:2] 
     390        reactor = self.buildReactor() 
     391        reactor.installResolver(FakeResolver({'localhost': self.interface})) 
     392 
     393        server = reactor.listenTCP( 
     394            0, serverFactoryFor(Protocol), interface=host) 
     395        serverAddress = server.getHost() 
     396 
     397        addresses = {'host': None, 'peer': None} 
     398        class CheckAddress(Protocol): 
     399            def makeConnection(self, transport): 
     400                addresses['host'] = transport.getHost() 
     401                addresses['peer'] = transport.getPeer() 
     402                reactor.stop() 
     403 
     404        clientFactory = Stop(reactor) 
     405        clientFactory.protocol = CheckAddress 
     406        reactor.connectTCP( 
     407            'localhost', server.getHost().port, clientFactory, 
     408            bindAddress=(self.interface, port)) 
     409 
     410        self.runReactor(reactor) 
     411 
     412        self.assertEqual( 
     413            addresses['host'], 
     414            self.addressClass('TCP', self.interface, port)) 
     415        self.assertEqual( 
     416            addresses['peer'], 
     417            self.addressClass('TCP', self.interface, serverAddress.port)) 
     418 
     419 
     420    def test_connectEvent(self): 
     421        """ 
     422        This test checks that we correctly get notifications event for a 
     423        client. This ought to prevent a regression under Windows using the GTK2 
     424        reactor. See #3925. 
     425        """ 
     426        reactor = self.buildReactor() 
     427 
     428        server = reactor.listenTCP(0, serverFactoryFor(Protocol),  
     429            interface=self.interface) 
     430        connected = [] 
     431 
     432        class CheckConnection(Protocol): 
     433            def connectionMade(self): 
     434                connected.append(self) 
     435                reactor.stop() 
     436 
     437        clientFactory = Stop(reactor) 
     438        clientFactory.protocol = CheckConnection 
     439        reactor.connectTCP( 
     440            self.interface, server.getHost().port, clientFactory) 
     441 
     442        reactor.run() 
     443 
     444        self.assertTrue(connected) 
     445 
     446 
     447    def test_unregisterProducerAfterDisconnect(self): 
     448        """ 
     449        If a producer is unregistered from a L{ITCPTransport} provider after the 
     450        transport has been disconnected (by the peer) and after 
     451        L{ITCPTransport.loseConnection} has been called, the transport is not 
     452        re-added to the reactor as a writer as would be necessary if the 
     453        transport were still connected. 
     454        """ 
     455        reactor = self.buildReactor() 
     456        port = reactor.listenTCP(0, serverFactoryFor(ClosingProtocol), 
     457            interface=self.interface) 
     458 
     459        finished = Deferred() 
     460        finished.addErrback(log.err) 
     461        finished.addCallback(lambda ign: reactor.stop()) 
     462 
     463        writing = [] 
     464 
     465        class ClientProtocol(Protocol): 
     466            """ 
     467            Protocol to connect, register a producer, try to lose the 
     468            connection, wait for the server to disconnect from us, and 
     469            then unregister the producer. 
     470            """ 
     471            def connectionMade(self): 
     472                log.msg("ClientProtocol.connectionMade") 
     473                self.transport.registerProducer( 
     474                    _SimplePullProducer(self.transport), False) 
     475                self.transport.loseConnection() 
     476 
     477            def connectionLost(self, reason): 
     478                log.msg("ClientProtocol.connectionLost") 
     479                self.unregister() 
     480                writing.append(self.transport in _getWriters(reactor)) 
     481                finished.callback(None) 
     482 
     483            def unregister(self): 
     484                log.msg("ClientProtocol unregister") 
     485                self.transport.unregisterProducer() 
     486 
     487        clientFactory = ClientFactory() 
     488        clientFactory.protocol = ClientProtocol 
     489        reactor.connectTCP(self.interface, port.getHost().port, clientFactory) 
     490        self.runReactor(reactor) 
     491        self.assertFalse( 
     492            writing[0], "Transport was writing after unregisterProducer.") 
     493 
     494 
     495    def test_disconnectWhileProducing(self): 
     496        """ 
     497        If L{ITCPTransport.loseConnection} is called while a producer 
     498        is registered with the transport, the connection is closed 
     499        after the producer is unregistered. 
     500        """ 
     501        reactor = self.buildReactor() 
     502 
     503        # For some reason, pyobject/pygtk will not deliver the close 
     504        # notification that should happen after the unregisterProducer call in 
     505        # this test.  The selectable is in the write notification set, but no 
     506        # notification ever arrives.  Probably for the same reason #5233 led 
     507        # win32eventreactor to be broken. 
     508        skippedReactors = ["Glib2Reactor", "Gtk2Reactor"] 
     509        reactorClassName = reactor.__class__.__name__ 
     510        if reactorClassName in skippedReactors and platform.isWindows(): 
     511            raise SkipTest( 
     512                "A pygobject/pygtk bug disables this functionality on Windows.") 
     513 
     514        class Producer: 
     515            def resumeProducing(self): 
     516                log.msg("Producer.resumeProducing") 
     517         
     518        port = reactor.listenTCP(0, serverFactoryFor(Protocol), 
     519            interface=self.interface) 
     520 
     521        finished = Deferred() 
     522        finished.addErrback(log.err) 
     523        finished.addCallback(lambda ign: reactor.stop()) 
     524 
     525        class ClientProtocol(Protocol): 
     526            """ 
     527            Protocol to connect, register a producer, try to lose the 
     528            connection, unregister the producer, and wait for the connection to 
     529            actually be lost. 
     530            """ 
     531            def connectionMade(self): 
     532                log.msg("ClientProtocol.connectionMade") 
     533                self.transport.registerProducer(Producer(), False) 
     534                self.transport.loseConnection() 
     535                # Let the reactor tick over, in case synchronously calling 
     536                # loseConnection and then unregisterProducer is the same as 
     537                # synchronously calling unregisterProducer and then 
     538                # loseConnection (as it is in several reactors). 
     539                reactor.callLater(0, reactor.callLater, 0, self.unregister) 
     540 
     541            def unregister(self): 
     542                log.msg("ClientProtocol unregister") 
     543                self.transport.unregisterProducer() 
     544                # This should all be pretty quick.  Fail the test 
     545                # if we don't get a connectionLost event really 
     546                # soon. 
     547                reactor.callLater( 
     548                    1.0, finished.errback, 
     549                    Failure(Exception("Connection was not lost"))) 
     550 
     551            def connectionLost(self, reason): 
     552                log.msg("ClientProtocol.connectionLost") 
     553                finished.callback(None) 
     554 
     555        clientFactory = ClientFactory() 
     556        clientFactory.protocol = ClientProtocol 
     557        reactor.connectTCP(self.interface, port.getHost().port, clientFactory) 
     558        self.runReactor(reactor) 
     559        # If the test failed, we logged an error already and trial 
     560        # will catch it. 
  • twisted/internet/test/test_tcp.py

    diff --git twisted/internet/test/test_tcp.py twisted/internet/test/test_tcp.py
    index af9ad0e..8f7b6c2 100644
    from twisted.internet.interfaces import ( 
    2525from twisted.internet.address import IPv4Address, IPv6Address 
    2626from twisted.internet.defer import ( 
    2727    Deferred, DeferredList, succeed, fail, maybeDeferred, gatherResults) 
    28 from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint 
     28from twisted.internet.endpoints import ( 
     29    TCP4ServerEndpoint, TCP4ClientEndpoint, 
     30    TCP6ServerEndpoint, TCP6ClientEndpoint) 
    2931from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol 
    3032from twisted.internet.interfaces import ( 
    3133    IPushProducer, IPullProducer, IHalfCloseableProtocol) 
    from twisted.internet.protocol import ClientCreator 
    3335from twisted.internet.tcp import Connection, Server 
    3436 
    3537from twisted.internet.test.connectionmixins import ( 
    36     LogObserverMixin, ConnectionTestsMixin, serverFactoryFor) 
     38    LogObserverMixin, ConnectionTestsMixin, serverFactoryFor, 
     39    TCPClientTestsMixin, findFreePort, Stop, FakeResolver) 
    3740from twisted.internet.test.test_core import ObjectModelIntegrationMixin 
    3841from twisted.test.test_tcp import MyClientFactory, MyServerFactory 
    3942from twisted.test.test_tcp import ClosingProtocol 
    def getLinkLocalIPv6Address(): 
    8285 
    8386 
    8487 
    85 def findFreePort(interface='127.0.0.1', family=socket.AF_INET, 
    86                  type=socket.SOCK_STREAM): 
    87     """ 
    88     Ask the platform to allocate a free port on the specified interface, 
    89     then release the socket and return the address which was allocated. 
    90  
    91     @param interface: The local address to try to bind the port on. 
    92     @type interface: C{str} 
    93  
    94     @param type: The socket type which will use the resulting port. 
    95  
    96     @return: A two-tuple of address and port, like that returned by 
    97         L{socket.getsockname}. 
    98     """ 
    99     probe = socket.socket(family, type) 
    100     try: 
    101         probe.bind((interface, 0)) 
    102         return probe.getsockname() 
    103     finally: 
    104         probe.close() 
    105  
    106  
    107  
    10888def connect(client, (host, port)): 
    109     if '%' in host: 
     89    if '%' in host or ':' in host: 
    11090        address = socket.getaddrinfo(host, port)[0][4] 
    11191    else: 
    11292        address = (host, port) 
    def connect(client, (host, port)): 
    11494 
    11595 
    11696 
    117 class Stop(ClientFactory): 
    118     """ 
    119     A client factory which stops a reactor when a connection attempt fails. 
    120     """ 
    121     def __init__(self, reactor): 
    122         self.reactor = reactor 
    123  
    124  
    125     def clientConnectionFailed(self, connector, reason): 
    126         self.reactor.stop() 
    127  
    128  
    129  
    130 class FakeResolver: 
    131     """ 
    132     A resolver implementation based on a C{dict} mapping names to addresses. 
    133     """ 
    134     implements(IResolverSimple) 
    135  
    136     def __init__(self, names): 
    137         self.names = names 
    138  
    139  
    140     def getHostByName(self, name, timeout): 
    141         try: 
    142             return succeed(self.names[name]) 
    143         except KeyError: 
    144             return fail(DNSLookupError("FakeResolver couldn't find " + name)) 
    145  
    146  
    147  
    148 class _SimplePullProducer(object): 
    149     """ 
    150     A pull producer which writes one byte whenever it is resumed.  For use by 
    151     L{test_unregisterProducerAfterDisconnect}. 
    152     """ 
    153     def __init__(self, consumer): 
    154         self.consumer = consumer 
    155  
    156  
    157     def stopProducing(self): 
    158         pass 
    159  
    160  
    161     def resumeProducing(self): 
    162         log.msg("Producer.resumeProducing") 
    163         self.consumer.write('x') 
    164  
    165  
    166  
    167 def _getWriters(reactor): 
    168     """ 
    169     Like L{IReactorFDSet.getWriters}, but with support for IOCP reactor as well. 
    170     """ 
    171     if IReactorFDSet.providedBy(reactor): 
    172         return reactor.getWriters() 
    173     elif 'IOCP' in reactor.__class__.__name__: 
    174         return reactor.handles 
    175     else: 
    176         # Cannot tell what is going on. 
    177         raise Exception("Cannot find writers on %r" % (reactor,)) 
    178  
    179  
    180  
    18197class FakeSocket(object): 
    18298    """ 
    18399    A fake for L{socket.socket} objects. 
    class TCPConnectionTests(TestCase): 
    399315        test_tlsAfterStartTLS.skip = "No SSL support available" 
    400316 
    401317 
    402 class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin): 
     318class TCP4ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin, 
     319                             TCPClientTestsMixin): 
    403320    """ 
    404321    Builder defining tests relating to L{IReactorTCP.connectTCP}. 
    405322    """ 
     323    def setUp(self): 
     324        self.interface = '127.0.0.1' 
     325        self.port = 1234 
     326        self.family = socket.AF_INET 
     327        self.addressClass = IPv4Address 
     328 
    406329    def serverEndpoint(self, reactor): 
    407330        """ 
    408331        Create a L{TCP4ServerEndpoint} listening on localhost on a 
    409332        TCP/IP-selected port. 
    410333        """ 
    411         return TCP4ServerEndpoint(reactor, 0, interface='127.0.0.1') 
     334        return TCP4ServerEndpoint(reactor, 0, interface=self.interface) 
    412335 
    413336 
    414337    def clientEndpoint(self, reactor, serverAddress): 
    class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin): 
    419342        @type serverAddress: L{IPv4Address} 
    420343        """ 
    421344        return TCP4ClientEndpoint( 
    422             reactor, '127.0.0.1', serverAddress.port) 
    423  
    424  
    425     def test_interface(self): 
    426         """ 
    427         L{IReactorTCP.connectTCP} returns an object providing L{IConnector}. 
    428         """ 
    429         reactor = self.buildReactor() 
    430         connector = reactor.connectTCP("127.0.0.1", 1234, ClientFactory()) 
    431         self.assertTrue(verifyObject(IConnector, connector)) 
    432  
    433  
    434     def test_clientConnectionFailedStopsReactor(self): 
    435         """ 
    436         The reactor can be stopped by a client factory's 
    437         C{clientConnectionFailed} method. 
    438         """ 
    439         host, port = findFreePort() 
    440         reactor = self.buildReactor() 
    441         reactor.connectTCP(host, port, Stop(reactor)) 
    442         self.runReactor(reactor) 
     345            reactor, self.interface, serverAddress.port) 
    443346 
    444347 
    445348    def test_addresses(self): 
    class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin): 
    448351        instances which give the dotted-quad string form of the local and 
    449352        remote endpoints of the connection respectively. 
    450353        """ 
    451         host, port = findFreePort() 
    452         reactor = self.buildReactor() 
    453  
    454         server = reactor.listenTCP( 
    455             0, serverFactoryFor(Protocol), interface=host) 
    456         serverAddress = server.getHost() 
    457  
    458         addresses = {'host': None, 'peer': None} 
    459         class CheckAddress(Protocol): 
    460             def makeConnection(self, transport): 
    461                 addresses['host'] = transport.getHost() 
    462                 addresses['peer'] = transport.getPeer() 
    463                 reactor.stop() 
     354        TCPClientTestsMixin.test_addresses(self) 
    464355 
    465         clientFactory = Stop(reactor) 
    466         clientFactory.protocol = CheckAddress 
    467         reactor.connectTCP( 
    468             'localhost', server.getHost().port, clientFactory, 
    469             bindAddress=('127.0.0.1', port)) 
    470356 
    471         reactor.installResolver(FakeResolver({'localhost': '127.0.0.1'})) 
    472         self.runReactor(reactor) 
    473  
    474         self.assertEqual( 
    475             addresses['host'], 
    476             IPv4Address('TCP', '127.0.0.1', port)) 
    477         self.assertEqual( 
    478             addresses['peer'], 
    479             IPv4Address('TCP', '127.0.0.1', serverAddress.port)) 
    480357 
     358class TCP6ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin, 
     359                             TCPClientTestsMixin): 
     360    """ 
     361    Builder defining tests relating to L{IReactorTCP.connectTCP}. 
     362    """ 
     363    def setUp(self): 
     364        self.interface = '::1' 
     365        self.port = 1234 
     366        self.family = socket.AF_INET6 
     367        self.addressClass = IPv6Address 
    481368 
    482     def test_connectEvent(self): 
     369    def serverEndpoint(self, reactor): 
    483370        """ 
    484         This test checks that we correctly get notifications event for a 
    485         client. This ought to prevent a regression under Windows using the GTK2 
    486         reactor. See #3925. 
     371        Create a L{TCP6ServerEndpoint} listening on localhost on a 
     372        TCP/IP-selected port. 
    487373        """ 
    488         reactor = self.buildReactor() 
    489  
    490         server = reactor.listenTCP(0, serverFactoryFor(Protocol)) 
    491         connected = [] 
    492  
    493         class CheckConnection(Protocol): 
    494             def connectionMade(self): 
    495                 connected.append(self) 
    496                 reactor.stop() 
    497  
    498         clientFactory = Stop(reactor) 
    499         clientFactory.protocol = CheckConnection 
    500         reactor.connectTCP( 
    501             '127.0.0.1', server.getHost().port, clientFactory) 
    502  
    503         reactor.run() 
     374        return TCP6ServerEndpoint(reactor, 0, interface=self.interface) 
    504375 
    505         self.assertTrue(connected) 
    506376 
    507  
    508     def test_unregisterProducerAfterDisconnect(self): 
    509         """ 
    510         If a producer is unregistered from a L{ITCPTransport} provider after the 
    511         transport has been disconnected (by the peer) and after 
    512         L{ITCPTransport.loseConnection} has been called, the transport is not 
    513         re-added to the reactor as a writer as would be necessary if the 
    514         transport were still connected. 
     377    def clientEndpoint(self, reactor, serverAddress): 
    515378        """ 
    516         reactor = self.buildReactor() 
    517         port = reactor.listenTCP(0, serverFactoryFor(ClosingProtocol)) 
    518  
    519         finished = Deferred() 
    520         finished.addErrback(log.err) 
    521         finished.addCallback(lambda ign: reactor.stop()) 
    522  
    523         writing = [] 
    524  
    525         class ClientProtocol(Protocol): 
    526             """ 
    527             Protocol to connect, register a producer, try to lose the 
    528             connection, wait for the server to disconnect from us, and 
    529             then unregister the producer. 
    530             """ 
    531             def connectionMade(self): 
    532                 log.msg("ClientProtocol.connectionMade") 
    533                 self.transport.registerProducer( 
    534                     _SimplePullProducer(self.transport), False) 
    535                 self.transport.loseConnection() 
     379        Create a L{TCP6ClientEndpoint} which will connect to localhost 
     380        on the port given by C{serverAddress}. 
    536381 
    537             def connectionLost(self, reason): 
    538                 log.msg("ClientProtocol.connectionLost") 
    539                 self.unregister() 
    540                 writing.append(self.transport in _getWriters(reactor)) 
    541                 finished.callback(None) 
    542  
    543             def unregister(self): 
    544                 log.msg("ClientProtocol unregister") 
    545                 self.transport.unregisterProducer() 
    546  
    547         clientFactory = ClientFactory() 
    548         clientFactory.protocol = ClientProtocol 
    549         reactor.connectTCP('127.0.0.1', port.getHost().port, clientFactory) 
    550         self.runReactor(reactor) 
    551         self.assertFalse( 
    552             writing[0], "Transport was writing after unregisterProducer.") 
     382        @type serverAddress: L{IPv4Address} 
     383        """ 
     384        return TCP6ClientEndpoint( 
     385            reactor, self.interface, serverAddress.port) 
    553386 
    554387 
    555     def test_disconnectWhileProducing(self): 
     388    def test_addresses(self): 
    556389        """ 
    557         If L{ITCPTransport.loseConnection} is called while a producer 
    558         is registered with the transport, the connection is closed 
    559         after the producer is unregistered. 
     390        A client's transport's C{getHost} and C{getPeer} return L{IPv6Address} 
     391        instances which give the hexadecimal string form of the local and 
     392        remote endpoints of the connection respectively. 
    560393        """ 
    561         reactor = self.buildReactor() 
    562  
    563         # For some reason, pyobject/pygtk will not deliver the close 
    564         # notification that should happen after the unregisterProducer call in 
    565         # this test.  The selectable is in the write notification set, but no 
    566         # notification ever arrives.  Probably for the same reason #5233 led 
    567         # win32eventreactor to be broken. 
    568         skippedReactors = ["Glib2Reactor", "Gtk2Reactor"] 
    569         reactorClassName = reactor.__class__.__name__ 
    570         if reactorClassName in skippedReactors and platform.isWindows(): 
    571             raise SkipTest( 
    572                 "A pygobject/pygtk bug disables this functionality on Windows.") 
    573  
    574         class Producer: 
    575             def resumeProducing(self): 
    576                 log.msg("Producer.resumeProducing") 
    577  
    578         port = reactor.listenTCP(0, serverFactoryFor(Protocol)) 
    579  
    580         finished = Deferred() 
    581         finished.addErrback(log.err) 
    582         finished.addCallback(lambda ign: reactor.stop()) 
    583  
    584         class ClientProtocol(Protocol): 
    585             """ 
    586             Protocol to connect, register a producer, try to lose the 
    587             connection, unregister the producer, and wait for the connection to 
    588             actually be lost. 
    589             """ 
    590             def connectionMade(self): 
    591                 log.msg("ClientProtocol.connectionMade") 
    592                 self.transport.registerProducer(Producer(), False) 
    593                 self.transport.loseConnection() 
    594                 # Let the reactor tick over, in case synchronously calling 
    595                 # loseConnection and then unregisterProducer is the same as 
    596                 # synchronously calling unregisterProducer and then 
    597                 # loseConnection (as it is in several reactors). 
    598                 reactor.callLater(0, reactor.callLater, 0, self.unregister) 
    599  
    600             def unregister(self): 
    601                 log.msg("ClientProtocol unregister") 
    602                 self.transport.unregisterProducer() 
    603                 # This should all be pretty quick.  Fail the test 
    604                 # if we don't get a connectionLost event really 
    605                 # soon. 
    606                 reactor.callLater( 
    607                     1.0, finished.errback, 
    608                     Failure(Exception("Connection was not lost"))) 
    609  
    610             def connectionLost(self, reason): 
    611                 log.msg("ClientProtocol.connectionLost") 
    612                 finished.callback(None) 
    613  
    614         clientFactory = ClientFactory() 
    615         clientFactory.protocol = ClientProtocol 
    616         reactor.connectTCP('127.0.0.1', port.getHost().port, clientFactory) 
    617         self.runReactor(reactor) 
    618         # If the test failed, we logged an error already and trial 
    619         # will catch it. 
     394        TCPClientTestsMixin.test_addresses(self) 
    620395 
    621396 
    622397 
    class TCPPortTestsBuilder(ReactorBuilder, ObjectModelIntegrationMixin, 
    699474        """ 
    700475        Get the message expected to be logged when a TCP port starts listening. 
    701476        """ 
    702         return "%s starting on %d" % (factory, port.getHost().port) 
     477        return "%s starting on %d (<class 'twisted.internet.address.IPv4Address'>)" % ( 
     478            factory, port.getHost().port) 
    703479 
    704480 
    705481    def getExpectedConnectionLostLogMsg(self, port): 
    class WriteSequenceTests(ReactorBuilder): 
    13851161        self.assertEquals(producer.actions, ["resume", "resume"]) 
    13861162 
    13871163 
    1388 globals().update(TCPClientTestsBuilder.makeTestCaseClasses()) 
     1164globals().update(TCP4ClientTestsBuilder.makeTestCaseClasses()) 
     1165globals().update(TCP6ClientTestsBuilder.makeTestCaseClasses()) 
    13891166globals().update(TCPPortTestsBuilder.makeTestCaseClasses()) 
    13901167globals().update(TCPConnectionTestsBuilder.makeTestCaseClasses()) 
    13911168globals().update(WriteSequenceTests.makeTestCaseClasses()) 
  • twisted/internet/test/test_tls.py

    diff --git twisted/internet/test/test_tls.py twisted/internet/test/test_tls.py
    index f77af0d..a8e3e97 100644
    class TLSPortTestsBuilder(TLSMixin, ContextGeneratingMixin, 
    273273        """ 
    274274        Get the message expected to be logged when a TLS port starts listening. 
    275275        """ 
    276         return "%s (TLS) starting on %d" % (factory, port.getHost().port) 
     276        return "%s (TLS) starting on %d (<class 'twisted.internet.address.IPv4Address'>)" % (factory, port.getHost().port) 
    277277 
    278278 
    279279    def getExpectedConnectionLostLogMsg(self, port): 
  • twisted/test/test_tcp.py

    diff --git twisted/test/test_tcp.py twisted/test/test_tcp.py
    index ecc9fcf..9ebe967 100644
    class FactoryTestCase(unittest.TestCase): 
    678678 
    679679 
    680680class ConnectorTestCase(unittest.TestCase): 
     681    """ 
     682    TCP Connector Test cases 
     683 
     684    @type interface: str 
     685    @ivar interface: A string representing an IPv4 literal used for this test. 
     686    """ 
     687 
     688    interface="127.0.0.1" 
    681689 
    682690    def test_connectorIdentity(self): 
    683691        """ 
    class ConnectorTestCase(unittest.TestCase): 
    688696        C{clientConnectionLost} method. 
    689697        """ 
    690698        serverFactory = ClosingFactory() 
    691         tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1") 
     699        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 
    692700        serverFactory.port = tcpPort 
    693701        self.addCleanup(serverFactory.cleanUp) 
    694702        portNumber = tcpPort.getHost().port 
    class ConnectorTestCase(unittest.TestCase): 
    702710                                       seenFailures.append(reason))) 
    703711        clientFactory.startedConnecting = seenConnectors.append 
    704712 
    705         connector = reactor.connectTCP("127.0.0.1", portNumber, clientFactory) 
     713        connector = reactor.connectTCP(self.interface, portNumber, clientFactory) 
    706714        self.assertTrue(interfaces.IConnector.providedBy(connector)) 
    707715        dest = connector.getDestination() 
    708716        self.assertEqual(dest.type, "TCP") 
    709         self.assertEqual(dest.host, "127.0.0.1") 
     717        self.assertEqual(dest.host, self.interface) 
    710718        self.assertEqual(dest.port, portNumber) 
    711719 
    712720        d = loopUntil(lambda: clientFactory.stopped) 
    class ConnectorTestCase(unittest.TestCase): 
    724732        L{error.UserError} as the reason. 
    725733        """ 
    726734        serverFactory = MyServerFactory() 
    727         tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1") 
     735        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 
    728736        self.addCleanup(tcpPort.stopListening) 
    729737        portNumber = tcpPort.getHost().port 
    730738 
    class ConnectorTestCase(unittest.TestCase): 
    733741 
    734742        clientFactory = ClientStartStopFactory() 
    735743        clientFactory.startedConnecting = startedConnecting 
    736         reactor.connectTCP("127.0.0.1", portNumber, clientFactory) 
     744        reactor.connectTCP(self.interface, portNumber, clientFactory) 
    737745 
    738746        d = loopUntil(lambda: clientFactory.stopped) 
    739747        def check(ignored): 
    class ConnectorTestCase(unittest.TestCase): 
    748756        a new connection attempt to be made. 
    749757        """ 
    750758        serverFactory = ClosingFactory() 
    751         tcpPort = reactor.listenTCP(0, serverFactory, interface="127.0.0.1") 
     759        tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 
    752760        serverFactory.port = tcpPort 
    753761        self.addCleanup(serverFactory.cleanUp) 
    754762        portNumber = tcpPort.getHost().port 
    class ConnectorTestCase(unittest.TestCase): 
    758766        def clientConnectionLost(connector, reason): 
    759767            connector.connect() 
    760768        clientFactory.clientConnectionLost = clientConnectionLost 
    761         reactor.connectTCP("127.0.0.1", portNumber, clientFactory) 
     769        reactor.connectTCP(self.interface, portNumber, clientFactory) 
    762770 
    763771        d = loopUntil(lambda: clientFactory.failed) 
    764772        def reconnectFailed(ignored): 
    class ConnectorTestCase(unittest.TestCase): 
    770778 
    771779 
    772780 
     781class Connector6TestCase(ConnectorTestCase): 
     782    """ 
     783    TCPv6 Connector Test cases. 
     784 
     785    @type interface: str 
     786    @ivar interface: A string representing an IPv6 literal used for this test. 
     787    """ 
     788 
     789    interface="::1" 
     790 
     791 
     792 
    773793class CannotBindTestCase(unittest.TestCase): 
    774794    """ 
    775795    Tests for correct behavior when a reactor cannot bind to the required TCP