PEP3333翻译

原文:https://legacy.python.org/dev/peps/pep-3333


PEP:3333
标题:PythonWeb服务器网关接口v1.0.1
版本:f44a2ade62d8
最后修改期:2011-01-16 09:57:25 +0000
作者: P.J. Eby
讨论: Python Web-SIG
状态: 最终版
类型: 报告性的
内容类型: text/x-rst
创作于: 2010年9月26日
公布历史: 2010年9月26日, 2010年10月4日
替代: 333


目录

  • 致PEP333读者的前言
  • 摘要
  • 原理和目标 (来自 PEP 333)
  • 规范概述
    • 一条关于字符串类型的笔记
    • 应用/框架端
    • 服务器/网关端
    • 中间件: 两边都起作用的元素
  • 规范细节
    • 环境 变量
      • 输入和错误流
    • 可调用 start_response()
      • 处理 内容长度
    • 缓冲和流
      • 块边界的中间件处理。
      • 可调用 write()
    • Unicode的问题
    • 错误处理
    • HTTP 1.1预期/继续
    • 其他HTTP特性
    • 线程的支持
  • 实现/应用笔记
    • 服务器扩展接口
    • 应用程序配置
    • URL重建
    • 支持更早版本(<2.2)的Python
    • 可选的特定于平台的文件处理
  • 提问与回答
  • 被提议的/正在讨论的
  • 知识
  • 偏好
  • 版权

致PEP333读者的前言

这是PEP 333的一个更新版本, 稍微修改了一下以改进在python3下的使用,也将一些长期的实际的修正划进WSGI协议 (它的代码示例也被移植到Python 3中。)

而由于程序上的原因, 这一定是一份独特的PEP,没做任何改变让以前符合python2的服务器和应用失效。如果你的Python2服务器和应用是符合PEP 333的,那么它也符合这份PEP。

但是,在Python3下,你的应用和服务器必须遵循那些在标题下面章节中概述的规则: “一条关于字符串类型的笔记”和“Unicode的问题”。

要获取细节,可以一行行对比这篇文档和PEP333,你也可以浏览它的历史SVN版本,从版本84854往前看。

摘要

本文档指定一份推荐的web服务器与python应用/框架之间的标准接口,使得web应用在多种web服务器之间具有可移植性。

原理和目标 (来自 PEP 333)

Python目前有多种web应用框架,列举几个比如Zope,Quixote,Webware,SkunkWeb,PSO和Twisted Web。对于python新手来说,多种选择会是一个问题,因为总的来说,他们的web框架的选择会限制他们对能用的web服务器的选择,反之亦然。

相对而言,虽然Java只有不多的web应用框架能用,但是Java的“servlet”API让用任何Java web应用框架写的应用运行在任何一个支持servlet 接口的web服务器变成可能。

这样一个python web 服务器的API的有效性和广泛应用会将对框架的选择从对web服务器的选择中分离出来,方便用户选择适合他们的一对,同时方便框架开发者和服务器开发者专注于他们偏好的专业领域。此处的python web 服务器包括像Medusa一样用python写的,像mod_python一样内嵌python的,或者像CGI, FastCGI等一样通过网关协议调用python的。

所以此PEP建议一个简单独特的web服务器和web应用或者框架之间的接口:Python Web服务器网关接口 (WSGI)。

然而仅有一份WSGI说明书对解决python web应用的服务器和框架的现状是没有卵用的。为了起作用,服务器和框架作者和维护人员必须实际操作WSGI。

然而,因为没有服务器和框架支持WSGI,所以没有给写WSGI支持的作者的即时奖励。因此WSGI必须很容易写,这样的话一个作者在这个接口上的初期投入才相对低一点。

所以,简化服务器和框架两边接口的编写对WSGI接口的实用性来说显然是最关键的,也因此是任何设计决定的主要标准。

然而,注意到对一个框架作者来说简化编写并不像对一个web应用作者来说那么简单。WSGI对框架作者展示了一种完全“无虚饰”的接口,因为铃铛和汽笛比如响应对象和cookie处理只会阻碍现存框架对这些问题的处理。再次说明,WSGI的目标是促进现存服务器和应用或者框架的友好互连,而不是创造一个新的web框架。

请注意,这个目标也阻止WSGI去需求任何在已发布的python版本里不可获得的东西。因此,新的标准的文库模块不被此说明书提议或需求,而且 WSGI里的任何东西都不需要比2.2.2高级的版本。(当然,如果将来的python版本包含对这个在由标准文库提供的web服务器上的接口的支持也是好事)

除了简化现有和未来框架和服务器的编写,创造请求预处理器,响应后处理器和其他基于WSGI的中间件元素也应该简单点,这些中间件对包含它们的服务器来说看起来像应用,而对包含它们的应用来说则扮演服务器的角色。

如果中间件能够既简单又强壮,而且WSGI在服务器和框架中广泛可用,那么一种完全新型的python web 应用框架将变得可能:它包含松耦合的WSGI中间件元素。确实,已存在的框架的作者甚至可能选择重构他们的框架的已有服务器使之以此种方式被提供:变得更像配合WSGI使用的数据库,而不像完整的框架。这将使得应用开发者可以为某个特定功能选择最佳组合,而不是不得不考虑一个简单框架的各方各面。

当然正如此文所述,这一天无疑还很遥远。在这之前,对WSGI来说一个合适的短期目标就是让任意框架在任意服务器上运行起来。

最后,必须提到的是,目前的WSGI版本没有规定如何部署一个应用与一个web服务器或服务器网关搭配使用的任何详细原理。目前,这必须通过服务器和网关定义。在大量的服务器和框架编写WSGI为领域经验提供不同的部署需求后,创造另一份PEP来为WSGI服务器和应用框架描述一个部署标准是很有意义的。

规范概述

WSGI接口包括两部分:服务器/网关端和应用/框架端。服务器端调用一个由应用端提供的可回调对象。关于这个对象怎样提供的细节取决于服务器/网关端。假定一些服务器/网关需要一个应用部署者写一个简短的脚本来创建一个服务器/网关实例然后将应用对象提供给这个实例。其他的服务器和网关可能会用配置文件或者其他原理来规定改从哪里引入一个应用对象或者其他方式获得。

除了纯服务器/网关和应用/框架,也可以创造一些使这个规则的两方面都生效的中间件。这些中间件对包含的服务器来说扮演一个应用的角色,对包含的应用来说扮演一个服务器的角色,也可以被用来提供扩展API,内容转变,导航或者其他有用的功能。

贯穿次说明书,我们用“可回调”这个术语来表示一个函数,方法,类或者是有call方法的实例。取决于服务器,网关,或者应用来执行回调函数来为它们的需求选择适当的执行技巧。相反地,一个调用回调函数的服务器,网关,或者应用对何种回调函数是可用的有任何依赖。回调函数只用来被调用,而不用来自省。

一条关于字符串类型的笔记

总的来说,HTTP处理字节,也就是说次说明书主要是关于操作字节。

但是,那些字节的内容经常有一些文本释义,而且,在python中,字符串是处理文本的最方便的方法。

但是在很多python版本和实现中,字符串是Unicode,而不是字节。这需要在HTTP环境下小心平衡可用API与字节和文本的正确转换,尤其是在提供python 采用不同字符串类型的实现之间的端口代码。

因此WSGI定义了两种字符串:

  • 用作请求/响应头和元数据的“本地”字符串,它们总是以称为“str”的类型执行。
  • 用作请求和响应体的“二进制字符串”,在python3中它们称为字节类型被使用,其它情况下也是str类型(例如POST/PUT输入数据和HTML页面输出)。

但是不要混淆:即使python的str类型在底层实际是unicode,本土字符串的内容也必须通过Latin-1 编码技术转换成字节!(查看本文档后面的“Unicode问题”部分获取详细信息)

简而言之:您在此文中看到的“string”指的都是“本地”字符串,即一个str类型的对象,不管它内在是作为二进制还是作为unicode执行的。当您读到了“bytestring”,它应该被看做一个python3下的二进制对象或者是python2下的str对象。

因此,即使HTTP在某种意义上就是“bytes”,通过python的默认的str类型也很方便获取很多API。

应用/框架端

应用对象仅仅是一个接受两个参数的可回调对象。术语“对象”不应该被曲解为需要一个实际的对象实例:一个函数,方法,类,或者有call方法的实例都可被用作一个应用对象。应用对象必须可以多次调用,事实上所有的服务器/网关(除了CGI)都会进行此种重复请求。

(注意:即使我们将它称为“应用”对象,它也不应该被理解为表示应用开发者会用WSGI作为程序的API!人们认为应用开发者会继续使用现存的高水平框架服务器来开发应用。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.
注意:“AppClass”在此处是应用,所以叫它返回一个AppClass的实例, 就像说明书要求的那样,然后这个实例成为应用回调函数的可迭代返回值。

    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.
如果我们反而想用AppClass的实例来作为应用对象,我们得写一个__call__方法,这个方法会被调用来运行应用,而且我们会需要创建一个实例被服务器或者网关使用。
    """

    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

服务器/网关端

The server or gateway invokes the application callable once for each request it receives from an HTTP client, that is directed at the application. To illustrate, here is a simple CGI gateway, implemented as a function taking an application object. Note that this simple example has limited error handling, because by default an uncaught exception will be dumped to sys.stderr and logged by the web server.一旦服务器/网关接收到来自HTTP客户端的每个请求,就调用应用回调函数,这是在应用中控制的。比如说,有一个简单的CGI网关被当做一个函数来获取应用对象。注意这个简单的例子限制了报错处理,因为默认一个没被捕获的错误会被归为sys.stderr然后由服务器打印日志。


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()

中间件:两边都起作用的元素

注意到一个简单的对象可能扮演一个与一些应用有关的服务器的角色,同时也扮演一个与一些服务器有关的应用的角色。这种中间件元素可以表现出以下功能:

  • 在相应地重写environ后基于目标URL引导请求到不同的应用对象。
  • 允许多种应用或框架在同一个进程中同时运行。
  • 通过在网上推进请求与响应加载平衡和远程进程。
  • 呈现内容后处理,比如获取层叠样式表CSS。

中间件的存在已经转移到了接口的“服务器/网关端”和“应用/框架端”,也不需要特殊的支持。想要将中间件合并到应用的用户仅需提供中间件给服务器,就像它是一个应用,配置中间件去调用应用,就像它是一个服务器。当然,中间件包裹的应用实际上可能是另一个中间件元素包裹另一个应用等等,创造一种称为“中间件堆叠”的东西。

在极大程度上,中间件必须确认WSGI服务器和应用两端的限制条件和需要。虽然有时候,中间件的需求比单纯的服务器或者应用更加严格,这些细节会在说明书上注明。

下面是一个(不是实际的)将text/plain响应转变成大latin的中间件元素的例子,此例使用Joe Strout的piglatin.py。(注意:一个真实的中间件可能会用一种更强健的方式来检查内容的类型,也应该检查内容编码。而且,这个简单的例子忽略了一个词语可能通过一块内容被分离的可能性)

from piglatin import piglatin

class LatinIter:

    """Transform iterated output to piglatin, if it's okay to do so

    Note that the "okayness" can change until the application yields
    its first non-empty bytestring, so 'transform_ok' has to be a mutable
    truth value.
    """

    def __init__(self, result, transform_ok):
        if hasattr(result, 'close'):
            self.close = result.close
        self._next = iter(result).__next__
        self.transform_ok = transform_ok

    def __iter__(self):
        return self

    def __next__(self):
        if self.transform_ok:
            return piglatin(self._next())   # call must be byte-safe on Py3
        else:
            return self._next()

class Latinator:

    # by default, don't transform output
    transform = False

    def __init__(self, application):
        self.application = application

    def __call__(self, environ, start_response):

        transform_ok = []

        def start_latin(status, response_headers, exc_info=None):

            # Reset ok flag, in case this is a repeat call
            del transform_ok[:]

            for name, value in response_headers:
                if name.lower() == 'content-type' and value == 'text/plain':
                    transform_ok.append(True)
                    # Strip content-length if present, else it'll be wrong
                    response_headers = [(name, value)
                        for name, value in response_headers
                            if name.lower() != 'content-length'
                    ]
                    break

            write = start_response(status, response_headers, exc_info)

            if transform_ok:
                def write_latin(data):
                    write(piglatin(data))   # call must be byte-safe on Py3
                return write_latin
            else:
                return write

        return LatinIter(self.application(environ, start_latin), transform_ok)


# Run foo_app under a Latinator's control, using the example CGI gateway
from foo_app import foo_app
run_with_cgi(Latinator(foo_app))

说明细节

应用对象必须接收两个位置参数,为了说明,我们将它们命名为environ和start_response,但是它们不需要被命名。一个服务器/网关必须用位置(不是关键字)参数调用应用对象。(例如:上面所示的赋值result = application(environ, start_response)。)

参数environ是一个字典对象,包含CGI类环境变量。这个对象必须是一个内嵌的python字典(不是一个子类,用户词典或者其他虚拟字典),而且这个对象可以以任何它希望的方式修饰这个字典。这个字典必须也包含确定的WSGI需要的变量(后面的部分会介绍),而且也可能包含服务器特定的扩展变量,同过下面会介绍到的一个习惯命名。

Start_response参数是一个接收两个必须位置参数可回调函数,也是一个可选参数。为了说明,我们将这些参数命名为status,response_headers和exc_info,但是它们本不需要被命名,而且应用必须用位置参数调用start_response回调函数(例如:start_response(status,response_headers)。)

参数status是一个“999 Message here”表里的状态字符串,response_headers是一个描述HTTP响应头的(header_name, header_value)形式的元组的列表。可选参数exc_info在下面的“start_response() 回调函数”和“错误处理”部分会讲到。只有在应用捕获到错误并试图向浏览器展示一天错误信息的时候才会用到它。

Start_response回调函数必须返回一个有一个位置参数的write(body_data)回调函数:一个二进制字符串被写进HTTP响应体。(注意:write()回调函数只用来支持特定的现存的框架的必要输出API;如果可以避免的话它不应该被新的应用和框架使用。欲知详情看Buffering和Streaming部分。)

当被服务器调用的时候,应用对象必须返回一个可迭代的屈从零或者更多的二进制字符串。这可以通过多种方式实现,比如通过返回一个二进制字符串的列表,或者通过把应用当做输出二进制字符串的生成器函数来用,或者通过将应用当做一个实例是迭代器的类。不管是如何实现的,应用对象都必须总是返回一个可迭代的屈从零或者更多的二进制字符串。

服务器/网关必须传输输出的二进制字符串到一个无缓冲机制里的客户端,在请求另一个二进制字符串之前要先完成这个的传输。(换句话说,应用应该表现自己的缓冲作用,看下面的“缓冲”和“流”部分来了解关于必须怎么样处理应用输出的信息。)

服务器/网关应该将屈从二进制字符串看成是二进制的字节结果:特别是应该保证行尾不可选。应用得负责保证二进制字符串是以适合客户端的形式书写的。(为了实现例如字节范围转换这样的http特征,服务器/网关可能应用http转换编码,或者表现其他的转换。看下面的“其他HTTP特点”来获取更多细节)

如果调用len成功,服务器必须能够依赖精确的结果。也就是说,如果应用返回的迭代器提供一个有效的len()方法,那它必须返回一个精确的结果。(看“处理‘内容长度’头”部分来获取关于这通常是怎样使用的信息。)

如果应用返回的迭代器有一个close()方法,那么一旦完成当前请求后服务器/网关必须调用这个方法,不管请求是正常完成,还是在迭代或者是与服务器的提前断线过程中由于应用错误提前终止了。close()方法需要支持由应用释放的资源。此协议试图用close()方法完成PEP 342的生成器支持,还有其他的公共迭代器。

应用返回一个生成器或者其他的习惯迭代器不应该假定整个迭代器都被消耗,因为它可能被服务器早早地关掉。

(注意:应用必须在迭代器输出它的第一条体二进制字符串之前调用start_response()回调函数,这样服务器才能在任意体内容前发送头信息。然而,这次调用可能由迭代器的第一次迭代实现,所以服务器不能假定start_response()方法在迭代器上开始迭代前被调用了。)

最后,服务器/网关禁止直接使用任何其他的应用返回的迭代器的贡献,除非它对那个服务器/网关来说明确是一个类型实例,例如一个由wsgi.file_wrapper返回的文件装饰器(看“可选平台-特定的文件处理”部分)。在通常情况下,只有属性明确了,或者通过例如PEP 234里面的迭代器接口可获得才行。

环境变量

环境子弹需要包含这些CGI环境变量,就像公共网关接口协议定义的那样。下列变量必须展示,除非他们的值是一个空的字符串,在某些情况下他们可能会被忽略,除了下面标出的情况。

请求方法

       HTTP请求方法,例如“GET”或者“POST”。这个不可能是一个空的字符串,所以一直是必须的。

脚本名

       路由路径的最初的部分对应应用对象,这样的话应用才知道它的虚拟的位置。如果应用对应的是服务器的根目录的话这个可能是一个空的字符串。

路径信息

QUERY_STRING

The portion of the request URL that follows the "?", if any. May be empty or absent.

CONTENT_TYPE

The contents of any Content-Type fields in the HTTP request. May be empty or absent.

CONTENT_LENGTH

The contents of any Content-Length fields in the HTTP request. May be empty or absent.

SERVER_NAME, SERVER_PORT

When combined with SCRIPT_NAME and PATH_INFO, these two strings can be used to complete the URL. Note, however, that HTTP_HOST, if present, should be used in preference to SERVER_NAME for reconstructing the request URL. See the URL Reconstruction section below for more detail. SERVER_NAME and SERVER_PORT can never be empty strings, and so are always required.

SERVER_PROTOCOL

The version of the protocol the client used to send the request. Typically this will be something like "HTTP/1.0" or "HTTP/1.1" and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called REQUEST_PROTOCOL, since it denotes the protocol used in the request, and is not necessarily the protocol that will be used in the server's response. However, for compatibility with CGI we have to keep the existing name.)

HTTP_ Variables

Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with "HTTP_"). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request.

A server or gateway should attempt to provide as many other CGI variables as are applicable. In addition, if SSL is in use, the server or gateway should also provide as many of the Apache SSL environment variables [5] as are applicable, such as HTTPS=on and SSL_PROTOCOL. Note, however, that an application that uses any CGI variables other than the ones listed above are necessarily non-portable to web servers that do not support the relevant extensions. (For example, web servers that do not publish files will not be able to provide a meaningful DOCUMENT_ROOT or PATH_TRANSLATED.)

A WSGI-compliant server or gateway should document what variables it provides, along with their definitions as appropriate. Applicationsshould check for the presence of any variables they require, and have a fallback plan in the event such a variable is absent.

Note: missing variables (such as REMOTE_USER when no authentication has occurred) should be left out of the environ dictionary. Also note that CGI-defined variables must be native strings, if they are present at all. It is a violation of this specification for any CGI variable's value to be of any type other than str.

In addition to the CGI-defined variables, the environ dictionary may also contain arbitrary operating-system "environment variables", and mustcontain the following WSGI-defined variables:

Variable Value
wsgi.version The tuple (1, 0), representing WSGI version 1.0.
wsgi.url_scheme A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value "http" or "https", as appropriate.
wsgi.input An input stream (file-like object) from which the HTTP request body bytes can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.)
wsgi.errors

Variable Value
wsgi.version The tuple (1, 0), representing WSGI version 1.0.
wsgi.url_scheme A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value "http" or "https", as appropriate.
wsgi.input An input stream (file-like object) from which the HTTP request body bytes can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.)
wsgi.errors

An output stream (file-like object) to which error output can be written, for the purpose of recording program or other errors in a standardized and possibly centralized location. This should be a "text mode" stream; i.e., applications should use "\n" as a line ending, and assume that it will be converted to the correct line ending by the server/gateway.

(On platforms where the str type is unicode, the error stream should accept and log arbitary unicode without raising an error; it is allowed, however, to substitute characters that cannot be rendered in the stream's encoding.)

For many servers, wsgi.errors will be the server's main error log. Alternatively, this may be sys.stderr, or a log file of some sort. The server's documentation should include an explanation of how to configure this or where to find the recorded output. A server or gateway may supply different error streams to different applications, if this is desired.

|
| wsgi.multithread | This value should evaluate true if the application object may be simultaneously invoked by another thread in the same process, and should evaluate false otherwise. |
| wsgi.multiprocess | This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise. |
| wsgi.run_once | This value should evaluate true if the server or gateway expects (but does not guarantee!) that the application will only be invoked this one time during the life of its containing process. Normally, this will only be true for a gateway based on CGI (or something similar). |

Finally, the environ dictionary may also contain server-defined variables. These variables should be named using only lower-case letters, numbers, dots, and underscores, and should be prefixed with a name that is unique to the defining server or gateway. For example, mod_pythonmight define variables with names like mod_python.some_variable.

Input and Error Streams

The input and error streams provided by the server must support the following methods:

Method Stream Notes
read(size) input 1
readline() input 1, 2
readlines(hint) input 1, 3
iter() input
flush() errors 4
write(str) errors
writelines(seq) errors

Method Stream Notes
read(size) input 1
readline() input 1, 2
readlines(hint) input 1, 3
iter() input
flush() errors 4
write(str) errors
writelines(seq) errors

The semantics of each method are as documented in the Python Library Reference, except for these notes as listed in the table above:

  1. The server is not required to read past the client's specified Content-Length, and should simulate an end-of-file condition if the application attempts to read past that point. The application should not attempt to read more data than is specified by the CONTENT_LENGTH variable.

    A server should allow read() to be called without an argument, and return the remainder of the client's input stream.

    A server should return empty bytestrings from any attempt to read from an empty or exhausted input stream.

  2. Servers should support the optional "size" argument to readline(), but as in WSGI 1.0, they are allowed to omit support for it.

    (In WSGI 1.0, the size argument was not supported, on the grounds that it might have been complex to implement, and was not often used in practice... but then the cgi module started using it, and so practical servers had to start supporting it anyway!)

  3. Note that the hint argument to readlines() is optional for both caller and implementer. The application is free not to supply it, and the server or gateway is free to ignore it.

  4. Since the errors stream may not be rewound, servers and gateways are free to forward write operations immediately, without buffering. In this case, the flush() method may be a no-op. Portable applications, however, cannot assume that output is unbuffered or that flush() is a no-op. They must call flush() if they need to ensure that output has in fact been written. (For example, to minimize intermingling of data from multiple processes writing to the same error log.)

The methods listed in the table above must be supported by all servers conforming to this specification. Applications conforming to this specification must not use any other methods or attributes of the input or errors objects. In particular, applications must not attempt to close these streams, even if they possess close() methods.

The start_response() Callable

The second parameter passed to the application object is a callable of the form start_response(status, response_headers, exc_info=None). (As with all WSGI callables, the arguments must be supplied positionally, not by keyword.) The start_response callable is used to begin the HTTP response, and it must return a write(body_data) callable (see the Buffering and Streaming section, below).

The status argument is an HTTP "status" string like "200 OK" or "404 Not Found". That is, it is a string consisting of a Status-Code and a Reason-Phrase, in that order and separated by a single space, with no surrounding whitespace or other characters. (See RFC 2616, Section 6.1.1 for more information.) The string must not contain control characters, and must not be terminated with a carriage return, linefeed, or combination thereof.

The response_headers argument is a list of (header_name, header_value) tuples. It must be a Python list; i.e. type(response_headers) is ListType, and the server may change its contents in any way it desires. Each header_name must be a valid HTTP header field-name (as defined by RFC 2616, Section 4.2), without a trailing colon or other punctuation.

Each header_value must not include any control characters, including carriage returns or linefeeds, either embedded or at the end. (These requirements are to minimize the complexity of any parsing that must be performed by servers, gateways, and intermediate response processors that need to inspect or modify response headers.)

In general, the server or gateway is responsible for ensuring that correct headers are sent to the client: if the application omits a header required by HTTP (or other relevant specifications that are in effect), the server or gateway must add it. For example, the HTTP Date: and Server: headers would normally be supplied by the server or gateway.

(A reminder for server/gateway authors: HTTP header names are case-insensitive, so be sure to take that into consideration when examining application-supplied headers!)

Applications and middleware are forbidden from using HTTP/1.1 "hop-by-hop" features or headers, any equivalent features in HTTP/1.0, or any headers that would affect the persistence of the client's connection to the web server. These features are the exclusive province of the actual web server, and a server or gateway should consider it a fatal error for an application to attempt sending them, and raise an error if they are supplied to start_response(). (For more specifics on "hop-by-hop" features and headers, please see the Other HTTP Features section below.)

Servers should check for errors in the headers at the time start_response is called, so that an error can be raised while the application is still running.

However, the start_response callable must not actually transmit the response headers. Instead, it must store them for the server or gateway to transmit only after the first iteration of the application return value that yields a non-empty bytestring, or upon the application's first invocation of the write() callable. In other words, response headers must not be sent until there is actual body data available, or until the application's returned iterable is exhausted. (The only possible exception to this rule is if the response headers explicitly include a Content-Length of zero.)

This delaying of response header transmission is to ensure that buffered and asynchronous applications can replace their originally intended output with error output, up until the last possible moment. For example, the application may need to change the response status from "200 OK" to "500 Internal Error", if an error occurs while the body is being generated within an application buffer.

The exc_info argument, if supplied, must be a Python sys.exc_info() tuple. This argument should be supplied by the application only if start_response is being called by an error handler. If exc_info is supplied, and no HTTP headers have been output yet, start_response should replace the currently-stored HTTP response headers with the newly-supplied ones, thus allowing the application to "change its mind" about the output when an error has occurred.

However, if exc_info is provided, and the HTTP headers have already been sent, start_response must raise an error, and should re-raise using the exc_info tuple. That is:

raise exc_info[1].with_traceback(exc_info[2])

This will re-raise the exception trapped by the application, and in principle should abort the application. (It is not safe for the application to attempt error output to the browser once the HTTP headers have already been sent.) The application must not trap any exceptions raised by start_response, if it called start_response with exc_info. Instead, it should allow such exceptions to propagate back to the server or gateway. See Error Handling below, for more details.

The application may call start_response more than once, if and only if the exc_info argument is provided. More precisely, it is a fatal error to call start_response without the exc_info argument if start_response has already been called within the current invocation of the application. This includes the case where the first call to start_response raised an error. (See the example CGI gateway above for an illustration of the correct logic.)

Note: servers, gateways, or middleware implementing start_response should ensure that no reference is held to the exc_info parameter beyond the duration of the function's execution, to avoid creating a circular reference through the traceback and frames involved. The simplest way to do this is something like:

def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
# do stuff w/exc_info here
finally:
exc_info = None # Avoid circular ref.

The example CGI gateway provides another illustration of this technique.

Handling the Content-Length Header

If the application supplies a Content-Length header, the server should not transmit more bytes to the client than the header allows, and shouldstop iterating over the response when enough data has been sent, or raise an error if the application tries to write() past that point. (Of course, if the application does not provide enough data to meet its stated Content-Length, the server should close the connection and log or otherwise report the error.)

If the application does not supply a Content-Length header, a server or gateway may choose one of several approaches to handling it. The simplest of these is to close the client connection when the response is completed.

Under some circumstances, however, the server or gateway may be able to either generate a Content-Length header, or at least avoid the need to close the client connection. If the application does not call the write() callable, and returns an iterable whose len() is 1, then the server can automatically determine Content-Length by taking the length of the first bytestring yielded by the iterable.

And, if the server and client both support HTTP/1.1 "chunked encoding" [3], then the server may use chunked encoding to send a chunk for each write() call or bytestring yielded by the iterable, thus generating a Content-Length header for each chunk. This allows the server to keep the client connection alive, if it wishes to do so. Note that the server must comply fully with RFC 2616 when doing this, or else fall back to one of the other strategies for dealing with the absence of Content-Length.

(Note: applications and middleware must not apply any kind of Transfer-Encoding to their output, such as chunking or gzipping; as "hop-by-hop" operations, these encodings are the province of the actual web server/gateway. See Other HTTP Features below, for more details.)

Buffering and Streaming

Generally speaking, applications will achieve the best throughput by buffering their (modestly-sized) output and sending it all at once. This is a common approach in existing frameworks such as Zope: the output is buffered in a StringIO or similar object, then transmitted all at once, along with the response headers.

The corresponding approach in WSGI is for the application to simply return a single-element iterable (such as a list) containing the response body as a single bytestring. This is the recommended approach for the vast majority of application functions, that render HTML pages whose text easily fits in memory.

For large files, however, or for specialized uses of HTTP streaming (such as multipart "server push"), an application may need to provide output in smaller blocks (e.g. to avoid loading a large file into memory). It's also sometimes the case that part of a response may be time-consuming to produce, but it would be useful to send ahead the portion of the response that precedes it.

In these cases, applications will usually return an iterator (often a generator-iterator) that produces the output in a block-by-block fashion. These blocks may be broken to coincide with mulitpart boundaries (for "server push"), or just before time-consuming tasks (such as reading another block of an on-disk file).

WSGI servers, gateways, and middleware must not delay the transmission of any block; they must either fully transmit the block to the client, or guarantee that they will continue transmission even while the application is producing its next block. A server/gateway or middleware may provide this guarantee in one of three ways:

  1. Send the entire block to the operating system (and request that any O/S buffers be flushed) before returning control to the application, OR
  2. Use a different thread to ensure that the block continues to be transmitted while the application produces the next block.
  3. (Middleware only) send the entire block to its parent gateway/server

By providing this guarantee, WSGI allows applications to ensure that transmission will not become stalled at an arbitrary point in their output data. This is critical for proper functioning of e.g. multipart "server push" streaming, where data between multipart boundaries should be transmitted in full to the client.

Middleware Handling of Block Boundaries

In order to better support asynchronous applications and servers, middleware components must not block iteration waiting for multiple values from an application iterable. If the middleware needs to accumulate more data from the application before it can produce any output, it mustyield an empty bytestring.

To put this requirement another way, a middleware component must yield at least one value each time its underlying application yields a value. If the middleware cannot yield any other value, it must yield an empty bytestring.

This requirement ensures that asynchronous applications and servers can conspire to reduce the number of threads that are required to run a given number of application instances simultaneously.

Note also that this requirement means that middleware must return an iterable as soon as its underlying application returns an iterable. It is also forbidden for middleware to use the write() callable to transmit data that is yielded by an underlying application. Middleware may only use their parent server's write() callable to transmit data that the underlying application sent using a middleware-provided write() callable.

The write() Callable

Some existing application framework APIs support unbuffered output in a different manner than WSGI. Specifically, they provide a "write" function or method of some kind to write an unbuffered block of data, or else they provide a buffered "write" function and a "flush" mechanism to flush the buffer.

Unfortunately, such APIs cannot be implemented in terms of WSGI's "iterable" application return value, unless threads or other special mechanisms are used.

Therefore, to allow these frameworks to continue using an imperative API, WSGI includes a special write() callable, returned by the start_response callable.

New WSGI applications and frameworks should not use the write() callable if it is possible to avoid doing so. The write() callable is strictly a hack to support imperative streaming APIs. In general, applications should produce their output via their returned iterable, as this makes it possible for web servers to interleave other tasks in the same Python thread, potentially providing better throughput for the server as a whole.

The write() callable is returned by the start_response() callable, and it accepts a single parameter: a bytestring to be written as part of the HTTP response body, that is treated exactly as though it had been yielded by the output iterable. In other words, before write() returns, it must guarantee that the passed-in bytestring was either completely sent to the client, or that it is buffered for transmission while the application proceeds onward.

An application must return an iterable object, even if it uses write() to produce all or part of its response body. The returned iterable may be empty (i.e. yield no non-empty bytestrings), but if it does yield non-empty bytestrings, that output must be treated normally by the server or gateway (i.e., it must be sent or queued immediately). Applications must not invoke write() from within their return iterable, and therefore any bytestrings yielded by the iterable are transmitted after all bytestrings passed to write() have been sent to the client.

Unicode Issues

HTTP does not directly support Unicode, and neither does this interface. All encoding/decoding must be handled by the application; all strings passed to or from the server must be of type str or bytes, never unicode. The result of using a unicode object where a string object is required, is undefined.

Note also that strings passed to start_response() as a status or as response headers must follow RFC 2616 with respect to encoding. That is, they must either be ISO-8859-1 characters, or use RFC 2047 MIME encoding.

On Python platforms where the str or StringType type is in fact Unicode-based (e.g. Jython, IronPython, Python 3, etc.), all "strings" referred to in this specification must contain only code points representable in ISO-8859-1 encoding (\u0000 through \u00FF, inclusive). It is a fatal error for an application to supply strings containing any other Unicode character or code point. Similarly, servers and gateways must not supply strings to an application containing any other Unicode characters.

Again, all objects referred to in this specification as "strings" must be of type str or StringType, and must not be of type unicode or UnicodeType. And, even if a given platform allows for more than 8 bits per character in str/StringType objects, only the lower 8 bits may be used, for any value referred to in this specification as a "string".

For values referred to in this specification as "bytestrings" (i.e., values read from wsgi.input, passed to write() or yielded by the application), the value must be of type bytes under Python 3, and str in earlier versions of Python.

Error Handling

In general, applications should try to trap their own, internal errors, and display a helpful message in the browser. (It is up to the application to decide what "helpful" means in this context.)

However, to display such a message, the application must not have actually sent any data to the browser yet, or else it risks corrupting the response. WSGI therefore provides a mechanism to either allow the application to send its error message, or be automatically aborted: the exc_info argument to start_response. Here is an example of its use:

try:
# regular application code here
status = "200 Froody"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers)
return ["normal body goes here"]
except:
# XXX should trap runtime issues like MemoryError, KeyboardInterrupt
# in a separate handler before this bare 'except:'...
status = "500 Oops"
response_headers = [("content-type", "text/plain")]
start_response(status, response_headers, sys.exc_info())
return ["error body goes here"]

If no output has been written when an exception occurs, the call to start_response will return normally, and the application will return an error body to be sent to the browser. However, if any output has already been sent to the browser, start_response will reraise the provided exception. This exception should not be trapped by the application, and so the application will abort. The server or gateway can then trap this (fatal) exception and abort the response.

Servers should trap and log any exception that aborts an application or the iteration of its return value. If a partial response has already been written to the browser when an application error occurs, the server or gateway may attempt to add an error message to the output, if the already-sent headers indicate a text/* content type that the server knows how to modify cleanly.

Some middleware may wish to provide additional exception handling services, or intercept and replace application error messages. In such cases, middleware may choose to not re-raise the exc_info supplied to start_response, but instead raise a middleware-specific exception, or simply return without an exception after storing the supplied arguments. This will then cause the application to return its error body iterable (or invoke write()), allowing the middleware to capture and modify the error output. These techniques will work as long as application authors:

  1. Always provide exc_info when beginning an error response
  2. Never trap errors raised by start_response when exc_info is being provided

HTTP 1.1 Expect/Continue

Servers and gateways that implement HTTP 1.1 must provide transparent support for HTTP 1.1's "expect/continue" mechanism. This may be done in any of several ways:

  1. Respond to requests containing an Expect: 100-continue request with an immediate "100 Continue" response, and proceed normally.
  2. Proceed with the request normally, but provide the application with a wsgi.input stream that will send the "100 Continue" response if/when the application first attempts to read from the input stream. The read request must then remain blocked until the client responds.
  3. Wait until the client decides that the server does not support expect/continue, and sends the request body on its own. (This is suboptimal, and is not recommended.)

Note that these behavior restrictions do not apply for HTTP 1.0 requests, or for requests that are not directed to an application object. For more information on HTTP 1.1 Expect/Continue, see RFC 2616, sections 8.2.3 and 10.1.1.

Other HTTP Features

In general, servers and gateways should "play dumb" and allow the application complete control over its output. They should only make changes that do not alter the effective semantics of the application's response. It is always possible for the application developer to add middleware components to supply additional features, so server/gateway developers should be conservative in their implementation. In a sense, a server should consider itself to be like an HTTP "gateway server", with the application being an HTTP "origin server". (See RFC 2616, section 1.3, for the definition of these terms.)

However, because WSGI servers and applications do not communicate via HTTP, what RFC 2616 calls "hop-by-hop" headers do not apply to WSGI internal communications. WSGI applications must not generate any "hop-by-hop" headers [4], attempt to use HTTP features that would require them to generate such headers, or rely on the content of any incoming "hop-by-hop" headers in the environ dictionary. WSGI servers must handle any supported inbound "hop-by-hop" headers on their own, such as by decoding any inbound Transfer-Encoding, including chunked encoding if applicable.

Applying these principles to a variety of HTTP features, it should be clear that a server may handle cache validation via the If-None-Match and If-Modified-Since request headers and the Last-Modified and ETag response headers. However, it is not required to do this, and the application should perform its own cache validation if it wants to support that feature, since the server/gateway is not required to do such validation.

Similarly, a server may re-encode or transport-encode an application's response, but the application should use a suitable content encoding on its own, and must not apply a transport encoding. A server may transmit byte ranges of the application's response if requested by the client, and the application doesn't natively support byte ranges. Again, however, the application should perform this function on its own if desired.

Note that these restrictions on applications do not necessarily mean that every application must reimplement every HTTP feature; many HTTP features can be partially or fully implemented by middleware components, thus freeing both server and application authors from implementing the same features over and over again.

你可能感兴趣的:(PEP3333翻译)