Bootstrap

wsgiref 源代码分析 --start_response()

http://blog.csdn.net/on_1y/article/details/18818081

http://blog.csdn.net/on_1y/article/details/18803563

wsgi有两方,服务器方 和 应用程序

        ①服务器方:其调用应用程序,给应用程序提供(环境信息)和(回调函数), 这个回调函数是用来将应用程序设置的http header和status等信息传递给服务器方.

②应用程序:用来生成返回的header,body和status,以便返回给服务器方


     用Python语言写的一个符合WSGI的“Hello World”应用程序如下所示:

<span class="kw1" style="color:#ff770;font-weight: bold;">def</span> app<span class="br0">(</span>environ<span class="sy0" style="color:#66cc66;">,</span> start_response<span class="br0">)</span>:
    start_response<span class="br0">(</span><span class="st0" style="color:#483d8b;">'200 OK'</span><span class="sy0" style="color:#66cc66;">,</span> <span class="br0">[</span><span class="br0">(</span><span class="st0" style="color:#483d8b;">'Content-Type'</span><span class="sy0" style="color:#66cc66;">,</span> <span class="st0" style="color:#483d8b;">'text/plain'</span><span class="br0">)</span><span class="br0">]</span><span class="br0">)</span>
    <span class="kw1" style="color:#ff770;font-weight: bold;">yield</span> <span class="st0" style="color:#483d8b;">"Hello world!<span class="es0" style="color:#0099;font-weight: bold;">\n</span>"</span>

其中

  • 第一行定义了一个名为app的应用程序,接受两个参数,environ和start_response,environ是一个字典包含了CGI中的环境变量,start_response也是一个callable,接受两个必须的参数,status(HTTP状态)和response_headers(响应消息的头)。
  • 第二行调用了start_response,状态指定为“200 OK”,消息头指定为内容类型是“text/plain”
  • 第三行将响应消息的消息体返回。

   那通俗点来将的话:
wsgi中的服务器方:我们可以理解成是webserver,当然这个webserver可以是外置的,比如lighttpd,也可以是python自己写的。
而应用程序说白了就是:请求的统一入口!所有的请求都进入到这个app中来处理! 这个app说白了就是一个函数!!(类中的__call__是一样的道理)

=========================================================================================================

WSGI应用

WSGI应用其实就是一个callable的对象。举一个最简单的例子,假设存在如下的一个应用:

?
1
2
3
4
5
6
7
8
def application(environ, start_response): 
  status = '200 OK' 
  output = 'World!' 
  response_headers = [('Content-type', 'text/plain'), 
                      ('Content-Length', str(12)] 
  write = start_response(status, response_headers) 
  write('Hello '
  return [output]

这个WSGI应用简单的可以用简陋来形容,但是他的确是一个功能完整的WSGI应用。只不过给人留下了太多的疑点,environ是什么?start_response是什么?为什么可以同时用write和return来返回内容?

对于这些疑问,不妨自己猜测一下他的作用。联想到CGI,那么environ可能就是一系列的环境变量,用来表示HTTP请求的信息,比如说method 之类的。start_response,可能是接受HTTP response头信息,然后返回一个write函数,这个write函数可以把HTTP response的body返回给客户端。return自然是将HTTP response的body信息返回。不过这里的write和函数返回有什么区别?会不会是其实外围默认调用write对应用返回值进行处理?而且为什么 应用的返回值是一个列表呢?说明肯定存在一个对应用执行结果的迭代输出过程。难道说他隐含的支持iterator或者generator吗?

等等,应用执行结果?一个应用既然是一个函数,说明肯定有一个对象去执行它,并且可以猜到,这个对象把environ和start_response传给应用,将应用的返回结果输出给客户端。那么这个对象是什么呢?自然就是WSGI容器了。


这里有很多问题貌似没解决:
1:wsgi一端的webserver是什么,这个貌似是很弱智的问题。比如lighttd就是一个webserver,那我们在lighttpd配置文件中可以指定程序的入口脚本(比如index.php).那么所有请求都会转到这个index.php来执行。  那gunicorn呢?其虽然全部都是用python写的,但是原理和lighttpd一样的,内部都是开socket来监听请求,而后将请求转到后端的python脚本中去执行!当然了:其是转到一个函数中去执行而已!所以我们要做的就是实现这个函数,而后将之加载到gunicorn中,而后启动gunicorn即可!!!!

 

WSGI详解

注意:以 点 开始的解释是WSGI规定 必须满足 的。

应用程序

  • 应用程序是可调用对象
  • 可调用对象有两个位置参数
    所谓位置参数就是调用的时候,依靠位置来确定参数的语义,而不是参数名,也就是说服务 器调用应用程序时,应该是这样:

 

application(env, start_response)

而不是这样:

 

application(start_response=start_response, environ=env)

所以,参数名其实是可以随便起的,只不过为了表义清楚,我们起了environ 和 start_response

  • 第一个参数environ是Python内置的dict对象,应用程序可以对这个参数任意修改。
  • environ参数必须包含 WSGI 需要的一些变量(详见后文)
    也可以包含一些扩展参数,命名规范见后文
  • start_response参数是一个可调用对象。接受两个位置参数,一个可选参数。 例如:start_response(status, response_headers, exc_info=None) status参数是状态码,例如 200 OK 。

response_headers参数是一个列表,列表项的形式为(header_name, header_value)。

exc_info参数在错误处理的时候使用。

status和response_headers的具体内容可以参考 HTTP 协议 Response部分

  • start_response必须返回一个可调用对象: write(body_data)
  • 应用程序必须返回一个可迭代对象。
  • 应用程序不应假设返回的可迭代对象被遍历至终止,因为遍历过程可能出现错误。
  • 应用程序必须在第一次返回可迭代数据之前调用 start_response 方法。
    这是因为可迭代数据是 返回数据的 body 部分,在它返回之前,需要使用 start_response 返回  response_headers 数据。

start_response()

start_response是HTTP响应的开始,它的形式为:

 

start_response(status, response_headers, exc_info=None)

返回一个可调用对象,这个可调用对象形式为:

 

write(body_data)

status 表示 HTTP 状态码,例如 "200 OK", "404 Not Found",它们在 RFC 2616中定义,status禁止包含控制字符。

response_headers 是一个列表,列表项是一个二元组: (header_name, heaer_value) , 每个 header_name 都必须是 RFC 2616 4.2 节中定义的HTTP 头部名。header_value  禁止包含控制字符。

另外,服务器程序必须保证正确的headers 被返回给客户端,如果应用程序没有返回headers,服务器必须添加它。

应用程序和middleware禁止使用 HTTP/1.1 中的 "hop-by-hop"特性,以及其它可能影响客户端与服务器永久连接的特性。

start_response 被调用时,服务器应该检查 headers 中的错误,另外,禁止 start_response直接将 response_headers传递给客户端,它必须把它们存储起来,一直到应用程序第一次迭代返回一个非空数据后,才能将response_headers传递给客户端。这其实是在说,HTTP响应body部分必须有数据,不能只返回一个header。

start_response的第三个参数是一个可选参数,exc_info,它必须和Python的 sys.exc_info()返回的数据有相同类型。当处理请求的过程遇到错误时,这个参数会被设置,同时调用 start_response。如果提供了exc_info,但是HTTP headers 还没有输出,那么 start_response需要将当前存储的 HTTP response headers替换成一个新值。但是,如果提供了exc_info,同时 HTTP headers已经输出了,那么 start_response  必须 raise 一个 error。禁止应用程序处理 start_response raise出的 exceptions,应该交给服务器程序处理。

当且仅当提供 exc_info参数时,start_response才可以被调用多于一次。换句话说,要是没提供这个参数,start_response在当前应用程序中调用后,禁止再调用。

为了避免循环引用,start_response实现时需要保证 exc_info在函数调用后不再包含引用。 也就是说start_response用完 exc_info后,需要保证执行一句

 

exc_info = None

这可以通过 try/finally实现。

 

 

;