Notes on PEP333 (Python Web Server Gateway Interface)

FangwenYu發表於2014-12-08

This note is about PEP3333- Python Web Server Gateway Interface. Refer to (Source: http://legacy.python.org/dev/peps/pep-3333/) for the complete description. 

 

1. From the Application/Framwork side

The application object is simply a callable object that accepts two arguments, named environ, and start_response, following the convention.

Example:

HELLO_WORLD = b"Hello world!\n"

def simple_app(environ, start_response):
    """Simplest possible application object"""
    status = '200 OK'
    response_headers = [('Content-type', 'text/plain')]
    start_response(status, response_headers)
    return [HELLO_WORLD]

 

 

2. From the Server/Gateway side

The server or gateway invokes the application callable once for each request it receives from an HTTP client.

Exmple:

import os, sys

enc, esc = sys.getfilesystemencoding(), 'surrogateescape'

def unicode_to_wsgi(u):
    # Convert an environment variable to a WSGI "bytes-as-unicode" string
    return u.encode(enc, esc).decode('iso-8859-1')

def wsgi_to_bytes(s):
    return s.encode('iso-8859-1')

def run_with_cgi(application):
    environ = {k: unicode_to_wsgi(v) for k,v in os.environ.items()}
    environ['wsgi.input']        = sys.stdin.buffer
    environ['wsgi.errors']       = sys.stderr
    environ['wsgi.version']      = (1, 0)
    environ['wsgi.multithread']  = False
    environ['wsgi.multiprocess'] = True
    environ['wsgi.run_once']     = True

    if environ.get('HTTPS', 'off') in ('on', '1'):
        environ['wsgi.url_scheme'] = 'https'
    else:
        environ['wsgi.url_scheme'] = 'http'

    headers_set = []
    headers_sent = []

    def write(data):
        out = sys.stdout.buffer

        if not headers_set:
             raise AssertionError("write() before start_response()")

        elif not headers_sent:
             # Before the first output, send the stored headers
             status, response_headers = headers_sent[:] = headers_set
             out.write(wsgi_to_bytes('Status: %s\r\n' % status))
             for header in response_headers:
                 out.write(wsgi_to_bytes('%s: %s\r\n' % header))
             out.write(wsgi_to_bytes('\r\n'))

        out.write(data)
        out.flush()

    def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[1].with_traceback(exc_info[2])
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        headers_set[:] = [status, response_headers]

        # Note: error checking on the headers should happen here,
        # *after* the headers are set.  That way, if an error
        # occurs, start_response can only be re-called with
        # exc_info set.

        return write

    result = application(environ, start_response)
    try:
        for data in result:
            if data:    # don't send headers until body appears
                write(data)
        if not headers_sent:
            write('')   # send headers now if body was empty
    finally:
        if hasattr(result, 'close'):
            result.close()

 

3. Specification Details

The application object must accept two positional arguments. For the sake of illustration, we have named them environ and start_response, but they are not required to have these names. A server or gateway must invoke the application object using positional(not keyword) arguments.

The environ parameter is a dictonary object, containing CGI-style enviroment variables. This object must be a builtin Python dictionary (not a subclass, or other dictionary simulation), and the application is allowed to modify the dictionary in any way it desires. The dictionary must also include cetain WSGI-required variables, and may also include server-specific extension variables, named according to a covention.

The start_reponse parameter is a callable accepting two required positional arguments, and one optional argument. For the sake of illustration, we have named these arguments statusresponse_headers, and exc_info, but they are not required to have these names, and the application must invoke the start_response callable using positional arguments.

The status parameter is a status string of form '999 Message here', and response_headers is a list of (header_name, head_value) tuples describing the HTTP response header. The optional exc_info parameter is used only when the application has trapped an error and is attempting to display an error message to the browser.

The start_response callable must return a write(body_data) callable that takes one postional parameter: a bytestring to be written as part of the HTTP response body.

When called by the server, the application object must return an iterable yielding zero or more bytestrings. This can be accomplished in a variety of ways, such as by returning a list of bytestrings, or by the application being a generator function that yields bytestrings, or by the application being a class whose instances are iterable. Regardless of how it is accomplised, the application object must always return an iterable yielding zero or more bytestrings.

The server or gateway must transmit the yielded bytestrings to the client in an unbuffered fashion, completing the transmission of each bytestring before requesting another one. (In other words, applications should perform their own buffering.)

The application must invoke the start_response() callable before the iteralbe yields its first body bytestring, so that the server can send the headers before any body content. However, this invocatio nmay be performed by the iterable's first iteration, so servers must not assume that start_reponse() has been called before they begin iterating over the iterable.

 

If the iterable returned by the appliation has close() method, the server or gateway must call that method upon completion of the current request, whether the request was completed normally, or terminated early due to an application error during iteration or an early disconnect of the browser. (The close() method requirement is to support resource release by the application.)

 

相關文章