WSGI规范(PEP 3333) 第二部分(细节)

WSGI规范(PEP 3333) 第二部分(细节)

上一篇完成了WSGI 规范的轮廓的翻译。接下来看看规范的细节。


规范细节

应用对象接受两个位置参数。为了便于说明,我们分别称他们为 environ 和 start_response,但不一定非要叫这两个名字。服务器或者网关必须使用位置参数而不是关键字参数调用应用对象。例如调用

result =  application(environ, start_response) 

environ参数是一个字典对象,包含了 CGI 样式的环境变量,必须使用 Python 内置的字典对象(不是子类、UserDict 或者其他模拟字典的对象),应用对象可以任意修改这个 environ。 environ 必须包含一些 WSGI 需要的变量(后续有描述),也可能包含服务器扩展的一些变量,下文会有描述。

start_response 参数是一个可调用对象,接受两个必须的位置参数和一个可选参数。为了便于说明,我们叫这三个参数 status、response_headers 和 exc_info,但它们也可以改为其他名字。应用对象必须用位置参数调用 start_response,例如start_response(status, response_headers) 。

status 是一个格式为『999 消息文本』的状态字符串。response_headers 是一个 (header_name, header_value)的列表,描述了 HTTP 的响应头信息。可选的exc_info参数会在 start_reponse()错误处理介绍,仅当应用对象出现异常并尝试向浏览器显示一个错误消息时需要。

start_response则必须返回 write(body_data)的可调用对象。body_data是字节字符串,表示 HTTP 的响应体部分。请记住:之所以需要write(),是因为已经存在的有些框架中必须用到它。在新的应用或者框架中应该尽量避免使用 write()。查看 缓冲和流 这一节可以了解更多。

当服务器调用应用对象时,应用对象应该返回一个可迭代的(iterable) 对象,此对象生成0或者多字节的字节字符串。这可以有多种方法实现,比如返回一个字节字符串的 list、应用使用生成器函数实现,或者应用对象用类实现,而此类实现了迭代器协议。不管应用对象如何实现,它总是返回一个可迭代产生0或者多字节字符串的对象。

在处理下一个请求之前,服务器/网关则必须以非缓冲的方式传输生成的字节字符串给客户端。换句话说,缓冲应该由应用对象负责。查看 缓冲和流 这一节了解应用对象如何处理输出。

服务器/网关应该视这些被生成的字节字符串为二进制字节序列:尤其需要确认行结束符没有被修改。应用对象负责确保输出的字节字符串以客户端可以理解的格式输出。(服务器/网关应该设置 HTTP 传输编码,或者为了实现某个 HTTP 特性而转换传输编码。查看 HTTP 特性 可以了解更多)

如果调用 len(iterable)成功了,服务器使用这一准确结果。也就是说,应用对象返回的可迭代结果对象实现了__len__()方法,服务器必须返回这个结果。(查看 处理 Content-Length 头这一节了解如何处理这个头的)

如果应用对象返回的可迭代对象支持 close()方法,服务器/网关必须在结束当前请求处理之前调用这个方法,不管是正常完成,还是在迭代期间或者过早断开了和浏览器的连接而异常终止。(需要close()方法是因为应用对象需要释放一些资源。本协议希望支持 PEP 342的生成器和其他常见的带有 close()方法的可迭代对象)。

返回生成器或者其他自定义迭代器的应用对象不能假设返回对象总是会正常结束,因为有可能会被服务器提前关闭。

(请记住:应用对象必须在可迭代对象生成第一个字节之前调用 start_response(),因为服务器在发送任务 HTTP 响应体之前需要先发送响应头。然而,服务器/网关不能寄希望于可迭代对象的第一次迭代结果,也就是服务器开始对可迭代对象输出之前不能假设start_response()已经被调用过了,有可能没有调用)。

最后,服务器/网关绝对不能直接使用应用对象返回的这个可迭代对象的其他属性,除非它是一个对此服务器/网关 类型特定的对象,例如 wsgi.file_wrapper 的实例(查看 平台特定的文件处理)。一般情况下,只有规范指定的属性和 PEP 234 可迭代 API 指定的属性可以访问。

环境变量

environ 字典必须包含 CGI 规范的变量。以下变量必须出现,除非它们的值是空字符串,这时候可以忽略。

  • REQUEST_METHOD HTTP 的请求方法,如『GET』、『POST』。REQUEST_METHOD 永远不可能是空字符串,所以总是出现。
  • SCRIPT_NAME 请求的 URL 的路径(path)的末尾部分,应用对象知道它的虚拟位置。如果应用对象对服务器的『root』进行相应,这个值可能是空字符串。
  • PATH_INFO 请求 URL 的剩余部分,指出请求的目标在应用对象上的具体位置(location)。这个变量的值也有可能是空字符串,如果请求 URL 的目标是应用对象的 root,并且没有以"/"结束。
  • QUERY_STRING 如果请求 URL 中有"?",就是"?"之后的部分。所以也有可能是空字符串。
  • CONTENT_TYPE HTTP 请求中Content-Type的值,可能为空,或者不存在。
  • CONTENT_LENGTH HTTP 请求中 Content-Length 的值,可能为空,或者不存在
  • SERVER_NAME , SERVER_PORT 把SCRIPT_NAME 和 PATH_INFO 相加,就是完整 URL。然而,如果有HTTP_HOST ,HTTP_HOST应该使用SERVER_NAME一起构造请求 URL。查看 URL 结构 这一节了解更多。 SERVER_NAME 和 SERVER_PORT 永远不是空字符串,所以总是需要的。
  • SERVER_PROTOCOL 发送请求的客户端使用的协议版本,常见的如『HTTP/1.0』 『HTTP/1.1』。应用对象用来处理相关请求头(这个变量可能叫做REQUEST_PROTOCOL更合适,因为它描述了请求的版本,在服务器的响应中是无关紧要的,然而,为了兼容 CGI,我们还是保持现在的名字)
  • 以 HTTP_开头的变量 客户端给出的其他以 HTTP_ 开头的变量。这些变量应该根据请求头是否存在而出现在 environ 中。服务器/网关应该尝试提供这些 CGI 变量。另外,如果使用了 SSL,服务器/网关应该尽量提供一些 Apache 的 SSL 环境变量,如 HTTPS=on 和 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 .)


WSGI 兼容的服务器/网关应该随着他们的定义文档,也用文档说明它提供的变量。应用对象应该检查他们需要的任何变量,当发现缺少某个变量时准备一个回调计划。

注意:丢失的变量(如当没有验证时没有REMOTE_USER)也不要出现在 environ 中。CGI 定义的变量必须是原生字符串(python2中 str,python3中 bytes)

除了 CGI 定义的变量,environ 字典可能包含额外的操作系统的『环境变量』。除了这些,还必须包含以下 WSGI 定义的变量:

wsgi.version 使用 tuple (1, 0) , 表示 WSGI 的版本是 1.0
wsgi.url_scheme 表示 URL 的模式部分,应用对象将以这个模式被调用。一般情况下,值为"http"或者"https"。

wsgi.input 一个输入流(类文件对象),从这个对象读取 HTTP 请求体。(当被应用对象请求时,服务器/网关执行 read ,或者预读取请求体,缓存它到内存或者磁盘中,或者用输入流提供的其他技术)
wsgi.errors 错误输出流(类文件对象), 日志记录程序或其他标准的/中心位置的目的。它应该是一个文本模式的流。应用对象使用"\n"作为行结束符,而服务器/网关会将它转换成相应的输出值。

(在一些平台上,str 是 unicode 字符。错误输出流应该能接受并记录普通 unicode 而不抛出异常。然后允许用流的编码转换不能显示的 unicode 字符。)

对许多服务器来说,wsgi.errors应该是主错误日志。另一个选择是 sys.stderr,或者某个日志文件。服务器文档应该包含如何配置错误日志文件的说明。服务器/网关可能支持错误流到不同的日志处理应用上去,如果这是希望的。
wsgi.multithread 如果应用对象能够同时被同一个进程中的另一个线程使用(支持多线程),这个值应该是 true,否则为 false。
wsgi.multiprocess 如果等价的应用对象支持被另一个进程同时使用(支持多进程),这个值应该是 true,否则是 false
wsgi.run_once 如果服务器/网关希望应用对象在包含它的进程中仅执行一次这个请求,它的值为 true。正常情况下,只有是基于 CGI 的网关时,这个值才是 true。

最后,environ字典也可能包含一些服务器定义的变量。这些变量用小写字母、数字和 "."、"_"命名。并且使用一个唯一的前缀。例如 mod_python 可能应以一个变量为 "mod_python.some_variable"。


输入流和错误流

服务器提供的输入流和错误流必须提供下面的方法:

方法 流 说明

read(size) input 1

readline() input 1,2

readlines(hint) input 1,3

__iter__() input

flush() errors 4

write(str) errors

writelines(seq) errore

每个方法的含义都可以在标准库文档中查找。除了下表中列出的几点:

服务器不需要读取超过客户端指定的 Content-Length的内容,而如果应用对象尝试读取超过 Content-Length长度的内容,服务器应该模拟已经读到文件结束。应用对象不应该尝试读取超过 CONTENT_LENGTH 变量指定长度的内容。(所以,两端读取都要克制,并且服务器要处理应用端读取过长的情况)

服务器应该支持不带参数地调用 read(),这时返回客户端输入流的全部剩余内容。

当从空的输入流或者读完的输入流再次读时,服务器应该返回空字符串(当然是字节字符串了。)


服务器应该让 readline() 支持可选参数 size。但是在 WSGI 1.0的时候,服务器是可以不支持的。

(在 WSGI 1.0中,由于在实现上比较复杂,实际上也很少使用,size 参数不被支持。后来 cgi 模块开始使用 size 参数,所以实际的服务器不得不用任何办法去支持 size。)


readlines()的 hint 参数四可选的,不管是调用方还是实现方。应用对象可以自动决定是否实现它,服务器/网关也可自由地决定是否忽略它。


由于错误流一般不能倒回(重读),服务器/网关自由地推进写操作,不带缓冲地。这种情况下,flush()方法相当于 空操作(noop)。然而,可移植的应用不能假设输出是无缓冲的或者 flush()是空操作。他们必须调用 flush(),如果他们需要确认输出已经被写入。(例如,最小化从多个进程写入相同错误日志的混合数据)

符合 WSGI 规范的服务器都必须实现上表中的方法。符合 WSGI 规范的应用不能使用上表所列以外的方法或者属性。特别地,应用对象不能尝试关闭输入流和错误流,即使应用对象可以调用 close()方法。


start_response()

传给应用对象的第二个参数是一个可调用对象,形式如:start_response(status, response_headers, exc_info)(所有 WSGI 用到的可调用对象,传参数时都必须使用位置参数,而不是关键字参数)。start_response 用于开始 http 响应,它必须返回形如 write(body_data)的可调用对象。(具体请查看 缓冲和流 一节)

status 参数指 HTTP 的状态字符串,如『200 OK』、『404 Not Found』等。也就是说,它是一个由状态码和原因短句组成的字符串,状态码在前,原因短句在后,中间用一个空格分隔,没有其他空白字符或者其他内容。这个字符串绝对不能使用控制字符,不能用回车、换行或者他们的组合作为终结。

response_headers参数是一个形如(header_name, header_value) tuple的列表。而且必须是 Python 的 list 实例,执行 type(response_headers)返回 ListType。服务器可以用任何需要的方法修改它。每一个 header_name 必须是一个有效的 HTTP 头名称(在 RFC 2616, 4.2节定义),结尾没有冒号或者其他符号。

每一个header_value必须不能使用控制字符,包括回车或者换行,不管是在中间还是结尾。(这个要求是为了最小化服务器/网关和响应处理器(需要检查或修改响应头)解析的复杂度)

通常来说,服务器/网关负责确认发送给客户端的头的正确性:如果应用对象忽略了 HTTP 需要的头,服务器/网关必须添加上。例如:『Date:』和『Server:』通常由服务器/网关提供。

(服务器/网关作者请注意:HTTP 头的名字是大小写不敏感的,在检查应用对象提供的头名称时请注意这一点。)

禁止应用对象和中间件使用 HTTP 1.1的 hop-by-hop 特性或者头,任何在 HTTP/1.0中等价的特性,或任何影响客户端到 web 服务器端连接的持久化的头。这些特性对某些服务器是专用的,排他的。如果应该对象尝试发送这样的头,服务器/网关应该视作一个致命错误,如果 start_response()提供了这样的头,服务器/网关应该抛出错误。(更多有关 hop-by-hop 特性的介绍,请查看 其它HTTP特性 一节)

调用 start_response() 的时候,服务器应该检查响应头中的错误,这样可以在应用执行时将错误抛出。

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 byte string, 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.

如果提供了exc_info 参数,其值必须是 Python 的 sys.exc_info()返回的 tuple。只有start_response()被错误处理程序调用时,这个参数才会提供,并且是有应用对象提供。如果 提供了exc_info,并且还没有 HTTP 头输出,start_response应该用新的HTTP头替换当前缓存的 HTTP 头。因此,当错误发生时允许应用对象改变输出内容。

然而,如果提供了 exc_info,而已经有 HTTP 头发送,start_response()必须抛出异常,并且应该使用 exc_info tuple重新抛出。用代码表示:

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

这会重新抛出由应用对象产生的异常,原则上应该终止应用的执行。(对应用对象来说,一旦 HTTP 头已经发送出去,再尝试向浏览器发送错误输出是不安全的。)如果应用对象调用 start_response()时传入了 exc_info,应用对象不应该再抛出任何异常。相反,它应该允许这样的异常扩散给服务器/网关。查看下面的 错误处理 一节有更详细的信息。

应用对象可能调用 start_response()不止一次,当且仅当提供了 exc_info 的时候。更精确地讲,当 start_response()已经被应用对象调用时,再次调用start_response()而没有传入 exc_info 参数,这是一个致命错误。这包括第一次调用start_response()时抛出了错误。(查看上一篇文章中有关 CGI 的代码示例中相关逻辑)

注意:为了避免对 trackback 和 frame 发生循环引用,服务器、网关、实现了 start_response的中间件应该确定在start_response()之外没有对 exc_info参数的引用,最简单的做法如下:

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.

CGI 网关示例提供了另一种这种技术的说明。

处理Content-Length头

入股应用对象提供了 Content-Length 头,服务器不应该传输多于Content-Length指定长度的内容给客户端,而是当发送了足够的内容后,停止对 response 迭代;或者当应用对象尝试写超过长度的内容时抛出异常。(当然如果应用对象没有提供它用Content-Length声明的长度时,服务器应该关闭连接,将错误写入日志,或者报告此错误)

如果应用对象没有提供Content-Length头,服务器/网关一下方法之一来提供这个头。最简单的方法是当响应结束时关闭客户端连接。

然而,在某些情况下,服务器/网关可能能够生成Content-Length头,或者至少避免关闭客户端连接。如果应用对象没有调用 write(),并且返回长度为1的可迭代对象,那么服务器应该,通过这个可迭代对象生成的第一个字节字符串的长度,自动地检测/设置Content-Length。

如果服务器和客户端都支持 HTTP/1.1 的 chunked encoding,服务器必须使用 chunked encoding来发送每次 write()的一个 chunk,或者从可迭代对象 yield 的字节字符串,因此,服务器为每个 chunk生成一个 COntent-Length 头。这允许服务器保持客户端连接 alive,如果服务器希望这么做。这么做时服务器必须完全遵守 RFC 2616,或者返回到处理Content-Length缺失的策略之一。

(应用和中间件必须不能提供任何Transfer-Encoding到输出,例如 chunk 或者 gzip;和 hop-by-hop操作类似,这些编码是实际 web 服务器/网关的特别处理,查看 其他HTTP特性 获取详细信息)




缓冲和流

通常来说,应用会通过缓冲(合适的大小)输出和一次发送来实现最佳生产能力。这在已经存在的框架(如 Zope)中是常见方法:用 StringIO或类似对象缓冲输出,然后和响应头一起,一次发送。

在 WSGI 中,对应用对象来说,相应的方法是:简单地返回单个可迭代对象,其包含了整个响应体(单个字节字符串)。对各种呈现 HTML 页面(页面文本很容易存放在内存里)的应用功能来说,这是推荐的方法。

然而,对大文件,或者 HTTP 流的特殊用法(如多部分服务器推送)来说,应用可能需要提供小块输出(避免将大文件加载到内存中)的功能。有时也会遇到响应的部分内容比较耗时,但是如果先发送已经准备好的部分内容将是有用的。

上面这些情况,应用对象通常返回一个迭代器(经常是一个生成器迭代器),产生一块一块的输出。这些块可能因为多部分边界(如服务器推送)或者耗时任务结束(从磁盘文件读取一个块)之前的原因被分开。

WSGI 服务器、网关和中间件不能延迟任何块的发送;他们必须要么完整地传输块到客户端,要么保证在应用对象生产下一个块的同时继续发送数据。服务区/网关或者中间件必须用下面三种方法之一提供这种保证:

  1. 返回到应用对象之前发送整个块到操作系统。(刷新 OS 缓冲区)。
  2. 应用对象生产下一个数据块的同时,用另一个线程继续发送数据。
  3. (仅适用中间件)发送这个数据库到上一级服务器/网关。

通过这种保证,WSGI使得应用对象能够在输出数据的任何地方不停止数据的传输。这对一个功能是极为重要的,如服务器推送流,在multipart boundaries之间的数据应该完整发送到客户端。

处理块边界的中间件

为了更好地支持异步应用和异步服务器,中间件组件不能为了等待从可迭代应用对象获得更多数据而阻塞块的迭代。在中间件组件可以产生任何输出之前,如果中间件组件需要积累更多的数据,它必须先 yield 一个空字符串。

用另一种形式表达的话,在每次应用对象 yield 一个值的时候,中间件组件必须 yield 一个值。如果中间件组件不能 yield 其他值,那就 yield 一个空字符串。

这个需求确保异步应用和服务器一起,可以减少同时运行给定数目应用对象实例的线程数

这个需求也意味着在应用对象返回可迭代对象时,中间件组件也必须返回可迭代对象。对中间件组件来说,使用 write()发送来自下层应用 yield 的数据是?禁止的。中间件可能仅仅能够使用它们父一级的服务器的 write()来发送下一级应用对象使用中间件提供的 write()方法 yield 的数据。(译者:因为多了中间件,情况变得略微复杂,文字描述台抽象,把图画出来就额容易理解了。)

write() 可调用对象

一些已经存在的应用框架 API 支持非缓冲输出,和 WSGI 的行为不太一样。尤其是,他们提供了 某种write函数或方法来写非缓冲块数据,或者提供一个缓冲的 write 函数和一种 flush 机制来刷新缓冲。

不幸的是,这些 API 不能实现 WSGI 定义的可迭代应用,除非使用线程或其他特殊的机制才行。

因此,为了使得这些框架继续可以使用这些 API,WSGI 包含了这个特殊的 write()可调用对象,由 start_response 可调用对象返回。

(译者:以上说明了 WSGI 为什么要提供 write())

新的 WSGI 应用和框架不应该再使用 write(),如果他们能够避免使用 write()。实际上,write()是一种 hack 技术,为了支持哪些已经存在的必要的流式 API。通常来说,应用通过迭代它的返回值就可以产生输出,对 web 服务器来说,这使得在同一个 Python 线程中插入其他任务成了可能,潜在地,从整体来说,也提供了更好的生产能力。

write()由 start_response()返回,它接受单个参数:作为 HTTP 响应体写入的字节字符串,被视作它好想从输出的可迭代对象 yield 出来的。换句话说,在 write()返回之前,它必须确认传递进来的字节字符串已经发送到了客户端,或者被缓冲起来等待应用对象进一步处理时发送出去。

应用对象必须返回可迭代对象,即使它用 write()产生所有或部分响应体数据。返回的可迭代对象可能是空(yield空内容),但是如果它 yield 非空字节字符串,通常被服务器或者网关视为输出数据(数据必须立刻发送出去或进入队列)。应用不能在它返回的可迭代对象内部调用 write(),因此全部传递给 write()的字节字符串被发送给客户端之后,任何由可迭代对象 yield 的字节字符串才被发送出去。

Unicode问题

HTTP 不直接支持 Unicode。本规范也不支持。所有的编码/解码必须有应用处理;所有传递给服务器的字符串和从服务器返回的字符串都必须是 python2的 str 或者 python3的 bytes,不是 Unicode。在需要字符串的地方使用 unicode 的结果为定义。

也请记着,床底给 start_response()的字符串,不管是用于 status 还是响应头,都必须符合 RFC 2616指定的编码。也就是说,它们必须是 ISO-8859-1字符,或者使用 RFC 2047 MIME 编码。

一些 Python 平台,str 或 StringType 实际上是基于 Unicode 的,比如 Jython、IronPython、Python 3等。这个规范提到的字符串都必须是 ISO-8859-1编码的(从\u0000 到\u00FF)。对应用来说,提供包含 Unicode 字符或者不属于前面区间的代码点,将是致命错误。类似地,服务器和网关不能提供包含任何 Unicode 字符的字符串给应用对象。

再一次,本规范提供的字符串对象是 str 或者 StringType,而不是 unicode 或者 UnicodeType。并且,即使某个平台允许多于8比特的 str/StringType 对象,只有低8位会被使用。

本规范引用的字节字符串(如从 wsgi.input 读取的,传递给 write()的,或者由应用对象 yield 的),其值必须在 Python3中必须是 bytes,在 Python2中是 str。

错误处理

通常来说,应用应该尝试 trap 它们自己,或者内部错误等,显示一个有用的帮助消息给浏览器。(决定有用的消息是什么的是应用对象)

然而,要显示这样的消息,应用对象不能实际发送任何数据给浏览器,紧急中断这个响应。因此,WSGI 提供了一种机制:要么允许应用发送错误消息,要么自动中断:将 exc_info 传给 start_reponse。下面是个例子:

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"]

如果异常发生时还没有任何输出,对 start_response()的调用将正常的返回,应用对象会返回一个错误消息发送给浏览器。然而,已经有数据发送到浏览器,start_response()会重新抛出这个异常。这个异常不应该被应用对象 trap,所以应用对象将异常中断(abort)。服务器/网关则 trap 这个异常,终止响应。

服务器应该 trap 并记录任何中断应用对象或者应用对象的返回值的迭代异常。如果应用对象错误发生时响应的部分数据已经发送给浏览器,服务器/网关会尝试添加错误消息到输出中,如果已经发送的头包含了一个 text/*的内容类型,服务器清晰地知道怎么做出修改。

某些中间件可能希望提供额外的异常处理服务,或者拦截并替换应用错误消息。这些情况下,中间件会选择不抛出由 start_response()提供的 exc_info 信息,但是替换地抛出一个中间件特定的异常,或存储相关参数后不带异常地返回。然后,这是的应用对象返回它的错误体,允许中间件捕获并修改错误输出。这些技术一直有效,就像应用的作者们:

  1. 总是提供 exc_info 当开始错误响应时。
  2. 当提供了 exc_info时永不 trap start_response()产生的错误。

HTTP 1.1 Expect/Continue

实现 HTTTP 1.1的服务器/网关必须提供 HTTP1.1的expect/continue机制的透明支持。这有以下几种方法可以实现:

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

对 HTTP 1.0请求,或者对不直接对应应用对象的请求来说,没有这些行为限制,有关 HTTP 1.1 Expect/Continue的更多资料,查看 RFC 2616的8.2.3节和10.1.1节吧。

其他 HTTP 特性

通常来说,服务器/网关影响像傻瓜一样,允许应用对象完全控制输出。仅当不改变应用对象的响应的有效语义的情况下,他们仅仅需要作出必要的改变。对应用开发者来说,添加中间件组件以获得额外的特性总是可行的,所以服务器/网关开发者应该在他们的实现中应该尽量保守。换句话说,服务器应该想 HTTP 网关服务器那样考虑问题,视应用对象为 HTTP 的原始服务器(查看 RFC 2616,1.3节,有这些属于的定义)

然而,因为 WSGI 服务器和应用不通过 HTTP 通信,RFC2616所说的hop-by-hop头不提供 WSGI 内部通信。 WSGI应用也不能产生任何hop-by-hop头。仅视同需要他们的 HTTP 特性的头,或依赖任何进来的hop-by-hop头。WSGI 服务器必须自己处理任何支持的内部的hop-by-hop头,如决定内部的Transfer-Encoding,如果可用,包含 chunked 编码等。

应用这些原则在大量 HTTP 特性上,服务器就可以通过 If-None-Match和If-Modified-Since请求头、Last-Modified和 Etag响应头 处理缓存验证。然而,不需要这样做。如果应用想支持这个特性,应该执行自己的缓存验证,因为服务器/网关不需要做这样的验证。

类似地,服务器可能重新编码或者转换编码一个应用的响应,但应用对象则应该有自己合适的编码,并且不能提供传输编码。服务器可能按应用的响应的顺序发送数据,而应用不需要原生地支持字节排序。再一次,如果希望有这个功能,应用应该自己实现自己的逻辑。

这些有关应用的限制不是必要的,这意味着每个应用必须重复实现每一个 HTTP 特性;许多 HTTP 特性部分地、全部地由中间件组件实现,因而对服务器和应用作者们来说,不必一遍一遍地实现相同的特性了。

线程支持

支持或者不支持线程,服务器独立的。可以并行运行多个请求的服务器,应该提供在单个线程中运行应用的选项,所以不支持线程安全的应用或者框架仍旧可以和这个服务器配合使用。


WSGI规范(PEP 3333) 第一部分(概述)

WSGI规范(PEP 3333) 第三部分(实现)

WSGI规范(PEP 3333) 第四部分(常见问题)

编辑于 2017-07-29

文章被以下专栏收录