$ ls ~yifei/notes/

Python 的 wsgi 协议

Posted on:

Last modified:

wsgi 协议

值得注意的是,wsgi 实际上定义了一个同步的模型,也就是每一个客户请求会调用一个同步的函数,这样也就无法发挥异步的特性。


其中实现 simple_app 函数也就是实现了 wsgi 协议。需要注意的有一下三点:

  1. environ 字典中包含的变量
  2. start_response 的参数
  3. simple_app 的调用次序和返回值
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]

class AppClass:
    """Produce the same output, but using a class
    (Note: "AppClass" is the "application" here, so calling it
    returns an instance of "AppClass", which is then the iterable
    return value of the "application callable" as required by
    the spec.
    If we wanted to use *instances* of "AppClass" as application
    objects instead, we would have to implement a "__call__"
    method, which would be invoked to execute the application,
    and we would need to create an instance for use by the
    server or gateway.
    def __init__(self, environ, start_response):
        self.environ = environ
        self.start = start_response
    def __iter__(self):
        status = "200 OK"
        response_headers = [("Content-type", "text/plain")]
        self.start(status, response_headers)
        yield HELLO_WORLD

而对于 server/gateway 来说,每接收到一个 http 客户端,都会调用一次这个 application callable

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"
        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))
def start_response(status, response_headers, exc_info=None):
        if exc_info:
                if headers_sent:
                    # Re-raise original exception if headers sent
                    raise exc_info[1].with_traceback(exc_info[2])
                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)
        for data in result:
            if data:    # don"t send headers until body appears
        if not headers_sent:
            write("")   # send headers now if body was empty
        if hasattr(result, "close"):


  1. https://bottlepy.org/docs/dev/async.html
  2. http://uwsgi-docs-cn.readthedocs.io/zh_CN/latest/WSGIquickstart.html
  3. https://www.digitalocean.com/community/tutorials/how-to-deploy-python-wsgi-applications-using-uwsgi-web-server-with-nginx

© 2016-2022 Yifei Kong. Powered by ynotes

All contents are under the CC-BY-NC-SA license, if not otherwise specified.

Opinions expressed here are solely my own and do not express the views or opinions of my employer.