<div>This is a very interesting initiative.</div>
<div>&nbsp;</div>
<div>However there are few problems:</div>
<div>- there is no support for chunked input - that would require having support for readline in the first place, also, it should be the gateway&#39;s business decoding the chunked input.</div>
<div>- the original wsgi spec somewhat has some support for streaming and asynchronicity [*1]</div>
<div>- i don&#39;t see how removing the write callable will help (i don&#39;t see a issue having the server providing a stringio.write as the write callable for synchronous apps)</div>
<div>- passing nonstring values though middleware will make using/porting existing wsgi middleware hairy (suppose you have a middleware that applies some filter to the appiter - you&#39;ll have your code full of isinstance nastiness)</div>

<div>&nbsp;</div>
<div>Also, have you looked at the existing gateway implementations with asynchronous support?</div>
<div>There are a bunch of them:</div>
<div><a href="http://trac.wiretooth.com/public/wiki/asycwsgi">http://trac.wiretooth.com/public/wiki/asycwsgi</a></div>
<div><a href="http://chiral.j4cbo.com/trac">http://chiral.j4cbo.com/trac</a></div>
<div><a href="http://wiki.secondlife.com/wiki/Eventlet">http://wiki.secondlife.com/wiki/Eventlet</a></div>
<div>my own shot at the problem: <a href="http://code.google.com/p/cogen/">http://code.google.com/p/cogen/</a></div>
<div>and manlio&#39;s mod_wsgi for nginx</div>
<div>(I may be missing some)</div>
<div>&nbsp;</div>
<div>However there is absolutely no unity in handling the wsgi.input (or equivalent)</div>
<div>&nbsp;</div>
<div>[*1]In my implementation i do a bunch of tricks to make use of regular wsgi middleware with async apps possible - i have a bunch of working examples using pylons:</div>
<div>&nbsp;- the extensions in the environ (like your environ[&#39;awsgi.readable&#39;]) return a empty string that penetrates most[*2] middleware and set the actual message (like your (token, fd, timeout) tuple on some internal object)</div>

<div>From this point of view, an async middleware stack is just a set of middleware that supports streaming.</div>
<div>&nbsp;</div>
<div>Please see:</div>
<div>
<div><a href="http://cogen.googlecode.com/svn/trunk/docs/cogen.web.async.html">http://cogen.googlecode.com/svn/trunk/docs/cogen.web.async.html</a></div>
<div><a href="http://cogen.googlecode.com/svn/trunk/docs/cogen.web.wsgi.html">http://cogen.googlecode.com/svn/trunk/docs/cogen.web.wsgi.html</a></div></div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>[*2] middleware that consume the app iter ruin that pattern, but regardless, they are not compliant to the wsgi spec (see <a href="http://www.python.org/dev/peps/pep-0333/#middleware-handling-of-block-boundaries">http://www.python.org/dev/peps/pep-0333/#middleware-handling-of-block-boundaries</a>&nbsp;) - notable examples are most of the exception handling middleware (they can&#39;t work otherwise anyway)</div>

<div>&nbsp;</div>
<div class="gmail_quote">On Tue, May 6, 2008 at 4:30 AM, Christopher Stawarz &lt;<a href="mailto:cstawarz@csail.mit.edu">cstawarz@csail.mit.edu</a>&gt; wrote:<br>
<blockquote class="gmail_quote" style="PADDING-LEFT: 1ex; MARGIN: 0px 0px 0px 0.8ex; BORDER-LEFT: #ccc 1px solid">(I&#39;m new to the list, so please forgive me for making my first post a<br>specification proposal :)<br>
<br>Browsing through the list archives, I see there&#39;s been some<br>inconclusive discussions on adding better support for asynchronous web<br>servers to the WSGI spec. &nbsp;Since such support would be very useful for<br>some upcoming projects of mine, I decided to take a shot at specing<br>
out and implementing it. &nbsp;I&#39;d be grateful for any feedback you have.<br>If this seems like something worth pursuing, I would also welcome<br>collaborators to help develop the spec further.<br><br>The name for this proposed specification is the Asynchronous Web<br>
Server Gateway Interface (AWSGI). &nbsp;As the name suggests, the spec is<br>closely related to WSGI and is most easily described in terms of how<br>it differs from WSGI. &nbsp;AWSGI eliminates the following parts of WSGI:<br><br>&nbsp;- the environment variables wsgi.version and wsgi.input<br>
<br>&nbsp;- the write() callable returned by start_response()<br><br>AWSGI adds the following environment variables:<br><br>&nbsp;- awsgi.version<br>&nbsp;- awsgi.input<br>&nbsp;- awsgi.readable<br>&nbsp;- awsgi.writable<br>&nbsp;- awsgi.timeout<br><br>
In addition, AWSGI allows the application iterable to yield two types<br>of data:<br><br>&nbsp;- byte strings, handled as in WSGI<br><br>&nbsp;- the result of calling awsgi.readable or awsgi.writable, which<br>&nbsp; &nbsp;indicates that the application should be paused and restarted when<br>
&nbsp; &nbsp;a specified file descriptor is ready for reading or writing<br><br>Because of AWSGI&#39;s similarity to WSGI, a simple wrapper can be used to<br>run AWSGI applications on WSGI servers without alteration.<br><br>The following example application demonstrates typical usage of AWSGI.<br>
This application simply reads the request body and sends it back to<br>the client. &nbsp;Each time it wants to receive data from the client, it<br>first tests awsgi.input for readability and then calls its recv()<br>method. &nbsp;If awsgi.input is not readable after one second, the<br>
application sends a &quot;408 Request Timeout&quot; response to the client and<br>terminates:<br><br><br>&nbsp;def echo_request_body(environ, start_response):<br>&nbsp; &nbsp; &nbsp;input = environ[&#39;awsgi.input&#39;]<br>&nbsp; &nbsp; &nbsp;readable = environ[&#39;awsgi.readable&#39;]<br>
<br>&nbsp; &nbsp; &nbsp;nbytes = int(environ.get(&#39;CONTENT_LENGTH&#39;) or 0)<br>&nbsp; &nbsp; &nbsp;output = &#39;&#39;<br>&nbsp; &nbsp; &nbsp;while nbytes:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;yield readable(input, 1.0) &nbsp;# Time out after 1 second<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if environ[&#39;awsgi.timeout&#39;]:<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;msg = &#39;The request timed out.&#39;<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;start_response(&#39;408 Request Timeout&#39;,<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [(&#39;Content-Type&#39;, &#39;text/plain&#39;),<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(&#39;Content-Length&#39;, str(len(msg)))])<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;yield msg<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return<br><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;data = input.recv(nbytes)<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;if not data:<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;break<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;output += data<br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;nbytes -= len(data)<br><br>&nbsp; &nbsp; &nbsp;start_response(&#39;200 OK&#39;, [(&#39;Content-Type&#39;, &#39;text/plain&#39;),<br>
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(&#39;Content-Length&#39;, str(len(output)))])<br>&nbsp; &nbsp; &nbsp;yield output<br><br><br>I have rough but functional implementations of a number of AWSGI<br>components available in a Bazaar branch at<br>
<a href="http://pseudogreen.org/bzr/awsgiref/" target="_blank">http://pseudogreen.org/bzr/awsgiref/</a>. &nbsp;The package includes an<br>asyncore-based AWSGI server and an AWSGI-to-WSGI application wrapper.<br>In addition, the file spec.txt contains a more detailed description of<br>
the specification (which is also appended below).<br><br>Again, I&#39;d very much appreciate comments and criticism.<br><br><br>Thanks,<br>Chris<br><br><br><br><br>Detailed AWSGI Specification<br>----------------------------<br>
<br>- Required AWSGI environ variables:<br><br>&nbsp;* All variables required by WSGI, except for wsgi.version and<br>&nbsp; &nbsp;wsgi.input, which must *not* be present<br><br>&nbsp;* awsgi.version =&gt; the tuple (1, 0)<br><br>&nbsp;* awsgi.input<br>
<br>&nbsp; &nbsp;This is an object with one method, recv(bufsize), which behaves<br>&nbsp; &nbsp;like the socket method of the same name (although it doesn&#39;t<br>&nbsp; &nbsp;support the optional flags parameter). &nbsp;Before each call to<br>&nbsp; &nbsp;recv(), the application must test awsgi.input for readability via<br>
&nbsp; &nbsp;awsgi.readable. &nbsp;The result of calling recv() without doing so is<br>&nbsp; &nbsp;undefined.<br><br>&nbsp; &nbsp;(XXX: Should recv() handle EINTR for the application?)<br><br>&nbsp;* awsgi.readable<br>&nbsp;* awsgi.writable<br><br>&nbsp; &nbsp;These are callables with the signature f(fd, timeout=None). &nbsp;fd is<br>
&nbsp; &nbsp;either a file descriptor (i.e. int or long) or an object with a<br>&nbsp; &nbsp;fileno() method that returns a file descriptor.<br><br>&nbsp; &nbsp;timeout has the same semantics as the timeout parameter to<br>&nbsp; &nbsp;select.select(). &nbsp;If the operation times out, awsgi.timeout will<br>
&nbsp; &nbsp;be true when the application resumes.<br><br>&nbsp; &nbsp;In addition to checking readiness for reading or writing, servers<br>&nbsp; &nbsp;should also monitor file descriptors for &quot;exceptional&quot; conditions<br>&nbsp; &nbsp;(e.g. out-of-band data) and restart the application if they occur.<br>
<br>&nbsp;* awsgi.timeout =&gt; boolean indicating whether the most recent read<br>&nbsp; &nbsp;or write wait timed out (false if there have been no waits)<br><br>- start_response() must *not* return a write() callable, as this<br>&nbsp;method of providing application output to the server is incompatible<br>
&nbsp;with asynchronous execution.<br><br>- The server must accept awsgi.input as input to awsgi.readable,<br>&nbsp;either by providing an actual socket object or by special-case<br>&nbsp;handling (i.e. awsgi.input needn&#39;t have a fileno() method, as long<br>
&nbsp;as the server handles it as if it did).<br><br>- Applications return iterators, which can yield:<br><br>&nbsp;* a string =&gt; sent to client, just as in standard WSGI<br><br>&nbsp;* the result of a call to awsgi.readable or awsgi.writable =&gt;<br>
&nbsp; &nbsp;application is resumed when either the file descriptor is ready<br>&nbsp; &nbsp;for reading/writing or the wait times out (in which case,<br>&nbsp; &nbsp;awsgi.timeout will be true)<br><br>- Although AWSGI applications will *not* be directly compatible with<br>
&nbsp;WSGI servers, middleware will allow them to run as standard WSGI<br>&nbsp;apps (with all I/O waits returning immediately).<br><br>- AWSGI servers will not support unmodified WSGI applications. &nbsp;There<br>&nbsp;are several reasons for this:<br>
<br>&nbsp;- If the app does blocking I/O, it will block the entire server.<br><br>&nbsp;- Calls to the read() method of wsgi.input may fail with<br>&nbsp; &nbsp;EWOULDBLOCK, which an app expecting synchronous I/O probably won&#39;t<br>&nbsp; &nbsp;be prepared to deal with.<br>
<br>&nbsp;- The readline(), readlines(), and __iter__() methods of wsgi.input<br>&nbsp; &nbsp;can require multiple network I/O operations, which is incompatible<br>&nbsp; &nbsp;with asynchronous execution.<br><br>&nbsp;- The write() callable returned by start_response() is inherently<br>
&nbsp; &nbsp;incompatible with asynchronous execution.<br><br>&nbsp;Because of these issues, this specification aims for one-way<br>&nbsp;compatibility between AWSGI and WSGI (i.e. the ability to run AWSGI<br>&nbsp;apps on WSGI servers via middleware, but not vice versa).<br>
<br>_______________________________________________<br>Web-SIG mailing list<br><a href="mailto:Web-SIG@python.org" target="_blank">Web-SIG@python.org</a><br>Web SIG: <a href="http://www.python.org/sigs/web-sig" target="_blank">http://www.python.org/sigs/web-sig</a><br>
Unsubscribe: <a href="http://mail.python.org/mailman/options/web-sig/ionel.mc%40gmail.com" target="_blank">http://mail.python.org/mailman/options/web-sig/ionel.mc%40gmail.com</a><br></blockquote></div><br><br clear="all">
<br>-- <br><a href="http://ionelmc.wordpress.com">http://ionelmc.wordpress.com</a><br>