[Web-SIG] Combine wsgi and asyncio - possible?

Robert Collins robertc at robertcollins.net
Mon Sep 29 22:14:39 CEST 2014


On 30 September 2014 02:26, Frank Millman <frank at chagford.com> wrote:
> Hi all
>
> I am developing a business/accounting application. It is not a web server in
> the conventional sense, but it uses http, and clients connect to it via a
> web browser.  The server responds to an initial connection by sending a
> block of javascript which uses on_load() to display a welcome page. After
> that, all communication is handled by ajax-style messages passed between
> server and client. At no point is a new page requested or reloaded.

Pages are browser constructs :) - I presume you're still speaking
HTTP/1.1, with each request and response JSON - so a typical HTTP API
implementation?
...
> Sometimes one user action generates more than one 'event'. I package these
> up into a list and send them to the server in one message. As the server
> processes the events, it can result in multiple responses to the client.
> These are also packaged up and sent in one message.

One orthogonal thought here - HTTP/2 and websockets are [differently
but relatedly] aimed at solving this in perhaps a cleaner way. HTTP/2
allows you to very sanely (unlike pipelining which had lots of
problems) send different messages without waiting for the response, on
the same connection. So you don't need to package up the events, just
submit them, and you get separate events on the server side, without
latency overheads. [There is some encoding overhead of course, but on
a TCP stream with its window opened, you shouldn't notice that!].
There are websocket APIs around today that e.g. uwsgi offer.

> However, it can happen that while the server is working through the events,
> it needs to send a message to the client to pop up a dialog box, ask a
> question, and get a response before proceeding. With asyncio I can create a
> Future to set up and send the message, and use 'yield from
> asyncio.wait_for(...)' to wait for the response.

Straight WSGI can in principle do this, but I suspect that browser
APIs won't play nice. Here's how it would work:
 - make sure your ajax request is chunked, so that we can stream the
request up. Don't close the request stream until you've received the
full answer to all of your events. Stream the response back from your
WSGI app by using yield rather than write() / returning a list. On the
client, make sure you can handle multiple JSON documents within the
one response without fully buffering it. You'll almost certainly need
to be using SSL to avoid hitting a buffering intermediary (things ilke
virus scanners etc).

Assuming all the intermediaries are well behaved, nothing will buffer
either the request or the response, and you'll have bidirectional
communication happening.

Doing it without browser API support is also possible, but it requires
some mind bending - its basically what you're doing in asyncio: you
reply from the first context, let the clients answer come back in on a
new context, and then handoff from the new context to the existing old
one to complete the data. I'm quite sure the asyncio code will be much
cleaner for this situation.

> I am using asyncio.start_server(). For each request, the handler is passed a
> client_reader and a client_writer. Normally the writer is used to write the
> response to the original request, but if I need to ask a question, I use the
> writer to send the 'dialog box' message. When I get the response, I take the
> new client_writer and pass it back to the original request handler for it to
> complete the request.
>
> As I understand it, wsgi requires me to actually 'return' the response, so I
> don't have the opportunity to call 'yield from', and I do not get access to
> the writer object.
>
> Any suggestions welcome.

HTH,
Rob



-- 
Robert Collins <rbtcollins at hp.com>
Distinguished Technologist
HP Converged Cloud


More information about the Web-SIG mailing list