tranzit

asynchronous http web framework based on aiohttp providing websocket server functionality


Keywords
async, http, web, framework, websocket, server, aiohttp, python3, websockets
License
GPL-2.0
Install
pip install tranzit==0.1.1

Documentation

What's that?

TRANZIT is an asynchronous micro web framework supporting websockets (full duplex socket communication). With tranzit you can easily maintain both push & pull websocket messaging.

In fact, tranzit is a just a tiny wrapper for wonderful aiohttp lib providing async websocket server (which can be run separately) and a little CLI tool.

Installation

You can install tranzit via pip:

$ pip install tranzit

Quickstart

You can use CLI tool for that:

$ tranzit project <PROJECT_NAME>
$ cd <PROJECT_NAME>
$ tranzit run

Now your project runs on http://0.0.0.0:3000/ .

The first command creates a skeleton project:

<PROJECT_NAME>/
    __init__.py
    apps/
        __init__.py
        hello/
            __init__.py
            static/
                camel.html
    common/
        static/
            tz.js
    main.py
    server.yml

The main.py file starts the whole thing. It gets configs from server.yml such as:

  • HTTP server host
  • HTTP server port
  • To start websocket server or not
  • Websocket server host
  • Websocket server port
  • Which apps to run
  • Is this a production start or not (not meaningful yet)

Info: You should always pass PROJECT_DIR to MainServer() constructor

Routing and Views

All routing (for both http requests and websocket requests) is handled by apps/<app_name>/routes.py file.

PATH_PREFIX variable contains prefix for urls handled by this application (like [http://0.0.0.0:3000/<PATH_PREFIX>/some_url]).

APP_STATIC_DIR variable contains absolute path to the static folder for this application.

All views for http requests are dispatched by routes dict. Http views are ordinary aiohttp views.

All websocket views are dispatched by ws_rules dict. See explanation below.

Middlewares

You can pass a list of your middlewares in main.py:

MainServer(
    'server.yml', PROJECT_DIR, middlewares=[my_middleware]
)

More on aiohttp middlewares here.

Staticfiles

There are two types of static files directories in the project skeleton.

The first is <PROJECT_NAME>/common/static/ . It contains common project static files and is available via

http://0.0.0.0:3000/static/common/YOUR_GLORIOUS.js

The second type is app-specific: <PROJECT_NAME>/apps/<APP_NAME>/static . As you may suggest, it can be found here:

http://0.0.0.0:3000/static/APP_NAME/ANOTHER_GLORIOUS.js

Few words about websockets

To establish websocket messaging you must add websocket rule in apps/<app_name>/routes.py file just like that:

ws_rules = {
    'get_hello': WSPushHandle.get_hello
}

Now this view is available via websockets. To call this view function, do something like this on the client side:

var ws = new WebSocket('ws://0.0.0.0:19719');

ws.onmessage = function(response) {
    console.log(response.data);
};

ws.send('get_hello|');

We send message to call the view. Here is an ugly syntax for that message:

<function_name>|arg1, arg2, arg3, ...

Now take a look at the websocket view:

class WSPushHandle(object):

    @staticmethod
    async def get_hello(*args, **kwargs):
        send_func = kwargs['send_func']
        writer = kwargs['writer']

        while True:
            await asyncio.sleep(1)
            await send_func(writer, 'HELLO!')

Last line is a way to send message back to the client:

            await send_func(writer, 'HELLO!')

As you can see, send_func and writer instances are accessible via **kwargs.

Warning: beware of yellow snow! all websocket routes are in the same namespace! Avoid collisions.

Another warning: No security provided! Be sure to verify websocket request (e.g. pass user session key in WS view parameters)

Using aiohttp module

All of aiohttp.web module is accessible via tranzit.web.

Starting stand alone websocket server

You can use websocket server without http server.

All you need is an instance of:

tranzit.web.WebSocketServer(host, port, api)

api is a handler class instance which implements 3 functions as shown below:

class TranzitWSHandler(object):

    async def handle_text(self, loop, writer, msg):
        pass

    async def handle_binary(self, loop, writer, msg):
        pass

    async def handle_buffered(self, loop, reader, writer, first_msg):
        pass

Example starting stand alone websocket server:

from tranzit.web import WebSocketServer


class MyHandler():
    def __init__(self, rules={}):
        self.rules = rules

    async def handle_text(self, loop, writer, msg):
        response = msg.upper()

        await WebSocketServer.send_text(writer, response)

    async def handle_binary(self, loop, writer, msg):
        pass

    async def handle_buffered(self, loop, reader, writer, first_msg):
        pass

ws_server = WebSocketServer(
    host='0.0.0.0', port=19719, api=MyHandler()
)

ws_server.run_forever()