本文描述Django runserver命令中使用的开发服务器如何实现WSGI规范.有位牛人已经翻译了PEP333,下文对规范的翻译引用自此牛人的文章.


Python django drf 源码解析 django源码解析视频_中间件

 

HTTP请求处理过程

一.启动服务器.

使用manage.py runserver命令启动服务器时,实际上是调用django.core.servers.basehttp模块下的run()方法,来实例化一个django.core.servers.basehttp.WSGIServer类的实例.上图中可见WSGIServer继承至Python内建的HTTPServer类,调用其serve_forever()方法即可启动http服务.

def run(addr, port, wsgi_handler, ipv6=False):
    server_address = (addr, port)
    httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

二.接受HTTP请求

当有一个HTTP请求到达服务器,WSGIServer类会通过调用WSGIRequestHandler类的handle()方法来处理HTTP请求.

WSGIRequestHandler类处理HTTP请求时,会根据WSGI1.0规范,创建一个WSGI应用程序接口的可调用对象,即WSGIHandler类的实例.然后作为参数传递给WSGI服务接口对象,即ServerHandler类的实例.

三.处理HTTP请求

第一,设置系统环境变量.包括HTTP服务器变量和WSGI规范变量

WSGIServer类中,设置HTTP环境变量如下:

env['SERVER_NAME'] = self.server_name
env['GATEWAY_INTERFACE'] = 'CGI/1.1'
env['SERVER_PORT'] = str(self.server_port)
env['REMOTE_HOST']=''
env['CONTENT_LENGTH']=''
env['SCRIPT_NAME'] = ''

WSGI服务接口对象ServerHandler设置WSGI环境变量如下:

env['wsgi.input']        = self.get_stdin()
env['wsgi.errors']       = self.get_stderr()
env['wsgi.version']      = self.wsgi_version
env['wsgi.run_once']     = self.wsgi_run_once
env['wsgi.url_scheme']   = self.get_scheme()
env['wsgi.multithread']  = self.wsgi_multithread
env['wsgi.multiprocess'] = self.wsgi_multiprocess

第二,调用应用程序接口对象.

WSGIHandler类继承至django.core.handlers.base.BaseHandler类.BaseHandler类实际上是Django处理HTTP请求的核心.它完成加载中间件,通过URL配置寻找并调用合适的视图,返回视图处理后的response.

另外,每个视图获得的参数实际上就是django.core.handlers.wsgi.WSGIRequset类的实例.

四.返回response

通过应用程序接口WSGIHandler获得相应视图函数返回的response后,服务接口ServerHandler通过调用HTTP服务器的write()方法,把response返回给HTTP的请求者.

 

至此,是一个Django开发服务器的HTTP请求处理的大致过程.当然还有很多细节未描述,如headers的处理.

 

########################################################################################################################################

以下是Python Web Server Gateway Interface v1.0 中文翻译文章的部分拷贝,这位牛人并未完全翻译完成,本来打算继续翻译,但是怕翻译出来对不起观众.o(╯□╰)o.暂时放在这,日后再说.

简介

本文档描述一份在web服务器与web应用/web框架之间的标准接口,此接口的目的是使得web应用在不同web服务器之间具有可移植性。

基本原理与目标

python目前拥有大量的web框架,比如 Zope, Quixote, Webware, SkunkWeb, PSO, 和Twisted Web。大量的选择使得新手无所适从,因为总得来说,框架的选择都会限制web服务器的选择。

对比之下,虽然java也拥有许多web框架,但是java的" servlet" API使得使用任何框架编写出来的应用程序可以在任何支持" servlet" API的web服务器上运行。服务器中这种针对python的API(不管服务器是用python写的,还是内嵌python,还是通过一种协议来启动python)的使用和普及,将分离人们对web框架和对web服务器的选择,用户可以自由选择适合他们的组合,而web服务器和web框架的开发者也能够把精力集中到各自的领域。

因此,这份PEP建议在web服务器和web应用/web框架之间建立一种简单的通用的接口规范,Python Web Server Gateway Interface (WSGI).

但是光有这么一份规范对于改变web服务器和web应用/框架的现状是不够的,只有web服务器和web框架的作者们实现WSGI,他才能起应有的效果。

然而,既然还没有任何框架或服务器实现了WSGI,对实现WSGI也没有什么直接的奖励,那么WSGI必须容易实现,这样才能降低作者的初始投资。

服务器和框架两边接口的实现的简单性,对于WSGI的作用来说,绝对是非常重要的。所以这一点是任何设计决策的首要依据。

对于框架作者来说,实现的简单和使用的方便是不一样的。WSGI为框架作者展示一套绝对没有"frills"的接口,因为象response对象和对cookie的处理这些问题和框架现有的对这些问题的处理是矛盾的。再次重申一遍,WSGI的目的是使得web框架和web服务器之间轻松互连,而不是创建一套新的web框架。

同时也要注意到,这个目标使得WSGI不能依赖任何在当前已部署版本的python没有提供的任何功能,因此,也不会依赖于任何新的标准模块,并且 WSGI并不需要2.2.2以上版本的python(当然,在以后的python标准库中内建支持这份接口的web服务器也是个不错的主意)

不光要让现有的或将要出现的框架和服务器容易实现,也应该容易创建请求预处理器、响应处理程序和其他基于WSGI的中间件组件,对于服务器来说他们是应用程序,而对于他们包含的应用程序来说他们是服务器。

如果中间件既简单又健壮,而且WSGI广泛得实现在服务器和框架中,那么就有可能出现全新的python web框架:整个框架都是由几个WSGI中间件组件组成。甚至现有框架的作者都会选择重构将以实现的服务以这种方式提供,变得更象一些和WSGI配合使用的库而不是一个独立的框架。这样web应用开发这就可以根据特定功能选择最适合的组件,而不是所有功能都由同一个框架提供。

当然,这一天无疑还要等很久,在这之间,一个合适的短期目标就是让任何框架在任何服务器上运行起来。

最后,需要指出的是当前版本的WSGI并没有规定一个应用具体以何种方式部署在web服务器或gateway上。目前,这个需要由服务器或 gateway的具体实现来定义。如果足够多实现了WSGI的服务器或gateway通过领域实践产生了这个需求,也许可以产生另一份PEP来描述 WSGI服务器和应用框架的部署标准。

概述

WSGI接口有两种形式:一个是针对服务器或gateway的,另一个针对应用程序或框架。

服务器接口请求一个由应用接口提供的可调用的对象,至于该对象是如何被请求的取决与服务器或gateway。我们假定一些服务器或gateway会需要应用程序的部署人员编写一个简短的脚本来启动一个服务器或 gateway的实例,并把应用程序对象提供得服务器,而其他的服务器或gateway需要配置文件或其他机制来指定从哪里导入或者获得应用程序对象。

除了纯粹的服务器/gateway和应用程序/框架,还可以创建实现了这份规格说明书的中间件组件,对于包含他们的服务器他们是应用程序,而对于他们包含的应用程序来说他们是服务器,他们可以用来提供可扩展的API,内容转换,导航和其他有用的功能。

在整个规格说明书中,我们使用短语"一个可调用者"意思是"一个函数,方法,类,或者拥有 __call__

应用程序接口

一个应用程序对象是一个简单的接受两个参数的可调用对象,这里的对象并不是真的需要一个对象实例,一个函数、方法、类、或者带有 __call__,实际上服务器/gateway(而非CGI)确实会产生这样的重复请求。

(注意:虽然我们把他叫做"应用程序"对象,但并不是说程序员要把WSGI当做API来调用,我们假定应用程序开发者仍然使用更高层面上的框架服务来开发应用程序,WSGI是提供给框架和服务器开发者使用的工具,并不打算直接对应用程序开发者提供支持)

这里有两个应用程序对象的示例,一个是函数,另一个是类:

def simple_app(environ, start_response):
    """也许是最简单的应用程序对象"""
    status = '200 OK'
    response_headers = [('Content-type','text/plain')]
    start_response(status, response_headers)
    return ['Hello world!\n']


class AppClass:
    """产生同样的输出,不过是使用一个类来实现

    (注意: 'AppClass' 在这里就是 "application" ,所以对它的调用会'AppClass'的一个实例,
    这个实例做为迭代器再返回"application callable"应该返回的那些值)

    如果我们想使用 'AppClass' 的实例直接作为应用程序对象, 我们就必须实现 ``__call__`` 方法,
    外部通过调用这个方法来执行应用程序, 并且我们需要创建一个实例给服务器或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!\n"

Django开发服务器使用的应用程序接口,django.core.handlers.WSGIHandler类.

class WSGIHandler(base.BaseHandler):
    initLock = Lock()
    request_class = WSGIRequest

    def __call__(self, environ, start_response):
        ##############################
        # 暂时略过
        ##############################
        return response

 

服务器接口

服务器/gateway为每一个http客户端发来的请求都会请求应用程序可调用者一次。为了说明这里有一个CGI gateway,以一个获取应用程序对象的函数实现,请注意,这个例子拥有有限的错误处理,因为默认情况下没有被捕获的异常都会被输出到 sys.stderr并被服务器记录下来。

import os, sys

def run_with_cgi(application):

    environ = dict(os.environ.items())
    environ['wsgi.input']        = sys.stdin
    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):
        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
             sys.stdout.write('Status: %s\r\n' % status)
             for header in response_headers:
                 sys.stdout.write('%s: %s\r\n' % header)
             sys.stdout.write('\r\n')

        sys.stdout.write(data)
        sys.stdout.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[0], exc_info[1], exc_info[2]
            finally:
                exc_info = None     # avoid dangling circular ref
        elif headers_set:
            raise AssertionError("Headers already set!")

        headers_set[:] = [status,response_headers]
        return write

    result = application(environ, start_response)
    try:
        for data in result:
            if data:    # body 出现以前不发送headers
                write(data)
        if not headers_sent:
            write('')   # 如果这个时候body为空则发送header
    finally:
        if hasattr(result,'close'):
            result.close()

Django的服务器接口,django.core.servers.WSGIServer类.WSGIServer类继承于Python lib的HTTPServer类.

服务器启动开始与django.core.servers下的run()方法.它启动服务器接口,且传递一个应用程序接口WSGIHandler类的实例给服务器接口.

def run(addr, port, wsgi_handler, ipv6=False):
    server_address = (addr, port)
    httpd = WSGIServer(server_address, WSGIRequestHandler, ipv6=ipv6)
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

class WSGIServer(HTTPServer):
    """BaseHTTPServer that implements the Python WSGI protocol"""
    application = None

    def __init__(self, *args, **kwargs):
        if kwargs.pop('ipv6', False):
            self.address_family = socket.AF_INET6
        HTTPServer.__init__(self, *args, **kwargs)

    def server_bind(self):
        """Override server_bind to store the server name."""
        try:
            HTTPServer.server_bind(self)
        except Exception, e:
            raise WSGIServerException(e)
        self.setup_environ()

    def setup_environ(self):
        # Set up base environment
        env = self.base_environ = {}
        env['SERVER_NAME'] = self.server_name
        env['GATEWAY_INTERFACE'] = 'CGI/1.1'
        env['SERVER_PORT'] = str(self.server_port)
        env['REMOTE_HOST']=''
        env['CONTENT_LENGTH']=''
        env['SCRIPT_NAME'] = ''

    def get_app(self):
        return self.application

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

中间件:同时扮演两种角色的组件

注意到单个对象可以作为请求应用程序的服务器存在,也可以作为被服务器调用的应用程序存在。这样的中间件可以执行这样一些功能:

  • 重写前面提到的 environ
  • 允许多个应用程序和框架在同一个进程中运行
  • 通过在网络传递请求和响应,实现负载均衡和远程处理
  • 对内容进行后加工,比如附加xsl样式表

中间件的存在对于服务器接口和应用接口来说都应该是透明的,并且不需要特别的支持。希望在应用程序中加入中间件的用户只需简单得把中间件当作应用提供给服务器,并配置中间件足见以服务器的身份来请求应用程序。

当然,中间件组件包裹的可能是包裹应用程序的另一个中间件组件,这样循环下去就构成了我们称为"中间件堆栈"的东西了。 for the most part,中间件要符合应用接口和服务器接口提出的一些限制和要求,有些时候这样的限制甚至比纯粹的服务器或应用程序还要严格,这些地方我们会特别指出。

这里有一个中间件组件的例子,他用Joe Strout的piglatin.py将text/plain的响应转换成pig latin(注意:真正的中间件应该使用更加安全的方式——应该检查内容的类型和内容的编码,同样这个简单的例子还忽略了一个单词might be split across a block boundary的可能性)。

from piglatin import piglatin

class LatinIter:

    """如果可以的话,将输出转换为piglatin格式

    Note that the "okayness" can change until the application yields
    its first non-empty string, 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())
        else:
            return self._next()

class Latinator:

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

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

    def __call__(environ, start_response):

        transform_ok = []

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

            # Reset ok flag, in case this is a repeat call
            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))
                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 ,但并非必须取这个名字。服务器或gateway必须用这两个参数请求应用程序对象(比如象上面展示的,这样调用 result = application(environ,start_response)

参数 environ,应用程序可以以任何他愿意的方式修改这个字典, environ

start_response方便说明,我们分别把他们命名为 status, response_headers ,和 exc_info 。应用程序必须用这些参数来请求可调用者 start_response (比如象这样 start_response(status,response_headers)

参数 status。而response_headers。可选的 exc_info 参数会在下面的 `The start_response() Callable`_ 和 Error Handling 两节中描述,他只有在应用程序产生了错误并希望在浏览器上显示错误的时候才有用。

start_response 可调用者必须返回一个 write(body_data)(注意:提供可调用者 write() 只是为了支持现有框架的必要的输出API,新的应用程序或框架尽量避免使用,详细情况请看 Buffering and Streaming 一节。)

当被服务器请求的时候,应用程序对象必须返回一个0或多个可迭代的字符串,这可以通过多种方法完成,比如返回一个字符串的列表,或者应用程序本身是一个生产字符串的函数,或者应用程序是一个类而他的实例是可迭代的,不管怎么完成,应用程序对象必须总是返回0或多个可迭代的字符串。

服务器必须将产生的字符串以一种无缓冲的方式传送到客户端,每次传完一个字符串再去获取下一个。(换句话说,应用程序应该实现自己的缓冲,更多关于应用程序输出必须如何处理的细节请阅读下面的 Buffering and Streaming 节。)

服务器或gateway应该把产生的字符串当字节流对待:特别地,他必须保证没修改行的结尾。应用程序负责确保字符串是以与客户端匹配的编码输出(服务器 /gateway可能会附加HTTP传送编码,或者为了实现一些http的特性而进行一些转换比如byte-range transmission,更多细节请看下面的 Other HTTP Features )

如果调 len(iterable) 成功,服务器将认为结果是正确的。也就是说,应用程序返回的可迭代的字符串提供了一个有用 的__len__() 方法,么肯定返回了正确的结果(关于这个方法正常情况下如何被使用的请阅读 Handling the Content-Length Header )

如果应用程序返回的可迭代者有close()方法,则不管该请求是正常结束还是由于错误而终止,服务器/gateway都**必须**在结束该请求之前调用这个方法,(这是用来支持应用程序对资源的释放,This protocol is intended to complement PEP 325's generator support, and other common iterables with close() methods.)

(注意:应用程序必须在可迭代者产生第一个字符串之间请求 start_response() 可调用者,这样服务器才能在发送任何主体内容之前发送响应头,然而这一步也可以在可迭代者第一次迭代的时候执行,所以服务器不能假定开始迭代之前 start_response()

最后,服务器或gateway不能应用程序返回的可迭代者的任何其他属性,除非是针对服务器或gateway特定类型的实例,比如wsgi.file_wrapper返回的“file wrapper”(阅读 Optional Platform-Specific File Handling )。通常情况下,只有在这里指定的属性,或者通过PEP 234 iteration APIs才是可以访问的。

 

environ 变量

environ 字典被用来包含这些在Common Gateway Interface specification [2]_中定义了的CGI环境变量。下面这些变量 必须 呈现出来, 除非其值是空字符串,这种情况下如果下面没有特别指出的话他们 可能 会被忽略

REQUEST_METHOD HTTP请求的方式, 比如 "GET" 或者 "POST". 这个不可能是空字符串并且也是必须给出的。 SCRIPT_NAME 请求URL中路径的开始部分,对应应用程序对象,这样应用程序就知道它的虚拟位置。如果该应用程序对应服务器的 根 的话, 它 可能 是为空字符串。

PATH_INFO 请求URL中路径的剩余部分,指定请求的目标在应用程序内部的虚拟位置。如果请求的目标是应用程序跟并且没有trailing slash的话,可能为空字符串 。 QUERY_STRING 请求URL中跟在"?"后面的那部分,可能为空或不存在. CONTENT_TYPE HTTP请求中任何 Content-Type CONTENT_LENGTH HTTP请求中任何 Content-Length SERVER_NAME, SERVER_PORT 这些变量可以和 SCRIPT_NAME、PATH_INFO 一起组成完整的URL。然而要注意的是,重建请求URL的时候应该优先使用 HTTP_HOST 而非 SERVER_NAME 。详细内容请阅读下面的 URL Reconstruction 。 SERVER_NAME 和 SERVER_PORT

SERVER_PROTOCOL 客户端发送请求所使用协议的版本。通常是类似 "HTTP/1.0" 或 "HTTP/1.1" 的东西可以被用来判断如何处理请求headers。(既然这个变量表示的是请求中使用的协议,而且和服务器响应时使用的协议无关,也许它应该被叫做 REQUEST_PROTOCOL HTTP_ 对应客户端提供的HTTP请求headers (也就是说名字以 "HTTP_"

服务器或gateway 应该 尽可能提供其他可用的CGI变量。另外,如果用了SSL,服务器或 gateway也 应该 尽可能提供可用的Apache SSL环境变量 [5] ,比如 HTTPS=on 和 SSL_PROTOCOL``。不过要注意,使用了任何上面没有列出的变量的应用程序对不支持相关扩展 的服务器来说就有点necessarily non-portable。(比如,不发布文件的web服务器就不能提供一 个有意义的 ``DOCUMENT_ROOT 或 PATH_TRANSLATED

一个支持WSGI的服务器或gateway 应该 在描述它们自己的同时说明它们可以提供些什么变量应用程序 应该 对所有他们需要的变量的存在性进行检查,并且在某变量不存在的时候有备用的措施

注意: 不需要的变量 (比如在不需要验证的情况下的 REMOTE_USER ) 应该被移出 environ 字典。同样注意CGI定义的变量如果存在的话必须是字符串。任何 str

除了CGI定义的变量, environ 字典也可以包含任意操作系统的环境变量,并且 必须 包含下面这些WSGI定义的变量:

wsgi.version

元组 (1,0), 表明WSGI 版本 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.


一个字符串,代表应用程序Url地址的协议部分.一般来说,此字符串的值是”http”或”https”.

wsgi.input

An input stream (file-like object) from which the HTTP request body 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.)


可读取Request body的类文件对象输入流.(根据其偏好,server/gateway可能会在应用程序要求时才读取此输入流,或者把客户端的request body缓存在内存或硬盘中,或者使用其他技术来提供此输入流.)

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"

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

最后 environ 字典也可以包含服务器定义的变量。这些变量的名字必须是小写字母、数字、点和下划线,并且应该带一个能唯一代表服务器或gateway的前缀。比如, 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)

errors

 

 

每个方法的语义如果上面没有特别指出均和Python Library Reference记载的一样:

  1. The server is not required to read past the client's specified Content-Length, and is allowed to 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
  2. The optional "size" argument to readline()
  3. Note that the hint argument to readlines()
  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()

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

start_response()可调用者

传给应用程序对象的第二个参数是一个形为 start_response(status,response_headers,exc_info=None) 的可调用者. (As with all WSGI callables, the arguments must be supplied positionally, not by keyword.  所有的WSGI定义可调用对象,参数必须是位置参数,不能是字典参数.) start_response 可调用者是用来开始HTTP响应,它必须返回一个 write(body_data) 可调用者 (阅读下面的 Buffering and Streaming).

status``参数是一个HTTP "status" 字符串,比如 ``"200 OK" 或 "404 Not Found". 也就是说,他是一个由状态编号和具体信息组成的字符串,按这个顺序并用空格隔开,两头没有其他空格和其他字符。 (更多信息请阅读RFC 2616, Section 6.1.1) 该字符串 禁止 包含控制字符,也不允许以回车、换行或他们的组合结束。

response_headers``参数是一个 ``(header_name,header_value) 元组的列表。它必须是一个Python列表;也就是说 type(response_headers) is ListType,并且服务器 可以 以任何方式改变其内容。每一个 header_name

每一个 header_value 禁止 包含 任何 控制字符,包括回车或换行。 (这些要求是要使得那些必须检查或修改响应头的服务器、gateway、响应处理中间件所必须执行的解析工作的复杂性降到最低。)