Ticket #5085: twisted-tcp6_v2.patch

File twisted-tcp6_v2.patch, 20.6 KB (added by chjurk, 5 years ago)
  • twisted/internet/address.py

    diff -ru ../listentcp-ipv6-5084-3/twisted/internet/address.py ./twisted/internet/address.py
    old new  
    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
     
    4747class IPv4Address(_IPAddress):
    4848    """
    4949    Object representing an IPv4 socket endpoint.
     50   
     51    @ivar type: A string describing the type of transport, either 'TCP' or
     52        'UDP'.
     53    @ivar host: A string containing the dotted-quad IP address.
     54    @ivar port: An integer representing the port number.
    5055    """
    5156    def __init__(self, type, host, port, _bwHack=None):
    5257        _IPAddress.__init__(self, type, host, port)
     
    5560                    DeprecationWarning, stacklevel=2)
    5661
    5762
     63    def __str__(self):
     64        """
     65        Returns human-readable IP address version.
     66       
     67        @return: A string containing the word "IPv6".
     68        """
     69        return "IPv4"
     70
     71
    5872
    5973class IPv6Address(_IPAddress):
    6074    """
    6175    Object representing an IPv6 socket endpoint.
     76
     77    @ivar type: A string describing the type of transport, either 'TCP' or
     78        'UDP'.
     79    @ivar host: A string containing the hexadecimal formatted IP address.
     80    @ivar port: An integer representing the port number.
     81    @ivar flow_info: An integer specifying IPv6 flow info flags.
     82    @ivar scope_id: A string containing the address scope identifier.
    6283    """
     84    def __init__(self, type, host, port, flow_info=0, scope_id=0):
     85        self.flow_info = flow_info
     86        self.scope_id = scope_id
     87        _IPAddress.__init__(self, type, host, port)
     88
     89
     90    def __hash__(self):
     91        return hash((self.type, self.host, self.port, self.flow_info, self.scope_id))
     92
     93
     94    def __str__(self):
     95        """
     96        Returns human-readable IP address version.
     97       
     98        @return: A string containing the word "IPv6".
     99        """
     100        return "IPv6"
    63101
    64102
    65103
  • twisted/internet/endpoints.py

    diff -ru ../listentcp-ipv6-5084-3/twisted/internet/endpoints.py ./twisted/internet/endpoints.py
    old new  
    2525
    2626__all__ = ["clientFromString", "serverFromString",
    2727           "TCP4ServerEndpoint", "TCP4ClientEndpoint",
     28           "TCP6ServerEndpoint", "TCP6ClientEndpoint",
    2829           "UNIXServerEndpoint", "UNIXClientEndpoint",
    2930           "SSL4ServerEndpoint", "SSL4ClientEndpoint"]
    3031
     
    145146
    146147
    147148
    148 class TCP4ServerEndpoint(object):
     149class _TCPServerEndpoint(object):
    149150    """
    150     TCP server endpoint with an IPv4 configuration
     151    TCP server endpoint with an generic IP configuration
    151152
    152153    @ivar _reactor: An L{IReactorTCP} provider.
    153154
     
    188189
    189190
    190191
    191 class TCP4ClientEndpoint(object):
     192class TCP4ServerEndpoint(_TCPServerEndpoint):
    192193    """
    193     TCP client endpoint with an IPv4 configuration.
     194    TCP server endpoint with an IPv4 configuration
     195
     196    @ivar _reactor: An L{IReactorTCP} provider.
     197
     198    @type _port: int
     199    @ivar _port: The port number on which to listen for incoming connections.
     200
     201    @type _backlog: int
     202    @ivar _backlog: size of the listen queue
     203
     204    @type _interface: str
     205    @ivar _interface: the hostname to bind to, defaults to '' (all)
     206    """
     207    pass
     208
     209
     210
     211class TCP6ServerEndpoint(_TCPServerEndpoint):
     212    """
     213    TCP server endpoint with an IPv6 configuration
     214
     215    @ivar _reactor: An L{IReactorTCP} provider.
     216
     217    @type _port: int
     218    @ivar _port: The port number on which to listen for incoming connections.
     219
     220    @type _backlog: int
     221    @ivar _backlog: size of the listen queue
     222
     223    @type _interface: str
     224    @ivar _interface: the hostname to bind to, defaults to '::' (all)
     225    """
     226    def __init__(self, reactor, port, backlog=50, interface='::'):
     227        _TCPServerEndpoint.__init__(self, reactor, port, backlog, interface)
     228
     229
     230
     231class _TCPClientEndpoint(object):
     232    """
     233    TCP client endpoint with an generic IP configuration.
    194234
    195235    @ivar _reactor: An L{IReactorTCP} provider.
    196236
     
    247287
    248288
    249289
    250 class SSL4ServerEndpoint(object):
     290class TCP4ClientEndpoint(_TCPClientEndpoint):
    251291    """
    252     SSL secured TCP server endpoint with an IPv4 configuration.
     292    TCP client endpoint with an IPv4 configuration.
     293
     294    @ivar _reactor: An L{IReactorTCP} provider.
     295
     296    @type _host: str
     297    @ivar _host: The hostname to connect to as a C{str}
     298
     299    @type _port: int
     300    @ivar _port: The port to connect to as C{int}
     301
     302    @type _timeout: int
     303    @ivar _timeout: number of seconds to wait before assuming the
     304        connection has failed.
     305
     306    @type _bindAddress: tuple
     307    @type _bindAddress: a (host, port) tuple of local address to bind
     308        to, or None.
     309    """
     310    pass
     311
     312
     313
     314class TCP6ClientEndpoint(_TCPClientEndpoint):
     315    """
     316    TCP client endpoint with an IPv6 configuration.
     317
     318    @ivar _reactor: An L{IReactorTCP} provider.
     319
     320    @type _host: str
     321    @ivar _host: The hostname to connect to as a C{str}
     322
     323    @type _port: int
     324    @ivar _port: The port to connect to as C{int}
     325
     326    @type _timeout: int
     327    @ivar _timeout: number of seconds to wait before assuming the
     328        connection has failed.
     329
     330    @type _bindAddress: tuple
     331    @type _bindAddress: a (host, port) tuple of local address to bind
     332        to, or None.
     333    """
     334    pass
     335
     336
     337
     338class _SSLServerEndpoint(object):
     339    """
     340    SSL secured TCP server endpoint with an generic IP configuration.
    253341
    254342    @ivar _reactor: An L{IReactorSSL} provider.
    255343
     
    303391
    304392
    305393
    306 class SSL4ClientEndpoint(object):
     394class SSL4ServerEndpoint(_SSLServerEndpoint):
    307395    """
    308     SSL secured TCP client endpoint with an IPv4 configuration
     396    SSL secured TCP server endpoint with an IPv4 configuration.
     397
     398    @ivar _reactor: An L{IReactorSSL} provider.
     399
     400    @type _host: str
     401    @ivar _host: The hostname to connect to as a C{str}
     402
     403    @type _port: int
     404    @ivar _port: The port to connect to as C{int}
     405
     406    @type _sslContextFactory: L{OpenSSLCertificateOptions}
     407    @var _sslContextFactory: SSL Configuration information as an
     408        L{OpenSSLCertificateOptions}
     409
     410    @type _backlog: int
     411    @ivar _backlog: size of the listen queue
     412
     413    @type _interface: str
     414    @ivar _interface: the hostname to bind to, defaults to '' (all)
     415    """
     416    pass
     417
     418
     419
     420class SSL6ServerEndpoint(_SSLServerEndpoint):
     421    """
     422    SSL secured TCP server endpoint with an IPv6 configuration.
     423
     424    @ivar _reactor: An L{IReactorSSL} provider.
     425
     426    @type _host: str
     427    @ivar _host: The hostname to connect to as a C{str}
     428
     429    @type _port: int
     430    @ivar _port: The port to connect to as C{int}
     431
     432    @type _sslContextFactory: L{OpenSSLCertificateOptions}
     433    @var _sslContextFactory: SSL Configuration information as an
     434        L{OpenSSLCertificateOptions}
     435
     436    @type _backlog: int
     437    @ivar _backlog: size of the listen queue
     438
     439    @type _interface: str
     440    @ivar _interface: the hostname to bind to, defaults to '::' (all)
     441    """
     442    pass
     443
     444
     445
     446
     447class _SSLClientEndpoint(object):
     448    """
     449    SSL secured TCP client endpoint with an generic IP configuration
    309450
    310451    @ivar _reactor: An L{IReactorSSL} provider.
    311452
     
    368509            return wf._onConnection
    369510        except:
    370511            return defer.fail()
     512
     513
     514
     515class SSL4ClientEndpoint(_SSLClientEndpoint):
     516    """
     517    SSL secured TCP client endpoint with an IPv4 configuration
     518
     519    @ivar _reactor: An L{IReactorSSL} provider.
     520
     521    @type _host: str
     522    @ivar _host: The hostname to connect to as a C{str}
     523
     524    @type _port: int
     525    @ivar _port: The port to connect to as C{int}
     526
     527    @type _sslContextFactory: L{OpenSSLCertificateOptions}
     528    @var _sslContextFactory: SSL Configuration information as an
     529        L{OpenSSLCertificateOptions}
     530
     531    @type _timeout: int
     532    @ivar _timeout: number of seconds to wait before assuming the
     533        connection has failed.
     534
     535    @type _bindAddress: tuple
     536    @ivar _bindAddress: a (host, port) tuple of local address to bind
     537        to, or None.
     538    """
     539    pass
     540
     541
     542
     543class SSL6ClientEndpoint(_SSLClientEndpoint):
     544    """
     545    SSL secured TCP client endpoint with an IPv6 configuration
     546
     547    @ivar _reactor: An L{IReactorSSL} provider.
     548
     549    @type _host: str
     550    @ivar _host: The hostname to connect to as a C{str}
     551
     552    @type _port: int
     553    @ivar _port: The port to connect to as C{int}
     554
     555    @type _sslContextFactory: L{OpenSSLCertificateOptions}
     556    @var _sslContextFactory: SSL Configuration information as an
     557        L{OpenSSLCertificateOptions}
     558
     559    @type _timeout: int
     560    @ivar _timeout: number of seconds to wait before assuming the
     561        connection has failed.
     562
     563    @type _bindAddress: tuple
     564    @ivar _bindAddress: a (host, port) tuple of local address to bind
     565        to, or None.
     566    """
     567    pass
    371568
    372569
    373570
  • twisted/internet/tcp.py

    diff -ru ../listentcp-ipv6-5084-3/twisted/internet/tcp.py ./twisted/internet/tcp.py
    old new  
    258258    A base class for client TCP (and similiar) sockets.
    259259    """
    260260    _base = Connection
     261    _addressType = address.IPv4Address
    261262
    262263    addressFamily = socket.AF_INET
    263264    socketType = socket.SOCK_STREAM
     
    270271            Connection.__init__(self, skt, None, reactor)
    271272            self.doWrite = self.doConnect
    272273            self.doRead = self.doConnect
    273             reactor.callLater(0, whenDone)
     274            self.doConnect()
    274275        else:
    275276            reactor.callLater(0, self.failIfNotConnected, error)
    276277
     
    312313        return s
    313314
    314315    def resolveAddress(self):
    315         if abstract.isIPAddress(self.addr[0]):
     316        if abstract.isIPAddress(self.addr[0]) or abstract.isIPv6Address(self.addr[0]):
    316317            self._setRealAddress(self.addr[0])
    317318        else:
    318319            d = self.reactor.resolve(self.addr[0])
    319320            d.addCallbacks(self._setRealAddress, self.failIfNotConnected)
    320321
    321     def _setRealAddress(self, address):
    322         self.realAddress = (address, self.addr[1])
    323         self.doConnect()
     322    def _setRealAddress(self, addr):
     323        """
     324        Set the real IP address for this client.
     325        Once the IP address is set, the socket is created using the correct
     326        address family.
     327        """
     328        if abstract.isIPv6Address(addr):
     329            self.addressFamily = socket.AF_INET6
     330            self._addressType = address.IPv6Address
     331        self.realAddress = (addr, self.addr[1])
     332
     333        # create the socket and wait finish init after that
     334        self.initConnection()
     335
     336    def initConnection(self):
     337        """
     338        Initialize connection by creating the appropriate socket.
     339        """
     340        err = None
     341        skt = None
     342        result = True
     343
     344        try:
     345            skt = self.createInternetSocket()
     346        except socket.error, se:
     347            err = error.ConnectBindError(se[0], se[1])
     348            result = None
     349        if result and self.bindAddress is not None:
     350            try:
     351                skt.bind(self.bindAddress)
     352            except socket.error, se:
     353                err = error.ConnectBindError(se[0], se[1])
     354                result = None
     355        self._finishInit(result, skt, err, self.reactor)
    324356
    325357    def doConnect(self):
    326358        """I connect the socket.
     
    394426        # BaseClient.__init__ is invoked later
    395427        self.connector = connector
    396428        self.addr = (host, port)
     429        self.bindAddress = bindAddress
     430        self.reactor = reactor
    397431
    398         whenDone = self.resolveAddress
    399         err = None
    400         skt = None
    401 
    402         try:
    403             skt = self.createInternetSocket()
    404         except socket.error, se:
    405             err = error.ConnectBindError(se[0], se[1])
    406             whenDone = None
    407         if whenDone and bindAddress is not None:
    408             try:
    409                 skt.bind(bindAddress)
    410             except socket.error, se:
    411                 err = error.ConnectBindError(se[0], se[1])
    412                 whenDone = None
    413         self._finishInit(whenDone, skt, err, reactor)
     432        # Do outstanding initialization when real address is resolved
     433        self.resolveAddress()
    414434
    415435    def getHost(self):
    416         """Returns an IPv4Address.
     436        """
     437        Returns an L{IPv4Address} or L{IPv6Address}.
    417438
    418439        This indicates the address from which I am connecting.
    419440        """
    420         return address.IPv4Address('TCP', *self.socket.getsockname())
     441        return self._addressType('TCP', *self.socket.getsockname())
    421442
    422443    def getPeer(self):
    423         """Returns an IPv4Address.
     444        """
     445        Returns an L{IPv4Address} or L{IPv6Address}.
    424446
    425447        This indicates the address that I am connected to.
    426448        """
    427         return address.IPv4Address('TCP', *self.realAddress)
     449        return self._addressType('TCP', *self.realAddress)
    428450
    429451    def __repr__(self):
    430452        s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self))
     
    556578
    557579    def __repr__(self):
    558580        if self._realPortNumber is not None:
    559             return "<%s of %s on %s>" % (self.__class__, self.factory.__class__,
    560                                          self._realPortNumber)
     581            return "<%s of %s on %s (%s)>" % (self.__class__, self.factory.__class__,
     582                                         self._realPortNumber, self._addressType)
    561583        else:
    562584            return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__)
    563585
     586
    564587    def createInternetSocket(self):
    565588        s = base.BasePort.createInternetSocket(self)
    566589        if platformType == "posix" and sys.platform != "cygwin":
     
    588611        # reflect what the OS actually assigned us.
    589612        self._realPortNumber = skt.getsockname()[1]
    590613
    591         log.msg("%s starting on %s" % (self.factory.__class__, self._realPortNumber))
     614        log.msg("%s starting on %s (%s)" % (self.factory.__class__, self._realPortNumber,
     615                                            self._addressType))
    592616
    593617        # The order of the next 6 lines is kind of bizarre.  If no one
    594618        # can explain it, perhaps we should re-arrange them.
     
    739763
    740764
    741765class Connector(base.BaseConnector):
     766    _addressType = address.IPv4Address
     767
    742768    def __init__(self, host, port, factory, timeout, bindAddress, reactor=None):
    743         self.host = host
    744769        if isinstance(port, types.StringTypes):
    745770            try:
    746771                port = socket.getservbyname(port, 'tcp')
    747772            except socket.error, e:
    748773                raise error.ServiceNameUnknownError(string="%s (%r)" % (e, port))
     774       
     775        self.host, self.port = host, port
     776       
     777        if abstract.isIPv6Address(host):
     778            self._addressType = address.IPv6Address
     779        elif not abstract.isIPAddress(host):
     780            # do a host lookup to make sure we have the correct address family
     781            try:
     782                addressInfo = socket.getaddrinfo(host, port)
     783            except socket.gaierror:
     784                raise error.DNSLookupError(host)
     785            else:
     786                assert len(addressInfo) > 0
     787
     788                # Sort addressInfo. IPv4 addresses should be preferred over
     789                # IPv6 addresses to keep legacy applications working.
     790                addressInfo = sorted(addressInfo, key=lambda fields: fields[0])
     791             
     792                if addressInfo[0][0] == socket.AF_INET6:
     793                    self._addressType = address.IPv6Address
     794
     795                host, port = addressInfo[0][4][:2]
     796       
     797        self.host = host
    749798        self.port = port
    750799        self.bindAddress = bindAddress
    751800        base.BaseConnector.__init__(self, factory, timeout, reactor)
     
    754803        return Client(self.host, self.port, self.bindAddress, self, self.reactor)
    755804
    756805    def getDestination(self):
    757         return address.IPv4Address('TCP', self.host, self.port)
     806        return self._addressType('TCP', self.host, self.port)
  • twisted/internet/test/test_tcp.py

    diff -ru ../listentcp-ipv6-5084-3/twisted/internet/test/test_tcp.py ./twisted/internet/test/test_tcp.py
    old new  
    2020    IResolverSimple, IConnector, IReactorFDSet)
    2121from twisted.internet.address import IPv4Address, IPv6Address
    2222from twisted.internet.defer import Deferred, DeferredList, succeed, fail, maybeDeferred
    23 from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint
     23from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint, TCP6ServerEndpoint, TCP6ClientEndpoint
    2424from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol
    2525from twisted.python.failure import Failure
    2626from twisted.python import log
     
    114114    """
    115115    def __init__(self, reactor):
    116116        self.reactor = reactor
    117 
    118 
     117   
     118   
    119119    def clientConnectionFailed(self, connector, reason):
    120120        self.reactor.stop()
    121121
     
    605605
    606606
    607607
     608class TCP6ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin):
     609    """
     610    Builder defining tests relating to L{IReactorTCP.connectTCP}, IPv6 version.
     611    """
     612    def serverEndpoint(self, reactor):
     613        """
     614        Create a L{TCP6ServerEndpoint} listening on localhost on a
     615        TCP/IP-selected port.
     616        """
     617        return TCP6ServerEndpoint(reactor, 0, interface='::1')
     618
     619
     620    def clientEndpoint(self, reactor, serverAddress):
     621        """
     622        Create a L{TCP6ClientEndpoint} which will connect to localhost
     623        on the port given by C{serverAddress}.
     624
     625        @type serverAddress: L{IPv6Address}
     626        """
     627        return TCP6ClientEndpoint(reactor, '::1', serverAddress.port)
     628
     629
     630    def test_interface(self):
     631        """
     632        L{IReactorTCP.connectTCP} returns an object providing L{IConnector}.
     633        """
     634        reactor = self.buildReactor()
     635        connector = reactor.connectTCP("::1", 1234, ClientFactory())
     636        self.assertTrue(verifyObject(IConnector, connector))
     637
     638
     639    def test_clientConnectionFailedStopsReactor(self):
     640        """
     641        The reactor can be stopped by a client factory's
     642        C{clientConnectionFailed} method.
     643        """
     644        host, port = findFreePort("::1", socket.AF_INET6)[:2]
     645        reactor = self.buildReactor()
     646        reactor.connectTCP(host, port, Stop(reactor))
     647        self.runReactor(reactor)
     648
     649
     650    def test_addresses(self):
     651        """
     652        A client's transport's C{getHost} and C{getPeer} return L{IPv6Address}
     653        instances which give the hexadecimal formatted string form of the local
     654        and remote endpoints of the connection respectively.
     655        """
     656        host, port = findFreePort("::1", socket.AF_INET6)[:2]
     657        reactor = self.buildReactor()
     658
     659        server = reactor.listenTCP(
     660            0, serverFactoryFor(Protocol), interface=host)
     661        serverAddress = server.getHost()
     662
     663        addresses = {'host': None, 'peer': None}
     664        class CheckAddress(Protocol):
     665            def makeConnection(self, transport):
     666                addresses['host'] = transport.getHost()
     667                addresses['peer'] = transport.getPeer()
     668                reactor.stop()
     669
     670        clientFactory = Stop(reactor)
     671        clientFactory.protocol = CheckAddress
     672        reactor.connectTCP(
     673            '::1', server.getHost().port, clientFactory,
     674            bindAddress=('::1', port))
     675
     676        self.runReactor(reactor)
     677
     678        self.assertEqual(
     679            addresses['host'],
     680            IPv6Address('TCP', '::1', port))
     681        self.assertEqual(
     682            addresses['peer'],
     683            IPv6Address('TCP', '::1', serverAddress.port))
     684
     685
     686
    608687class StreamTransportTestsMixin:
    609688    """
    610689    Mixin defining tests which apply to any port/connection based transport.
     
    10241103
    10251104
    10261105globals().update(TCPClientTestsBuilder.makeTestCaseClasses())
     1106globals().update(TCP6ClientTestsBuilder.makeTestCaseClasses())
    10271107globals().update(TCPPortTestsBuilder.makeTestCaseClasses())
    10281108globals().update(TCPConnectionTestsBuilder.makeTestCaseClasses())