[Twisted-Python] How can I disconnect ssl connection from the client side?
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,
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()
On Mon, 2 Feb 2009 19:48:50 +0900, Atsuo Ishimoto <ishimoto@gembook.org> wrote:
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?
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
This does look like a bug in Twisted. Could you file a ticket for it?
Done. #3644.
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.
Thank you for your comment, Jean-Paul , Johann. Now my application working right!
Atsuo Ishimoto wrote: ...
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 cancel(): print "cancel", conn.state global CANCELED CANCELED = True conn.disconnect()
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
reactor.callLater(2, cancel) # disconnect 2 sec later reactor.run()
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
On Mon, 02 Feb 2009 14:45:40 +0100, Johann Borck <johann.borck@densedata.com> wrote:
[snip] The correct way is to call conn.disconnect()/transport.loseConnection() in f1 instead of in cancel.
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
Jean-Paul Calderone wrote:
On Mon, 02 Feb 2009 14:45:40 +0100, Johann Borck <johann.borck@densedata.com> wrote:
[snip] The correct way is to call conn.disconnect()/transport.loseConnection() in f1 instead of in cancel.
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. I personally find it counterintuitive to have an open connection after an explicit call to disconnect, but assumed it is intended behavior since it's explicitly mentioned in the docstring. However for the given example I'd still say the above is the only correct way unless Atsuo intends to break the connection before the transfer has finished.
Johann
On Mon, 02 Feb 2009 16:15:16 +0100, Johann Borck <johann.borck@densedata.com> wrote:
On Mon, 02 Feb 2009 14:45:40 +0100, Johann Borck <johann.borck@densedata.com> wrote:
[snip] The correct way is to call conn.disconnect()/transport.loseConnection() in f1 instead of in cancel.
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. I personally find it counterintuitive to have an open connection after an explicit call to disconnect, but assumed it is intended behavior since it's explicitly mentioned in the docstring. However for the given example I'd still say the above is the only correct way unless Atsuo intends to break
Jean-Paul Calderone wrote: the connection before the transfer has finished.
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