Using Asyncio Libraries with txtorcon

It is possible to use Twisted’s asyncioreactor in order to use Twisted together with asyncio libraries. This comes with a couple caveats:

  • You need to install Twisted

  • Twisted “owns” the event-loop (i.e. you call;

  • You need to convert Futures/co-routines to Deferred sometimes (Twisted provides the required machinery)

Here is an example using the aiohttp library as a Web server behind an Onion service that txtorcon has set up (in a newly-launched Tor process):

wanted: I can’t get this example to work properly with a Unix socket.

Download the example.

# This launches Tor and starts an Onion service using Twisted and
# txtorcon, and then starts a Web server using the aiohttp library.
# This style of interop between asyncio and Twisted requires twisted
# to use the "asyncioreactor" and for code to convert Futures/Tasks to
# Deferreds (most of which is already in Deferred)
# Thanks to Mark Williams for the inspiration, and this code:
# note: if run in Python2, there are SyntaxErrors before we can tell
# the user nicely

import asyncio
from twisted.internet import asyncioreactor

# get our reactor installed as early as possible, in case other
# imports decide to import a reactor and we get the default

from twisted.internet.task import react
from twisted.internet.defer import ensureDeferred, Deferred
from twisted.internet.endpoints import UNIXClientEndpoint

import txtorcon
    from aiohttp import web
except ImportError:
    raise Exception(
        "You need aiohttp to run this example:\n  pip install aiohttp"

def as_future(d):
    return d.asFuture(asyncio.get_event_loop())

def as_deferred(f):
    return Deferred.fromFuture(asyncio.ensure_future(f))

def get_slash(request):
    return web.Response(
        text="I am an aiohttp Onion service\n",

def create_aio_application():
    app = web.Application()
        web.get('/', get_slash)
    return app

async def _main(reactor):
    if False:
        print("launching tor")
        tor = await txtorcon.launch(reactor, progress_updates=print)
        tor = await txtorcon.connect(
            UNIXClientEndpoint(reactor, "/var/run/tor/control"),
    print("Connected to tor {}".format(tor.version))

    # here, we've just chosen 1234 as the port. We have three other
    # options:
    # - select a random, unused one ourselves
    # - put "ports=[80]" below, and find out which port txtorcon
    #   selected after
    # - use a Unix-domain socket

    # we create a Tor onion service on a specific local TCP port
    print("Creating onion service")
    onion = await tor.create_onion_service(
            (80, 1234)  # 80 is the 'public' port, 1234 is local
        version=2,  # FIXME use v3; using old tor for now

    # we're now listening on some onion service URL and re-directing
    # public port 80 requests to local TCP port 1234.
    app = create_aio_application()
    runner = web.AppRunner(app)
    await as_deferred(runner.setup())
    site = web.TCPSite(runner, 'localhost', 1234)
    await as_deferred(site.start())

    # now we're completely set up
    print("Onion site on http://{}".format(onion.hostname))
    await Deferred()

def main():
    return react(
        lambda reactor: ensureDeferred(

if __name__ == '__main__':