[Web-SIG] ngx.poll extension (was Re: Are you going to convert Pylons code into Python 3000?)

Brian Smith brian at briansmith.org
Thu Mar 6 20:42:43 CET 2008


Graham Dumpleton wrote:
> This following idea may not make much sense, but baby keeping 
> me up, its 4am and I am probably not going to get back to 
> sleep until I get this idea out of my head now.

:) I think you need to have a serious discussion with the baby. Maybe if
she got a job she wouldn't sleep all day, and she would sleep through
the night. I had such a talk with my roommate a few years ago, and we
got along much better after that.

> Anyway, WSGI 2.0 currently talks about returning a single 
> tuple containing status, headers and iterable. What if it 
> actually optionally allowed the response to itself be an 
> iterable, such that you could do:
> 
>   yield ('102 Processing', [], None)
>   ...
>   yield ('102 Processing', [], None)
>   ...
>   yield ('200 OK', [...], [...])
> 
> I'll admit that I am not totally across what the HTTP 102 
> status code is meant to be used for and am sort of presuming 
> that this might make sense. Am sure though that Brian who 
> understands this sort of level better than me will set me straight.

The application should definitely be able to send as many 1xx status
lines as it wants. However, I expect any yielded status line to be sent
to the client, and there should be no need to include other headers or a
body. I will write more about this below.

That idea doesn't really benefit Manlio's programs. Manlio's program is
trying to say "use my thread for some other processing until some
(external) event happens." We already have standard mechanisms for doing
something similar in WSGI: multi-threaded and multi-process WSGI
gateways that let applications block indefinitely while letting other
applications run. A polling interface like Manlio proposes does help for
applications that are doing I/O via TCP(-like) protocols. But, it
doesn't provide a way to wait for a database query to finish, or for any
other kind of IPC to complete, unless everything is rebuilt around that
polling mechanism. It isn't a general enough interface to become a part
of WSGI. I think it is safe to say that multi-threaded or multi-process
execution is something that is virtually required for WSGI.

> Going a bit further with this, would it make sense for an 
> application to also be able to return a 100 to force server 
> layer to tell client to start sending data if 100-continue 
> expect header sent.

The handling of 1xx status codes is inhibited by the current state of
CGI and FastCGI gateways. In particular, most CGI and FastCGI modules do
not provide useful support for "Expect: 100-continue"; they always send
the "100 Continue" even when you don't want them to. As long as CGI and
FastCGI have to be supported as gateways, the design of WSGI will not be
able to change substantially from WSGI 1.0 (the current proposed changes
for WSGI 2.0 are really just cosmetic except for the removal of
start_response.write()).

Consequently, support for 1xx status lines must be optional, so it might
as well be done as a WSGI 1.0-compatible extension like this:

  def application(environ, start_response):
    def ignore(x):
      pass
    send_provisional_response = environ.get(
        "wsgi.send_provisional_response",
        ignore)
    ...
    send_provisional_response("102 Processing")
    ...
    send_provisional_response("102 Processing")

> Could it also be used in some way to allow better control 
> over output chunking by allowing:
> 
>   yield ('200 OK', [...], [...])
>   ...
>   yield (None, None, [...])
> 
> In other words the application could effectively yield up 
> multiple iterables related to the actual response content.

Again, I like the simplification that WSGI 2.0 applications are always
functions or function-like callables, and never iterables. It would be
easy to create a WSGI-1.0-compatible interface for efficient batching of
output strings, which could also then support buffer objects instead of
just (byte)strings:

  def application(environ, start_response):
    def join_buffers(buffers):
       return "".join([str(b) for b in buffers])
    vectorize = environ.get("wsgi.vectorize", join_buffers)
    return vectorize(buffers)

> Not that all HTTP servers support it, could this be a way of 
> allowing an application when using output chunking to specify 
> trailer headers for after last response content chunk.

The trailers feature is something I haven't thought a lot about. Again,
that is something that CGI doesn't support (I don't think FastCGI
supports it either). So, that is something that has to also be done in a
way similar to the above:

   def application(environ, start_response):
      headers = [...]
      trailers = environ.get("wsgi.trailers")
      if trailers is None:
          # inefficiently calculate the trailer fields
          # in advance
          headers.append("Header-A", ...)
          headers.append("Header-B", ...)
      ...
      start_response("200 OK", headers)      
      ...
      while ...:
        if trailers is not None:
          # calculate trailer fields as we yield
          # output
        yield output

      trailers.append("Header-A", ...)
      trailers.append("Header-B", ...)

It would be nice of the specification for the trailers extension
specified that the trailers list is included in the WSGI environment if
and only if (1) we are talking HTTP/1.1, and (2) the gateway and web
server support trailers.

> Important thing though is I am not suggesting this be the 
> default way of doing responses, but that be an optionally 
> available lower level layer for doing it. An application 
> could still just return a single tuple as per WSGI 2.0 now. A 
> good server adapter may optionally also allow this more low 
> level interface which allows some better measure of control. 
> Support of this low level interface could be optional, with 
> WSGI environment used to indicate if server supports it or not.

Right, but if these features are all optional, then they can be spec'd
to work with WSGI 1.0.

> Now, this doesn't deal with request content and an 
> alternative to current wsgi.input so that one could do the 
> non blocking read to get back just what was available, ie. 
> next chunk, but surely we can come up with solutions for that 
> as well. Thus I don't see it as impossible to also handle 
> input chunked content as well. We just need to stop thinking 
> that what has been proposed for WSGI 2.0 so far is the full 
> and complete interface.

We can just say that WSGI-2.0-style applications must support chunked
request bodies, but gateways are not required to support them.
WSGi-2.0-style applications would have to check for CONTENT_LENGTH, and
if that is missing, check to see if environ['HTTP_TRANSFER_ENCODING']
includes the "chunked" token. wsgi_input.read() would have to stop at
the end of the request; applications would not restricted from
attempting to read more than CONTENT_LENGTH bytes.

WSGI gateways would have to support an additional (keyword?) argument to
wsgi.input.read() that controls whether it is blocking or non-blocking.
It seems pretty simple.

Notice that all of this can be done even with WSGI 1.0, if these
additional features were broken out into their own PEP(s). 

- Brian



More information about the Web-SIG mailing list