# 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 `reactor.run()`);

- 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.

## `web_onion_service_aiohttp.py`

`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:
    # https://gist.github.com/markrwilliams/bffb9c293194d105169ea06f03484ba1
    #
    # 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
    asyncioreactor.install(asyncio.get_event_loop())

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

    import txtorcon
    try:
        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()
        app.add_routes([
            web.get('/', get_slash)
        ])
        return app

    async def _main(reactor):
        if False:
            print("launching tor")
            tor = await txtorcon.launch(reactor, progress_updates=print)
        else:
            tor = await txtorcon.connect(
                reactor,
                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(
            ports=[
                (80, 1234)  # 80 is the 'public' port, 1234 is local
            ],
            private_key='RSA1024: ',
            version=2,  # FIXME use v3; using old tor for now
            progress=print,
        )

        # 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(
                _main(reactor)
            )
        )

    if __name__ == '__main__':
        main()