255
256
clientF = MyClientFactory()
256
257
# XXX we assume no one is listening on TCP port 69
257
258
reactor.connectTCP("127.0.0.1", 69, clientF, timeout=5)
259
259
def check(ignored):
260
260
clientF.reason.trap(error.ConnectionRefusedError)
261
261
return clientF.failDeferred.addCallback(check)
262
#self.assert_(time.time() - start < 0.1)
264
def test_connectionRefusedErrorNumber(self):
266
Assert that the error number of the ConnectionRefusedError is
267
ECONNREFUSED, and not some other socket related error.
270
# Bind a number of ports in the operating system. We will attempt
271
# to connect to these in turn immediately after closing them, in the
272
# hopes that no one else has bound them in the mean time. Any
273
# connection which succeeds is ignored and causes us to move on to
274
# the next port. As soon as a connection attempt fails, we move on
275
# to making an assertion about how it failed. If they all succeed,
276
# the test will fail.
278
# It would be nice to have a simpler, reliable way to cause a
279
# connection failure from the platform.
281
# On Linux (2.6.15), connecting to port 0 always fails. FreeBSD
282
# (5.4) rejects the connection attempt with EADDRNOTAVAIL.
284
# On FreeBSD (5.4), listening on a port and then repeatedly
285
# connecting to it without ever accepting any connections eventually
286
# leads to an ECONNREFUSED. On Linux (2.6.15), a seemingly
287
# unbounded number of connections succeed.
291
serverSocket = socket.socket()
292
serverSocket.bind(('127.0.0.1', 0))
293
serverSocket.listen(1)
294
serverSockets.append(serverSocket)
295
random.shuffle(serverSockets)
297
clientCreator = protocol.ClientCreator(reactor, protocol.Protocol)
299
def tryConnectFailure():
300
def connected(proto):
302
Darn. Kill it and try again, if there are any tries left.
304
proto.transport.loseConnection()
306
return tryConnectFailure()
307
self.fail("Could not fail to connect - could not test errno for that case.")
309
serverSocket = serverSockets.pop()
310
serverHost, serverPort = serverSocket.getsockname()
313
connectDeferred = clientCreator.connectTCP(serverHost, serverPort)
314
connectDeferred.addCallback(connected)
315
return connectDeferred
317
refusedDeferred = tryConnectFailure()
318
self.assertFailure(refusedDeferred, error.ConnectionRefusedError)
319
def connRefused(exc):
320
self.assertEqual(exc.osError, errno.ECONNREFUSED)
321
refusedDeferred.addCallback(connRefused)
322
def cleanup(passthrough):
324
serverSockets.pop().close()
326
refusedDeferred.addBoth(cleanup)
327
return refusedDeferred
264
330
def testConnectByServiceFail(self):
687
755
def connectionLost(self, reason):
688
756
self.d.callback(True)
690
class ProperlyCloseFilesTestCase(PortCleanerUpper):
696
PortCleanerUpper.setUp(self)
697
self.serverConns = []
698
f = protocol.ServerFactory()
699
f.protocol = NoopProtocol
700
f.protocol.master = self
702
self.listener = reactor.listenTCP(0, f, interface="127.0.0.1")
703
self.ports.append(self.listener)
705
self.clientF = f = protocol.ClientFactory()
706
f.protocol = ConnectionLosingProtocol
707
f.protocol.master = self
710
p = self.listener.getHost().port
711
return reactor.connectTCP('127.0.0.1', p, f)
712
self.connector = connector
714
self.totalConnections = 0
717
# Wait until all the protocols on the server-side of this test have
718
# been disconnected, to avoid leaving junk in the reactor.
719
d = defer.gatherResults(self.serverConns)
720
d.addBoth(lambda x : PortCleanerUpper.tearDown(self))
723
def testProperlyCloseFiles(self):
724
self.deferred = defer.Deferred()
726
self.deferred.addCallback(
727
lambda x : self.failUnlessEqual(self.totalConnections,
731
def _connectionMade(self):
732
self.totalConnections += 1
733
if self.totalConnections<self.numberRounds:
736
self.deferred.callback(None)
760
class ConnectionLostNotifyingProtocol(protocol.Protocol):
762
Protocol which fires a Deferred which was previously passed to
763
its initializer when the connection is lost.
765
def __init__(self, onConnectionLost):
766
self.onConnectionLost = onConnectionLost
769
def connectionLost(self, reason):
770
self.onConnectionLost.callback(self)
774
class HandleSavingProtocol(ConnectionLostNotifyingProtocol):
776
Protocol which grabs the platform-specific socket handle and
777
saves it as an attribute on itself when the connection is
780
def makeConnection(self, transport):
782
Save the platform-specific socket handle for future
785
self.handle = transport.getHandle()
786
return protocol.Protocol.makeConnection(self, transport)
790
class ProperlyCloseFilesMixin:
792
Tests for platform resources properly being cleaned up.
794
def createServer(self, address, portNumber, factory):
796
Bind a server port to which connections will be made. The server
797
should use the given protocol factory.
799
@return: The L{IListeningPort} for the server created.
801
raise NotImplementedError()
804
def connectClient(self, address, portNumber, clientCreator):
806
Establish a connection to the given address using the given
807
L{ClientCreator} instance.
809
@return: A Deferred which will fire with the connected protocol instance.
811
raise NotImplementedError()
814
def getHandleExceptionType(self):
816
Return the exception class which will be raised when an operation is
817
attempted on a closed platform handle.
819
raise NotImplementedError()
822
def getHandleErrorCode(self):
824
Return the errno expected to result from writing to a closed
825
platform socket handle.
827
# These platforms have been seen to give EBADF:
829
# Linux 2.4.26, Linux 2.6.15, OS X 10.4, FreeBSD 5.4
830
# Windows 2000 SP 4, Windows XP SP 2
834
def test_properlyCloseFiles(self):
836
Test that lost connections properly have their underlying socket
837
resources cleaned up.
839
onServerConnectionLost = defer.Deferred()
840
serverFactory = protocol.ServerFactory()
841
serverFactory.protocol = lambda: ConnectionLostNotifyingProtocol(
842
onServerConnectionLost)
843
serverPort = self.createServer('127.0.0.1', 0, serverFactory)
845
onClientConnectionLost = defer.Deferred()
846
serverAddr = serverPort.getHost()
847
clientCreator = protocol.ClientCreator(
848
reactor, lambda: HandleSavingProtocol(onClientConnectionLost))
849
clientDeferred = self.connectClient(
850
serverAddr.host, serverAddr.port, clientCreator)
852
def clientConnected(client):
854
Disconnect the client. Return a Deferred which fires when both
855
the client and the server have received disconnect notification.
857
client.transport.loseConnection()
858
return defer.gatherResults([
859
onClientConnectionLost, onServerConnectionLost])
860
clientDeferred.addCallback(clientConnected)
862
def clientDisconnected((client, server)):
864
Verify that the underlying platform socket handle has been
867
expectedErrorCode = self.getHandleErrorCode()
868
err = self.assertRaises(
869
self.getHandleExceptionType(), client.handle.send, 'bytes')
870
self.assertEqual(err.args[0], expectedErrorCode)
871
clientDeferred.addCallback(clientDisconnected)
873
def cleanup(passthrough):
875
Shut down the server port. Return a Deferred which fires when
878
result = defer.maybeDeferred(serverPort.stopListening)
879
result.addCallback(lambda ign: passthrough)
881
clientDeferred.addBoth(cleanup)
883
return clientDeferred
887
class ProperlyCloseFilesTestCase(unittest.TestCase, ProperlyCloseFilesMixin):
888
def createServer(self, address, portNumber, factory):
889
return reactor.listenTCP(portNumber, factory, interface=address)
892
def connectClient(self, address, portNumber, clientCreator):
893
return clientCreator.connectTCP(address, portNumber)
896
def getHandleExceptionType(self):
740
900
class AProtocol(protocol.Protocol):
1071
1231
self.client.transport.loseConnection()
1072
1232
return self.p.stopListening()
1074
def _delayDeferred(self, time, arg=None):
1075
from twisted.internet import reactor
1076
d = defer.Deferred()
1077
reactor.callLater(time, d.callback, arg)
1080
1234
def testNoNotification(self):
1081
client = self.client
1083
client.transport.write("hello")
1084
w = client.transport.write
1085
client.transport.loseWriteConnection()
1086
d = self._delayDeferred(0.2, f.protocol)
1087
d.addCallback(lambda x : self.assertEqual(f.protocol.data, 'hello'))
1088
d.addCallback(lambda x : self.assertEqual(f.protocol.closed, True))
1236
TCP protocols support half-close connections, but not all of them
1237
support being notified of write closes. In this case, test that
1238
half-closing the connection causes the peer's connection to be
1241
self.client.transport.write("hello")
1242
self.client.transport.loseWriteConnection()
1243
self.f.protocol.closedDeferred = d = defer.Deferred()
1244
self.client.closedDeferred = d2 = defer.Deferred()
1245
d.addCallback(lambda x:
1246
self.assertEqual(self.f.protocol.data, 'hello'))
1247
d.addCallback(lambda x: self.assertEqual(self.f.protocol.closed, True))
1248
return defer.gatherResults([d, d2])
1091
1250
def testShutdownException(self):
1092
client = self.client
1094
f.protocol.transport.loseConnection()
1095
client.transport.write("X")
1096
client.transport.loseWriteConnection()
1097
d = self._delayDeferred(0.2, f.protocol)
1098
d.addCallback(lambda x : self.failUnlessEqual(x.closed, True))
1252
If the other side has already closed its connection,
1253
loseWriteConnection should pass silently.
1255
self.f.protocol.transport.loseConnection()
1256
self.client.transport.write("X")
1257
self.client.transport.loseWriteConnection()
1258
self.f.protocol.closedDeferred = d = defer.Deferred()
1259
self.client.closedDeferred = d2 = defer.Deferred()
1260
d.addCallback(lambda x:
1261
self.failUnlessEqual(self.f.protocol.closed, True))
1262
return defer.gatherResults([d, d2])
1101
1265
class HalfClose3TestCase(PortCleanerUpper):
1102
1266
"""Test half-closing connections where notification code has bugs."""