The preferred way to implement a new transport in asyncio

Jonas Wielicki jonas at wielicki.name
Sun Dec 7 05:59:11 EST 2014


Hello fellow pythoneers,

First some background: I am implementing an XMPP client library in
asyncio. XMPP uses (START-)TLS. However, the native SSL support of
Python is rather restricted: We cannot hook into the certificate
validation process (it is PKI-all-or-nothing) at all, in addition many
of the goodies arrive with Python 3.4 only, while asyncio is available
in 3.3 already (with tulip). STARTTLS is also not quite supported with
asyncio (for which I found a hack, but it is about as unpleasant as
number (3) below).


So I wondered how feasible a PyOpenSSL based transport would be. From
looking into the asyncio selector_events.py code, it should be
managealbe to implement a client-side PyOpenSSL based transport
(possibly with STARTTLS support).

The main question I have is how to interweave this transport with
asyncio itself. Is there a preferred process? My approach would have
been to provide a coroutine to the user which takes a loop and the
arguments neccessary to create a transport (socket/(hostname, port), ssl
context, protocol factory, …) and returns the newly created transport.

I’m not entirely sure yet how to create the socket though. I have three
(all more or less unpleasant) options in mind:

1. Instead of making a simple transport, I make a Protocol/Transport
   hybrid one would layer on top of the TCP transport provided by
   asyncio. This is unpleasant because OpenSSL wants direct access to
   the socket and I expect trouble with that (have not dug into that
   yet though).

2. Create a Transport using the original create_connection and
   hard-unwire it from the loop using remove_reader/writer and hostily
   take over the socket object. This is unpleasant because it depends
   on things which I would consider internal details.

3. Duplicate the whole code from the original create_connection
   implementation, minus the unneeded parts. This is unpleasant because
   of code duplication.

(3) seems like the safest approach to me, as it does not rely on any
internalities of asyncio, but it is also the most cumbersome. Ideally,
there would be a public version of create_connection which returns the
socket instead of creating a transport from it. (If there is general
agreement on this, we could take this discussion to python-ideas. )

So, does anyone have input on that?

best regards,
jwi



More information about the Python-list mailing list