[Twisted-Python] How can I disconnect ssl connection from the client side?
![](https://secure.gravatar.com/avatar/7021ad555305a5cea648512704c4b2c0.jpg?s=120&d=mm&r=g)
Hello, I'm writing a smtp-client application with twisted.mail. Testing the application, I noticed that timeout doesn't work in some case if client started starttls session. If smtp-server stops responding after DATA command was sent from client, calling transport.loseConnection() doesn't close connection. I found ticket # 2827 reports connector.disconnect() doesn't work for ssl connection if the server doesn't send any data. Is there a workaround to disconnect ssl connection? Regards,
![](https://secure.gravatar.com/avatar/7021ad555305a5cea648512704c4b2c0.jpg?s=120&d=mm&r=g)
Investigating further, I found this is not SSL related problem, but in my code may have bug. In the following script, I expect that reactor stops in 2 secs after it started, but connectionLost() is not fired at all. Is this a bug in Twisted? Or Am I missing something? ========================================= from twisted.protocols import basic from twisted.internet import protocol, reactor serverfactory = protocol.Factory() serverfactory.protocol = basic.LineOnlyReceiver port = reactor.listenTCP(9084, serverfactory, interface='127.0.0.1') CANCELED = False class Client(basic.LineReceiver): def connectionMade(self): class dmyfile: def read(self, n): if CANCELED: return else: return '1' s = basic.FileSender() print "start sending" def f1(lastChunk): print "finished" def f2(reason): print "failed" print reason s.beginFileTransfer( dmyfile(), self.transport, None ).addCallbacks(f1, f2) def connectionLost(self, conn, reason=protocol.connectionDone): print "protocol:connectionlost" class Factory(protocol.ClientFactory): protocol = Client def clientConnectionLost(self, conn, reason): print "clientConnectionLost" reactor.stop() conn = reactor.connectTCP('127.0.0.1', 9084, Factory()) def cancel(): print "cancel", conn.state global CANCELED CANCELED = True conn.disconnect() reactor.callLater(2, cancel) # disconnect 2 sec later reactor.run()
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 2 Feb 2009 19:48:50 +0900, Atsuo Ishimoto <ishimoto@gembook.org> wrote:
This does look like a bug in Twisted. Could you file a ticket for it? In the mean time, you can work around this by using a simpler cancel technique. Get rid of the "conn.disconnect()" call in cancel and instead call the protocol's transport's loseConnection method in f1 and f2 if CANCEL is set. Jean-Paul
![](https://secure.gravatar.com/avatar/3f8b0ee4e56cebdb9430ed8d08e7b7c0.jpg?s=120&d=mm&r=g)
Atsuo Ishimoto wrote: ...
...
I think the problem is that you call conn.disconnect before the FileSender has a chance to call unregisterProducer on the transport (in real life before it has sent the whole file). If the producer is not unregistered, the doWrite() method called from the reactor doesn't even check if the connection is in 'disconnecting' state, assumes there's more data to come, and since no further events occur on the channel, the connection just remains open. You can check that by manually writing one more byte to the transport in f1, after FileSender has unregistered itself. The correct way is to call conn.disconnect()/transport.loseConnection() in f1 instead of in cancel. from t.i.abstract.FileDescriptor.loseConnection.__doc__: "... If you have a producer registered, the connection won't be closed until the producer is finished. Therefore, make sure you unregister your producer when it's finished, or the connection willnever close ..." hth, Johann
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 02 Feb 2009 14:45:40 +0100, Johann Borck <johann.borck@densedata.com> wrote:
It's "correct" in that it works around the bug in Twisted. However, this *is* a bug in Twisted which should be fixed. Then it will be just as correct to do it one way as the other. Jean-Paul
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 02 Feb 2009 16:15:16 +0100, Johann Borck <johann.borck@densedata.com> wrote:
Since that contradicts the documentation, I don't know why you'd say it. Are you trying to make some point other than that you think the documented behavior is bad? Jean-Paul
![](https://secure.gravatar.com/avatar/7021ad555305a5cea648512704c4b2c0.jpg?s=120&d=mm&r=g)
Investigating further, I found this is not SSL related problem, but in my code may have bug. In the following script, I expect that reactor stops in 2 secs after it started, but connectionLost() is not fired at all. Is this a bug in Twisted? Or Am I missing something? ========================================= from twisted.protocols import basic from twisted.internet import protocol, reactor serverfactory = protocol.Factory() serverfactory.protocol = basic.LineOnlyReceiver port = reactor.listenTCP(9084, serverfactory, interface='127.0.0.1') CANCELED = False class Client(basic.LineReceiver): def connectionMade(self): class dmyfile: def read(self, n): if CANCELED: return else: return '1' s = basic.FileSender() print "start sending" def f1(lastChunk): print "finished" def f2(reason): print "failed" print reason s.beginFileTransfer( dmyfile(), self.transport, None ).addCallbacks(f1, f2) def connectionLost(self, conn, reason=protocol.connectionDone): print "protocol:connectionlost" class Factory(protocol.ClientFactory): protocol = Client def clientConnectionLost(self, conn, reason): print "clientConnectionLost" reactor.stop() conn = reactor.connectTCP('127.0.0.1', 9084, Factory()) def cancel(): print "cancel", conn.state global CANCELED CANCELED = True conn.disconnect() reactor.callLater(2, cancel) # disconnect 2 sec later reactor.run()
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 2 Feb 2009 19:48:50 +0900, Atsuo Ishimoto <ishimoto@gembook.org> wrote:
This does look like a bug in Twisted. Could you file a ticket for it? In the mean time, you can work around this by using a simpler cancel technique. Get rid of the "conn.disconnect()" call in cancel and instead call the protocol's transport's loseConnection method in f1 and f2 if CANCEL is set. Jean-Paul
![](https://secure.gravatar.com/avatar/3f8b0ee4e56cebdb9430ed8d08e7b7c0.jpg?s=120&d=mm&r=g)
Atsuo Ishimoto wrote: ...
...
I think the problem is that you call conn.disconnect before the FileSender has a chance to call unregisterProducer on the transport (in real life before it has sent the whole file). If the producer is not unregistered, the doWrite() method called from the reactor doesn't even check if the connection is in 'disconnecting' state, assumes there's more data to come, and since no further events occur on the channel, the connection just remains open. You can check that by manually writing one more byte to the transport in f1, after FileSender has unregistered itself. The correct way is to call conn.disconnect()/transport.loseConnection() in f1 instead of in cancel. from t.i.abstract.FileDescriptor.loseConnection.__doc__: "... If you have a producer registered, the connection won't be closed until the producer is finished. Therefore, make sure you unregister your producer when it's finished, or the connection willnever close ..." hth, Johann
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 02 Feb 2009 14:45:40 +0100, Johann Borck <johann.borck@densedata.com> wrote:
It's "correct" in that it works around the bug in Twisted. However, this *is* a bug in Twisted which should be fixed. Then it will be just as correct to do it one way as the other. Jean-Paul
![](https://secure.gravatar.com/avatar/7ed9784cbb1ba1ef75454034b3a8e6a1.jpg?s=120&d=mm&r=g)
On Mon, 02 Feb 2009 16:15:16 +0100, Johann Borck <johann.borck@densedata.com> wrote:
Since that contradicts the documentation, I don't know why you'd say it. Are you trying to make some point other than that you think the documented behavior is bad? Jean-Paul
participants (3)
-
Atsuo Ishimoto
-
Jean-Paul Calderone
-
Johann Borck