Twisted always immediately reports the connectionMade to the application-level protocol, so a TLS protocol's connectionMade means the same thing as a TCP protocol's connectionMade.
If you call self.transport.write on a TLS transport which has not yet succeeded at verifying its peer, the bytes will be buffered and delivered only when the handshake has succeeded. If the handshake fails the outgoing bytes will be discarded. The application can tell that the connection failed due to TLS negotiation failure as opposed to a TCP transport problem by inspecting the exception within the Failure passed as the "reason" to the connectionLost method. The full horrific menagerie of OpenSSL error codes are sadly exposed there, but the easiest rule of thumb is that it's a handshake problem if (A) it's OpenSSL.SSL.Error rather than twisted.internet.error.ConnectionLost, and (B) you haven't gotten any application-level data yet. Presently, there is no general hook for "TLS handshake succeeded". It's possible to do using OpenSSL APIs, although it's unpleasant. This is clearly an oversight, and we're working on it <https://twistedmatrix.com/trac/ticket/6024> (thanks for reminding me, I should probably contribute to that ticket) but it has rarely been a practical problem. From an application's perspective the only problem it can see is that getPeerCertificate returns None in connectionMade, but the only time you care about that is if you have peer-certificate-dependent data that you need to emit in a greeting (i.e. before they speak) to your peer, which is fairly unusual except in applications that need to do elaborate client-certificate verification. I've written exactly one application that needed that in the past decade, and I managed to hack around it using the aforementioned pyOpenSSL APIs with a minimum of queasiness. If you have control over the protocol, or if you're speaking something like HTTP where the client speaks first, you can always do certificate-dependent calculations in dataReceived, and dataReceived implicitly happens after the handshake is completed. So if you're going to skip either the "handshake succeeded" notification or the "connection established" notification though, "handshake succeeded" is definitely the one to skip. There are very, very few things that you actually want to do upon handshake completion and almost all of them can be accomplished in a different callback, whereas there are a bunch of resource-management things you want to do in connectionMade. Although our implementation isn't complete, I'm pretty confident that the "right" way to do this is simply to have a separate (optional) callback which is only invoked on protocols with a TLS transport. Probably rather than having the transport promise to store its peer certificate forever, you could just pass the certificate to this callback. -glyph P.S.: I am definitely going to put the quote below on a bumper sticker, with attribution ;-) > On Jan 28, 2015, at 6:56 PM, Guido van Rossum <[email protected]> wrote: > > What would Twisted do? (WWTD) > > On Wed, Jan 28, 2015 at 6:20 PM, Victor Stinner <[email protected] > <mailto:[email protected]>> wrote: > 2015-01-29 2:47 GMT+01:00 Guido van Rossum <[email protected] > <mailto:[email protected]>>: > > Doesn't the exception bubble up to the caller of create_connection()? > > Yes, it does. > > > So what's the problem? > > The problem is in server which accepts incoming connections: > BaseSelectorEventLoop._accept_conncetion(). Extract of the code: > --- > conn, addr = sock.accept() > (...) > protocol = protocol_factory() > if sslcontext: > self._make_ssl_transport( > conn, protocol, sslcontext, > server_side=True, extra={'peername': addr}, server=server) > else: > self._make_socket_transport( > conn, protocol , extra={'peername': addr}, > server=server) > # It's now up to the protocol to handle the connection. > --- > > If the transport fails before calling connection_made() (ex: SSL > handshake failure): the transport and the protocol are destroyed, no > message is logged, the server is not aware of the connection failure. > > I would like to notify the server through the protocol (call > connection_failed) that an incoming connection failed. Random use > case: blacklist temporary the client IP. > > Victor > > > > -- > --Guido van Rossum (python.org/~guido <http://python.org/~guido>)
