Posted on:
Last modified:
值得注意的是,wsgi 实际上定义了一个同步的模型,也就是每一个客户请求会调用一个同步的函数, 这样也就无法发挥异步的特性。
其中实现 simple_app 函数也就是实现了 wsgi 协议。需要注意的有一下三点:
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"
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()
© 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.
友情链接: MySQL 教程站