Ticket #5085: connecttcp-ipv6-5085-1.patch
| File connecttcp-ipv6-5085-1.patch, 43.5 KB (added by ragzilla, 17 months ago) |
|---|
-
twisted/internet/address.py
diff --git twisted/internet/address.py twisted/internet/address.py index b9192d4..8e5a483 100644
19 19 20 20 @ivar type: A string describing the type of transport, either 'TCP' or 21 21 'UDP'. 22 @ivar host: A string containing the dotted-quadIP address.22 @ivar host: A string containing the native IP address. 23 23 @ivar port: An integer representing the port number. 24 24 """ 25 25 … … 43 43 return hash((self.type, self.host, self.port)) 44 44 45 45 46 46 47 47 class IPv4Address(_IPAddress): 48 48 """ 49 49 Object representing an IPv4 socket endpoint. 50 51 @ivar host: A string containing the dotted-quad IPv4 address. 50 52 """ 51 53 def __init__(self, type, host, port, _bwHack=None): 52 54 _IPAddress.__init__(self, type, host, port) … … 59 61 class IPv6Address(_IPAddress): 60 62 """ 61 63 Object representing an IPv6 socket endpoint. 64 65 @ivar host: A string containing the hexadecimal formatted IPv6 address. 62 66 """ 67 pass 63 68 64 69 65 70 … … 85 90 if getattr(os.path, 'samefile', None) is not None: 86 91 def __eq__(self, other): 87 92 """ 88 overriding L{util.FancyEqMixin} to ensure the os level samefile check89 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. 90 95 """ 91 96 res = super(UNIXAddress, self).__eq__(other) 92 97 if res == False: -
twisted/internet/endpoints.py
diff --git twisted/internet/endpoints.py twisted/internet/endpoints.py index 79d7672..e80e1cc 100644
25 25 26 26 __all__ = ["clientFromString", "serverFromString", 27 27 "TCP4ServerEndpoint", "TCP4ClientEndpoint", 28 "TCP6ServerEndpoint", "TCP6ClientEndpoint", 28 29 "UNIXServerEndpoint", "UNIXClientEndpoint", 29 30 "SSL4ServerEndpoint", "SSL4ClientEndpoint"] 30 31 … … 169 170 170 171 171 172 172 class TCP4ServerEndpoint(object):173 class _TCPServerEndpoint(object): 173 174 """ 174 TCP server endpoint with a n IPv4configuration175 TCP server endpoint with a default (IPv4) configuration 175 176 176 177 @ivar _reactor: An L{IReactorTCP} provider. 177 178 … … 212 213 213 214 214 215 215 class TCP4ClientEndpoint(object): 216 class 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 231 class 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 247 class _TCPClientEndpoint(object): 216 248 """ 217 TCP client endpoint with a n IPv4configuration.249 TCP client endpoint with a default (IPv4) configuration. 218 250 219 251 @ivar _reactor: An L{IReactorTCP} provider. 220 252 … … 271 303 272 304 273 305 306 class TCP4ClientEndpoint(_TCPClientEndpoint): 307 """ 308 TCP client endpoint with an IPv4 configuration 309 """ 310 pass 311 312 313 314 class TCP6ClientEndpoint(_TCPClientEndpoint): 315 """ 316 TCP client endpoint with an IPv6 configuration 317 """ 318 pass 319 320 321 274 322 class SSL4ServerEndpoint(object): 275 323 """ 276 324 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
320 320 else: 321 321 reactor.callLater(0, self.failIfNotConnected, error) 322 322 323 323 324 def stopConnecting(self): 324 325 """Stop attempt to connect.""" 325 326 self.failIfNotConnected(error.UserError()) 326 327 328 327 329 def failIfNotConnected(self, err): 328 330 """ 329 331 Generic method called when the attemps to connect failed. It basically … … 348 350 else: 349 351 del self.socket, self.fileno 350 352 353 351 354 def createInternetSocket(self): 352 355 """(internal) Create a non-blocking socket using 353 356 self.addressFamily, self.socketType. … … 357 360 fdesc._setCloseOnExec(s.fileno()) 358 361 return s 359 362 363 360 364 def resolveAddress(self): 361 365 if abstract.isIPAddress(self.addr[0]): 362 366 self._setRealAddress(self.addr[0]) … … 364 368 d = self.reactor.resolve(self.addr[0]) 365 369 d.addCallbacks(self._setRealAddress, self.failIfNotConnected) 366 370 371 367 372 def _setRealAddress(self, address): 368 373 self.realAddress = (address, self.addr[1]) 369 374 self.doConnect() 370 375 376 371 377 def doConnect(self): 372 378 """I connect the socket. 373 379 … … 383 389 self.failIfNotConnected(error.getConnectError((err, strerror(err)))) 384 390 return 385 391 386 387 392 # doConnect gets called twice. The first time we actually need to 388 393 # start the connection attempt. The second time we don't really 389 394 # want to (SO_ERROR above will have taken care of any errors, and if … … 418 423 self.stopWriting() 419 424 self._connectDone() 420 425 426 421 427 def _connectDone(self): 422 428 self.protocol = self.connector.buildProtocol(self.getPeer()) 423 429 self.connected = 1 … … 426 432 self.startReading() 427 433 self.protocol.makeConnection(self) 428 434 435 429 436 def connectionLost(self, reason): 430 437 if not self.connected: 431 438 self.failIfNotConnected(error.ConnectError(string=reason)) … … 434 441 self.connector.connectionLost(reason) 435 442 436 443 444 437 445 class 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 439 454 440 455 def __init__(self, host, port, bindAddress, connector, reactor=None): 441 456 # BaseClient.__init__ is invoked later 442 457 self.connector = connector 443 458 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 444 472 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 """ 446 494 err = None 447 495 skt = None 448 496 result = True 497 449 498 try: 450 499 skt = self.createInternetSocket() 451 500 except socket.error, se: 452 501 err = error.ConnectBindError(se.args[0], se.args[1]) 453 whenDone= None454 if whenDone andbindAddress is not None:502 result = None 503 if result and self.bindAddress is not None: 455 504 try: 456 skt.bind( bindAddress)505 skt.bind(self.bindAddress) 457 506 except socket.error, se: 458 507 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 461 517 462 518 def getHost(self): 463 """Returns an IPv4Address.519 """Returns an L{IPv4Address} or L{IPv6Address}. 464 520 465 521 This indicates the address from which I am connecting. 466 522 """ 467 return address.IPv4Address('TCP', *self.socket.getsockname()) 523 return self._addressType('TCP', *self.socket.getsockname()[:2]) 524 468 525 469 526 def getPeer(self): 470 """Returns an IPv4Address.527 """Returns an L{IPv4Address} or L{IPv6Address}. 471 528 472 529 This indicates the address that I am connected to. 473 530 """ 474 return address.IPv4Address('TCP', *self.realAddress) 531 return self._addressType('TCP', *self.realAddress) 532 475 533 476 534 def __repr__(self): 477 535 s = '<%s to %s at %x>' % (self.__class__, self.addr, unsignedID(self)) 478 536 return s 479 537 480 538 539 481 540 class Server(_TLSServerMixin, Connection): 482 541 """ 483 542 Serverside socket-stream connection class. … … 612 671 613 672 def __repr__(self): 614 673 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) 617 677 else: 618 678 return "<%s of %s (not listening)>" % (self.__class__, self.factory.__class__) 619 679 680 620 681 def createInternetSocket(self): 621 682 s = base.BasePort.createInternetSocket(self) 622 683 if platformType == "posix" and sys.platform != "cygwin": … … 644 705 # reflect what the OS actually assigned us. 645 706 self._realPortNumber = skt.getsockname()[1] 646 707 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)) 649 711 650 712 # The order of the next 6 lines is kind of bizarre. If no one 651 713 # can explain it, perhaps we should re-arrange them. … … 795 857 796 858 797 859 class Connector(base.BaseConnector): 860 _addressType = address.IPv4Address 861 798 862 def __init__(self, host, port, factory, timeout, bindAddress, reactor=None): 799 self.host = host800 863 if isinstance(port, types.StringTypes): 801 864 try: 802 865 port = socket.getservbyname(port, 'tcp') 803 866 except socket.error, e: 804 867 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 806 871 self.bindAddress = bindAddress 807 872 base.BaseConnector.__init__(self, factory, timeout, reactor) 808 873 … … 810 875 return Client(self.host, self.port, self.bindAddress, self, self.reactor) 811 876 812 877 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
5 5 Various helpers for tests for connection-oriented transports. 6 6 """ 7 7 8 import socket 9 8 10 from gc import collect 9 11 from weakref import ref 10 12 13 from zope.interface import implements 14 from zope.interface.verify import verifyObject 15 11 16 from twisted.python import context, log 17 from twisted.python.failure import Failure 12 18 from twisted.python.reflect import fullyQualifiedName 13 19 from twisted.python.log import ILogContext, msg, err 14 from twisted.internet.defer import Deferred, gatherResults 15 from twisted.internet.protocol import ServerFactory, Protocol 20 from twisted.internet.defer import Deferred, gatherResults, succeed 21 from twisted.internet.interfaces import ( 22 IConnector, IResolverSimple, IReactorFDSet) 23 from twisted.internet.protocol import ClientFactory, Protocol, ServerFactory 24 from twisted.test.test_tcp import ClosingProtocol 16 25 17 26 def serverFactoryFor(protocol): 18 27 """ … … 29 38 # ServerFactory is good enough for client endpoints, too. 30 39 factoryFor = serverFactoryFor 31 40 41 42 43 def 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 66 def _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 32 80 class _AcceptOneClient(ServerFactory): 33 81 """ 34 82 This factory fires a L{Deferred} with a protocol instance shortly after it … … 50 98 51 99 52 100 101 class _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 120 class 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 133 class 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 53 151 class ClosingLaterProtocol(Protocol): 54 152 """ 55 153 ClosingLaterProtocol exchanges one byte with its peer and then disconnects … … 254 352 log.addObserver(loggedMessages.append) 255 353 self.addCleanup(log.removeObserver, loggedMessages.append) 256 354 return loggedMessages 355 356 357 358 class 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
25 25 from twisted.internet.address import IPv4Address, IPv6Address 26 26 from twisted.internet.defer import ( 27 27 Deferred, DeferredList, succeed, fail, maybeDeferred, gatherResults) 28 from twisted.internet.endpoints import TCP4ServerEndpoint, TCP4ClientEndpoint 28 from twisted.internet.endpoints import ( 29 TCP4ServerEndpoint, TCP4ClientEndpoint, 30 TCP6ServerEndpoint, TCP6ClientEndpoint) 29 31 from twisted.internet.protocol import ServerFactory, ClientFactory, Protocol 30 32 from twisted.internet.interfaces import ( 31 33 IPushProducer, IPullProducer, IHalfCloseableProtocol) … … 33 35 from twisted.internet.tcp import Connection, Server 34 36 35 37 from twisted.internet.test.connectionmixins import ( 36 LogObserverMixin, ConnectionTestsMixin, serverFactoryFor) 38 LogObserverMixin, ConnectionTestsMixin, serverFactoryFor, 39 TCPClientTestsMixin, findFreePort, Stop, FakeResolver) 37 40 from twisted.internet.test.test_core import ObjectModelIntegrationMixin 38 41 from twisted.test.test_tcp import MyClientFactory, MyServerFactory 39 42 from twisted.test.test_tcp import ClosingProtocol … … 82 85 83 86 84 87 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 by97 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 108 88 def connect(client, (host, port)): 109 if '%' in host :89 if '%' in host or ':' in host: 110 90 address = socket.getaddrinfo(host, port)[0][4] 111 91 else: 112 92 address = (host, port) … … 114 94 115 95 116 96 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 = reactor123 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 = names138 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 by151 L{test_unregisterProducerAfterDisconnect}.152 """153 def __init__(self, consumer):154 self.consumer = consumer155 156 157 def stopProducing(self):158 pass159 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.handles175 else:176 # Cannot tell what is going on.177 raise Exception("Cannot find writers on %r" % (reactor,))178 179 180 181 97 class FakeSocket(object): 182 98 """ 183 99 A fake for L{socket.socket} objects. … … 399 315 test_tlsAfterStartTLS.skip = "No SSL support available" 400 316 401 317 402 class TCPClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin): 318 class TCP4ClientTestsBuilder(ReactorBuilder, ConnectionTestsMixin, 319 TCPClientTestsMixin): 403 320 """ 404 321 Builder defining tests relating to L{IReactorTCP.connectTCP}. 405 322 """ 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 406 329 def serverEndpoint(self, reactor): 407 330 """ 408 331 Create a L{TCP4ServerEndpoint} listening on localhost on a 409 332 TCP/IP-selected port. 410 333 """ 411 return TCP4ServerEndpoint(reactor, 0, interface= '127.0.0.1')334 return TCP4ServerEndpoint(reactor, 0, interface=self.interface) 412 335 413 336 414 337 def clientEndpoint(self, reactor, serverAddress): … … 419 342 @type serverAddress: L{IPv4Address} 420 343 """ 421 344 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) 443 346 444 347 445 348 def test_addresses(self): … … 448 351 instances which give the dotted-quad string form of the local and 449 352 remote endpoints of the connection respectively. 450 353 """ 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) 464 355 465 clientFactory = Stop(reactor)466 clientFactory.protocol = CheckAddress467 reactor.connectTCP(468 'localhost', server.getHost().port, clientFactory,469 bindAddress=('127.0.0.1', port))470 356 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))480 357 358 class 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 481 368 482 def test_connectEvent(self):369 def serverEndpoint(self, reactor): 483 370 """ 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. 487 373 """ 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) 504 375 505 self.assertTrue(connected)506 376 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): 515 378 """ 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}. 536 381 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) 553 386 554 387 555 def test_ disconnectWhileProducing(self):388 def test_addresses(self): 556 389 """ 557 If L{ITCPTransport.loseConnection} is called while a producer558 i s registered with the transport, the connection is closed559 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. 560 393 """ 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) 620 395 621 396 622 397 … … 699 474 """ 700 475 Get the message expected to be logged when a TCP port starts listening. 701 476 """ 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) 703 479 704 480 705 481 def getExpectedConnectionLostLogMsg(self, port): … … 1385 1161 self.assertEquals(producer.actions, ["resume", "resume"]) 1386 1162 1387 1163 1388 globals().update(TCPClientTestsBuilder.makeTestCaseClasses()) 1164 globals().update(TCP4ClientTestsBuilder.makeTestCaseClasses()) 1165 globals().update(TCP6ClientTestsBuilder.makeTestCaseClasses()) 1389 1166 globals().update(TCPPortTestsBuilder.makeTestCaseClasses()) 1390 1167 globals().update(TCPConnectionTestsBuilder.makeTestCaseClasses()) 1391 1168 globals().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
273 273 """ 274 274 Get the message expected to be logged when a TLS port starts listening. 275 275 """ 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) 277 277 278 278 279 279 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
678 678 679 679 680 680 class 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" 681 689 682 690 def test_connectorIdentity(self): 683 691 """ … … 688 696 C{clientConnectionLost} method. 689 697 """ 690 698 serverFactory = ClosingFactory() 691 tcpPort = reactor.listenTCP(0, serverFactory, interface= "127.0.0.1")699 tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 692 700 serverFactory.port = tcpPort 693 701 self.addCleanup(serverFactory.cleanUp) 694 702 portNumber = tcpPort.getHost().port … … 702 710 seenFailures.append(reason))) 703 711 clientFactory.startedConnecting = seenConnectors.append 704 712 705 connector = reactor.connectTCP( "127.0.0.1", portNumber, clientFactory)713 connector = reactor.connectTCP(self.interface, portNumber, clientFactory) 706 714 self.assertTrue(interfaces.IConnector.providedBy(connector)) 707 715 dest = connector.getDestination() 708 716 self.assertEqual(dest.type, "TCP") 709 self.assertEqual(dest.host, "127.0.0.1")717 self.assertEqual(dest.host, self.interface) 710 718 self.assertEqual(dest.port, portNumber) 711 719 712 720 d = loopUntil(lambda: clientFactory.stopped) … … 724 732 L{error.UserError} as the reason. 725 733 """ 726 734 serverFactory = MyServerFactory() 727 tcpPort = reactor.listenTCP(0, serverFactory, interface= "127.0.0.1")735 tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 728 736 self.addCleanup(tcpPort.stopListening) 729 737 portNumber = tcpPort.getHost().port 730 738 … … 733 741 734 742 clientFactory = ClientStartStopFactory() 735 743 clientFactory.startedConnecting = startedConnecting 736 reactor.connectTCP( "127.0.0.1", portNumber, clientFactory)744 reactor.connectTCP(self.interface, portNumber, clientFactory) 737 745 738 746 d = loopUntil(lambda: clientFactory.stopped) 739 747 def check(ignored): … … 748 756 a new connection attempt to be made. 749 757 """ 750 758 serverFactory = ClosingFactory() 751 tcpPort = reactor.listenTCP(0, serverFactory, interface= "127.0.0.1")759 tcpPort = reactor.listenTCP(0, serverFactory, interface=self.interface) 752 760 serverFactory.port = tcpPort 753 761 self.addCleanup(serverFactory.cleanUp) 754 762 portNumber = tcpPort.getHost().port … … 758 766 def clientConnectionLost(connector, reason): 759 767 connector.connect() 760 768 clientFactory.clientConnectionLost = clientConnectionLost 761 reactor.connectTCP( "127.0.0.1", portNumber, clientFactory)769 reactor.connectTCP(self.interface, portNumber, clientFactory) 762 770 763 771 d = loopUntil(lambda: clientFactory.failed) 764 772 def reconnectFailed(ignored): … … 770 778 771 779 772 780 781 class 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 773 793 class CannotBindTestCase(unittest.TestCase): 774 794 """ 775 795 Tests for correct behavior when a reactor cannot bind to the required TCP
