asycamore: An updated approach to asynchronous network programming with python

License:MIT
Name:aSycamore
Project-Label:asycamore
Version: 0.1dev1
Author: Robin Bryce
Author-email:robinbryce@gmail.com
Description:An updated approach to asynchronous network programming with python
Copyright: Copyright (c) 2007 Robin Bryce
Classifiers:Development Status :: 2 - Pre-Alpha
Classifiers:Programming Language :: Python

Abstract

asycamore provides a descriptor event dispatch api, a set of generator based primitives for working with sockets and detecting HTTP message boundaries, a light weight model for writing asynchronous network services, and a http server with (some) wsgi support.

The current implementation requires Python 2.5, and (currently) and the poll system call.

A fully functional http server with support for Keep-Alive, Transfer-Encoding:chunked, and request pipelineing is included. The server and much of the HTTP support code owes a lot to several prior works:

Contents

overview

If you are looking for a proven, reliable and performant asynchronous networking framework then Twisted is what you want. If you are looking for a mature general purpose python web application server then take a look at CherryPy 3.0.

If you are looking for a light weight library to support writing a specialised, performant, asynchronous server then hopefully you will find this package usefull.

For documentation on using the dispatch mainloop and implementing compatible dispatch targets see asycamore.dispatcher and asycamore.tcpsocket. These aspects of this package stand in their own right. The http server implementation uses the dispatch api in conjunction with the service model interface. For a documentation on the service model api see asycamore.servicemodel

For the current server implementation see:

python2.5 -m asycamore.asycwsgi --help

For profiling, use the --profile option of the server above in conjunction with the httpbench tool:

python2.5 -m asycamore.httpbench

asycamore.httpbench tool is not a bench mark. It is used to test the servers ability to deal with aggressively pipelined http requests and large numbers of concurrent connections.

About the name:

asycamore is a cheeky play on "asyncore: I want more!" and a direct reference to the 'wing' shaped seeds of the sycamore tree. If you think about how sycamore seeds behave as they fall to the ground then you have a good picture of the basic metaphor that underlies most asynchronous network programming.

History:

This work was started as an effort to better understand the impact of different Python PEP 333 WSGI application profiles on server performance, and the utility of PEP 342 in the domain of asynchronous network programming. It was originally developed as asynwsgi.

wsgi implementation status

Certain aspects of the wsgi spec implementation may be neither complete or correct. Development is currently focused on identifying how performance of async servers is affected by aspects of the wsgi spec.

wsgi.input:
  • For Transfer-Encoding:chunks: read, readline etc are NOT SUPPORTED. The only means to process the input is via the __iter__ method. See asycamore.asycwsgi:wsgi_file_uploader for an example. The yield pattern for __iter__ is also somewhat specialised. It is:

    [chunksz, datafirst] [data]* [chunksz, datafirst] [data]* ... [0 '']
    

    This pattern enables the server to deal with chunk boundaries on behalf of your application with out incurring excessive buffering overhead.

    The iterator will raise StopIteration after it has yielded the terminating chunk. The future plan is to incorporate trailer headers into this yield pattern but presently any trailer headers are silently droped on the floor (this is actualy RFC 2616 compliant although not very friendly).

    Your application iterable is GUARANTEED only to be resumed if input.next() has some data ready (either [chunksz, datafirst] or some/all of [data]*). If on a subsequent iteration the server detects EAGAIN/EWOULDBLOCK then input.next() will return wsgi.input_sentinel. If your application sees this value it should simply yield it back to the server. So the simplest idiom is:

    uploaded = cStringIO.StringIO()
    content_length = 0
    for v in iter(environ['wsgi.input']):
        if d is environ['wsgi.input_sentinel']):
            yield d
            continue
        if not isinstance(d, str):
            chunksz, d = d
            content_length += chunksz
        uploaded.write(d)
    assert len(uploaded.getvalue()) == content_length
    

    Note: asycamore.asycwsgi has an example application interable that handles POST with transfercoding:chunks and you can test this using:

    python2.5 -m asycamore.asycwsgi --synthesize-post=NCHUNKS\
    --chunksz=NBYTES http://localhost:8080/uploader/
    
  • POST suport where Transfer-Encoding header is absent or is set to 'none' At present the server collects all the data into a cStringIO.StringIO instance and does not invoke the application until all input has been collected. (AND this is not particularly well tested at present)