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 os
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
    import aiohttp
    from aiohttp import web
    from aiosocks.connector import ProxyConnector, ProxyClientRequest
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(reactor,
            UNIXClientEndpoint(reactor, "/var/run/tor/control")
    config = await tor.get_config()
    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__':