原文:https://legacy.python.org/dev/peps/pep-3333
PEP:3333
標(biāo)題:PythonWeb服務(wù)器網(wǎng)關(guān)接口v1.0.1
版本:f44a2ade62d8
最后修改期:2011-01-16 09:57:25 +0000
作者: P.J. Eby <pje at telecommunity.com>
討論: Python Web-SIG
狀態(tài): 最終版
類型: 報(bào)告性的
內(nèi)容類型: text/x-rst
創(chuàng)作于: 2010年9月26日
公布?xì)v史: 2010年9月26日, 2010年10月4日
替代: 333
- 致PEP333讀者的前言
- 摘要
- 原理和目標(biāo) (來(lái)自 PEP 333)
- 規(guī)范概述
- 一條關(guān)于字符串類型的筆記
- 應(yīng)用/框架端
- 服務(wù)器/網(wǎng)關(guān)端
- 中間件: 兩邊都起作用的元素
- 規(guī)范細(xì)節(jié)
-
環(huán)境 變量
- 輸入和錯(cuò)誤流
- 可調(diào)用 start_response()
- 處理 內(nèi)容長(zhǎng)度 頭
- 緩沖和流
- 塊邊界的中間件處理。
- 可調(diào)用 write()
- Unicode的問(wèn)題
- 錯(cuò)誤處理
- HTTP 1.1預(yù)期/繼續(xù)
- 其他HTTP特性
- 線程的支持
-
環(huán)境 變量
- 實(shí)現(xiàn)/應(yīng)用筆記
- 服務(wù)器擴(kuò)展接口
- 應(yīng)用程序配置
- URL重建
- 支持更早版本(<2.2)的Python
- 可選的特定于平臺(tái)的文件處理
- 提問(wèn)與回答
- 被提議的/正在討論的
- 知識(shí)
- 偏好
- 版權(quán)
致PEP333讀者的前言
這是PEP 333的一個(gè)更新版本, 稍微修改了一下以改進(jìn)在python3下的使用,也將一些長(zhǎng)期的實(shí)際的修正劃進(jìn)WSGI協(xié)議 (它的代碼示例也被移植到Python 3中。)
而由于程序上的原因, 這一定是一份獨(dú)特的PEP,沒(méi)做任何改變讓以前符合python2的服務(wù)器和應(yīng)用失效。如果你的Python2服務(wù)器和應(yīng)用是符合PEP 333的,那么它也符合這份PEP。
但是,在Python3下,你的應(yīng)用和服務(wù)器必須遵循那些在標(biāo)題下面章節(jié)中概述的規(guī)則: “一條關(guān)于字符串類型的筆記”和“Unicode的問(wèn)題”。
要獲取細(xì)節(jié),可以一行行對(duì)比這篇文檔和PEP333,你也可以瀏覽它的歷史SVN版本,從版本84854往前看。
摘要
本文檔指定一份推薦的web服務(wù)器與python應(yīng)用/框架之間的標(biāo)準(zhǔn)接口,使得web應(yīng)用在多種web服務(wù)器之間具有可移植性。
原理和目標(biāo) (來(lái)自 PEP 333)
Python目前有多種web應(yīng)用框架,列舉幾個(gè)比如Zope,Quixote,Webware,SkunkWeb,PSO和Twisted Web。對(duì)于python新手來(lái)說(shuō),多種選擇會(huì)是一個(gè)問(wèn)題,因?yàn)榭偟膩?lái)說(shuō),他們的web框架的選擇會(huì)限制他們對(duì)能用的web服務(wù)器的選擇,反之亦然。
相對(duì)而言,雖然Java只有不多的web應(yīng)用框架能用,但是Java的“servlet”API讓用任何Java web應(yīng)用框架寫的應(yīng)用運(yùn)行在任何一個(gè)支持servlet 接口的web服務(wù)器變成可能。
這樣一個(gè)python web 服務(wù)器的API的有效性和廣泛應(yīng)用會(huì)將對(duì)框架的選擇從對(duì)web服務(wù)器的選擇中分離出來(lái),方便用戶選擇適合他們的一對(duì),同時(shí)方便框架開(kāi)發(fā)者和服務(wù)器開(kāi)發(fā)者專注于他們偏好的專業(yè)領(lǐng)域。此處的python web 服務(wù)器包括像Medusa一樣用python寫的,像mod_python一樣內(nèi)嵌python的,或者像CGI, FastCGI等一樣通過(guò)網(wǎng)關(guān)協(xié)議調(diào)用python的。
所以此PEP建議一個(gè)簡(jiǎn)單獨(dú)特的web服務(wù)器和web應(yīng)用或者框架之間的接口:Python Web服務(wù)器網(wǎng)關(guān)接口 (WSGI)。
然而僅有一份WSGI說(shuō)明書對(duì)解決python web應(yīng)用的服務(wù)器和框架的現(xiàn)狀是沒(méi)有卵用的。為了起作用,服務(wù)器和框架作者和維護(hù)人員必須實(shí)際操作WSGI。
然而,因?yàn)闆](méi)有服務(wù)器和框架支持WSGI,所以沒(méi)有給寫WSGI支持的作者的即時(shí)獎(jiǎng)勵(lì)。因此WSGI必須很容易寫,這樣的話一個(gè)作者在這個(gè)接口上的初期投入才相對(duì)低一點(diǎn)。
所以,簡(jiǎn)化服務(wù)器和框架兩邊接口的編寫對(duì)WSGI接口的實(shí)用性來(lái)說(shuō)顯然是最關(guān)鍵的,也因此是任何設(shè)計(jì)決定的主要標(biāo)準(zhǔn)。
然而,注意到對(duì)一個(gè)框架作者來(lái)說(shuō)簡(jiǎn)化編寫并不像對(duì)一個(gè)web應(yīng)用作者來(lái)說(shuō)那么簡(jiǎn)單。WSGI對(duì)框架作者展示了一種完全“無(wú)虛飾”的接口,因?yàn)殁忚K和汽笛比如響應(yīng)對(duì)象和cookie處理只會(huì)阻礙現(xiàn)存框架對(duì)這些問(wèn)題的處理。再次說(shuō)明,WSGI的目標(biāo)是促進(jìn)現(xiàn)存服務(wù)器和應(yīng)用或者框架的友好互連,而不是創(chuàng)造一個(gè)新的web框架。
請(qǐng)注意,這個(gè)目標(biāo)也阻止WSGI去需求任何在已發(fā)布的python版本里不可獲得的東西。因此,新的標(biāo)準(zhǔn)的文庫(kù)模塊不被此說(shuō)明書提議或需求,而且 WSGI里的任何東西都不需要比2.2.2高級(jí)的版本。(當(dāng)然,如果將來(lái)的python版本包含對(duì)這個(gè)在由標(biāo)準(zhǔn)文庫(kù)提供的web服務(wù)器上的接口的支持也是好事)
除了簡(jiǎn)化現(xiàn)有和未來(lái)框架和服務(wù)器的編寫,創(chuàng)造請(qǐng)求預(yù)處理器,響應(yīng)后處理器和其他基于WSGI的中間件元素也應(yīng)該簡(jiǎn)單點(diǎn),這些中間件對(duì)包含它們的服務(wù)器來(lái)說(shuō)看起來(lái)像應(yīng)用,而對(duì)包含它們的應(yīng)用來(lái)說(shuō)則扮演服務(wù)器的角色。
如果中間件能夠既簡(jiǎn)單又強(qiáng)壯,而且WSGI在服務(wù)器和框架中廣泛可用,那么一種完全新型的python web 應(yīng)用框架將變得可能:它包含松耦合的WSGI中間件元素。確實(shí),已存在的框架的作者甚至可能選擇重構(gòu)他們的框架的已有服務(wù)器使之以此種方式被提供:變得更像配合WSGI使用的數(shù)據(jù)庫(kù),而不像完整的框架。這將使得應(yīng)用開(kāi)發(fā)者可以為某個(gè)特定功能選擇最佳組合,而不是不得不考慮一個(gè)簡(jiǎn)單框架的各方各面。
當(dāng)然正如此文所述,這一天無(wú)疑還很遙遠(yuǎn)。在這之前,對(duì)WSGI來(lái)說(shuō)一個(gè)合適的短期目標(biāo)就是讓任意框架在任意服務(wù)器上運(yùn)行起來(lái)。
最后,必須提到的是,目前的WSGI版本沒(méi)有規(guī)定如何部署一個(gè)應(yīng)用與一個(gè)web服務(wù)器或服務(wù)器網(wǎng)關(guān)搭配使用的任何詳細(xì)原理。目前,這必須通過(guò)服務(wù)器和網(wǎng)關(guān)定義。在大量的服務(wù)器和框架編寫WSGI為領(lǐng)域經(jīng)驗(yàn)提供不同的部署需求后,創(chuàng)造另一份PEP來(lái)為WSGI服務(wù)器和應(yīng)用框架描述一個(gè)部署標(biāo)準(zhǔn)是很有意義的。
規(guī)范概述
WSGI接口包括兩部分:服務(wù)器/網(wǎng)關(guān)端和應(yīng)用/框架端。服務(wù)器端調(diào)用一個(gè)由應(yīng)用端提供的可回調(diào)對(duì)象。關(guān)于這個(gè)對(duì)象怎樣提供的細(xì)節(jié)取決于服務(wù)器/網(wǎng)關(guān)端。假定一些服務(wù)器/網(wǎng)關(guān)需要一個(gè)應(yīng)用部署者寫一個(gè)簡(jiǎn)短的腳本來(lái)創(chuàng)建一個(gè)服務(wù)器/網(wǎng)關(guān)實(shí)例然后將應(yīng)用對(duì)象提供給這個(gè)實(shí)例。其他的服務(wù)器和網(wǎng)關(guān)可能會(huì)用配置文件或者其他原理來(lái)規(guī)定改從哪里引入一個(gè)應(yīng)用對(duì)象或者其他方式獲得。
除了純服務(wù)器/網(wǎng)關(guān)和應(yīng)用/框架,也可以創(chuàng)造一些使這個(gè)規(guī)則的兩方面都生效的中間件。這些中間件對(duì)包含的服務(wù)器來(lái)說(shuō)扮演一個(gè)應(yīng)用的角色,對(duì)包含的應(yīng)用來(lái)說(shuō)扮演一個(gè)服務(wù)器的角色,也可以被用來(lái)提供擴(kuò)展API,內(nèi)容轉(zhuǎn)變,導(dǎo)航或者其他有用的功能。
貫穿次說(shuō)明書,我們用“可回調(diào)”這個(gè)術(shù)語(yǔ)來(lái)表示一個(gè)函數(shù),方法,類或者是有call方法的實(shí)例。取決于服務(wù)器,網(wǎng)關(guān),或者應(yīng)用來(lái)執(zhí)行回調(diào)函數(shù)來(lái)為它們的需求選擇適當(dāng)?shù)膱?zhí)行技巧。相反地,一個(gè)調(diào)用回調(diào)函數(shù)的服務(wù)器,網(wǎng)關(guān),或者應(yīng)用對(duì)何種回調(diào)函數(shù)是可用的有任何依賴。回調(diào)函數(shù)只用來(lái)被調(diào)用,而不用來(lái)自省。
一條關(guān)于字符串類型的筆記
總的來(lái)說(shuō),HTTP處理字節(jié),也就是說(shuō)次說(shuō)明書主要是關(guān)于操作字節(jié)。
但是,那些字節(jié)的內(nèi)容經(jīng)常有一些文本釋義,而且,在python中,字符串是處理文本的最方便的方法。
但是在很多python版本和實(shí)現(xiàn)中,字符串是Unicode,而不是字節(jié)。這需要在HTTP環(huán)境下小心平衡可用API與字節(jié)和文本的正確轉(zhuǎn)換,尤其是在提供python 采用不同字符串類型的實(shí)現(xiàn)之間的端口代碼。
- 用作請(qǐng)求/響應(yīng)頭和元數(shù)據(jù)的“本地”字符串,它們總是以稱為“str”的類型執(zhí)行。
- 用作請(qǐng)求和響應(yīng)體的“二進(jìn)制字符串”,在python3中它們稱為字節(jié)類型被使用,其它情況下也是str類型(例如POST/PUT輸入數(shù)據(jù)和HTML頁(yè)面輸出)。
但是不要混淆:即使python的str類型在底層實(shí)際是unicode,本土字符串的內(nèi)容也必須通過(guò)Latin-1 編碼技術(shù)轉(zhuǎn)換成字節(jié)?。ú榭幢疚臋n后面的“Unicode問(wèn)題”部分獲取詳細(xì)信息)
簡(jiǎn)而言之:您在此文中看到的“string”指的都是“本地”字符串,即一個(gè)str類型的對(duì)象,不管它內(nèi)在是作為二進(jìn)制還是作為unicode執(zhí)行的。當(dāng)您讀到了“bytestring”,它應(yīng)該被看做一個(gè)python3下的二進(jìn)制對(duì)象或者是python2下的str對(duì)象。
因此,即使HTTP在某種意義上就是“bytes”,通過(guò)python的默認(rèn)的str類型也很方便獲取很多API。
應(yīng)用/框架端
應(yīng)用對(duì)象僅僅是一個(gè)接受兩個(gè)參數(shù)的可回調(diào)對(duì)象。術(shù)語(yǔ)“對(duì)象”不應(yīng)該被曲解為需要一個(gè)實(shí)際的對(duì)象實(shí)例:一個(gè)函數(shù),方法,類,或者有call方法的實(shí)例都可被用作一個(gè)應(yīng)用對(duì)象。應(yīng)用對(duì)象必須可以多次調(diào)用,事實(shí)上所有的服務(wù)器/網(wǎng)關(guān)(除了CGI)都會(huì)進(jìn)行此種重復(fù)請(qǐng)求。
(注意:即使我們將它稱為“應(yīng)用”對(duì)象,它也不應(yīng)該被理解為表示應(yīng)用開(kāi)發(fā)者會(huì)用WSGI作為程序的API!人們認(rèn)為應(yīng)用開(kāi)發(fā)者會(huì)繼續(xù)使用現(xiàn)存的高水平框架服務(wù)器來(lái)開(kāi)發(fā)應(yīng)用。WSGI是框架和服務(wù)器開(kāi)發(fā)者的一個(gè)工具,不是打算用來(lái)直接支持應(yīng)用開(kāi)發(fā)者的。)
下面是兩個(gè)應(yīng)用對(duì)象的例子;一個(gè)是一個(gè)函數(shù),另一個(gè)是一個(gè)類:
HELLO_WORLD = b"Hello world!\n"
def simple_app(environ, start_response):
"""Simplest possible application object最簡(jiǎn)單的應(yīng)用對(duì)象"""
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
產(chǎn)生同樣的輸出,但是是用一個(gè)類
(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”在此處是應(yīng)用,所以叫它返回一個(gè)AppClass的實(shí)例, 就像說(shuō)明書要求的那樣,然后這個(gè)實(shí)例成為應(yīng)用回調(diào)函數(shù)的可迭代返回值。
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的實(shí)例來(lái)作為應(yīng)用對(duì)象,我們得寫一個(gè)__call__方法,這個(gè)方法會(huì)被調(diào)用來(lái)運(yùn)行應(yīng)用,而且我們會(huì)需要?jiǎng)?chuàng)建一個(gè)實(shí)例被服務(wù)器或者網(wǎng)關(guān)使用。
"""
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
服務(wù)器/網(wǎng)關(guān)端
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 <tt class="docutils literal">sys.stderr</tt> and logged by the web server.一旦服務(wù)器/網(wǎng)關(guān)接收到來(lái)自HTTP客戶端的每個(gè)請(qǐng)求,就調(diào)用應(yīng)用回調(diào)函數(shù),這是在應(yīng)用中控制的。比如說(shuō),有一個(gè)簡(jiǎn)單的CGI網(wǎng)關(guān)被當(dāng)做一個(gè)函數(shù)來(lái)獲取應(yīng)用對(duì)象。注意這個(gè)簡(jiǎn)單的例子限制了報(bào)錯(cuò)處理,因?yàn)槟J(rèn)一個(gè)沒(méi)被捕獲的錯(cuò)誤會(huì)被歸為sys.stderr然后由服務(wù)器打印日志。
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()
中間件:兩邊都起作用的元素
注意到一個(gè)簡(jiǎn)單的對(duì)象可能扮演一個(gè)與一些應(yīng)用有關(guān)的服務(wù)器的角色,同時(shí)也扮演一個(gè)與一些服務(wù)器有關(guān)的應(yīng)用的角色。這種中間件元素可以表現(xiàn)出以下功能:
- 在相應(yīng)地重寫environ后基于目標(biāo)URL引導(dǎo)請(qǐng)求到不同的應(yīng)用對(duì)象。
- 允許多種應(yīng)用或框架在同一個(gè)進(jìn)程中同時(shí)運(yùn)行。
- 通過(guò)在網(wǎng)上推進(jìn)請(qǐng)求與響應(yīng)加載平衡和遠(yuǎn)程進(jìn)程。
- 呈現(xiàn)內(nèi)容后處理,比如獲取層疊樣式表CSS。
中間件的存在已經(jīng)轉(zhuǎn)移到了接口的“服務(wù)器/網(wǎng)關(guān)端”和“應(yīng)用/框架端”,也不需要特殊的支持。想要將中間件合并到應(yīng)用的用戶僅需提供中間件給服務(wù)器,就像它是一個(gè)應(yīng)用,配置中間件去調(diào)用應(yīng)用,就像它是一個(gè)服務(wù)器。當(dāng)然,中間件包裹的應(yīng)用實(shí)際上可能是另一個(gè)中間件元素包裹另一個(gè)應(yīng)用等等,創(chuàng)造一種稱為“中間件堆疊”的東西。
在極大程度上,中間件必須確認(rèn)WSGI服務(wù)器和應(yīng)用兩端的限制條件和需要。雖然有時(shí)候,中間件的需求比單純的服務(wù)器或者應(yīng)用更加嚴(yán)格,這些細(xì)節(jié)會(huì)在說(shuō)明書上注明。
下面是一個(gè)(不是實(shí)際的)將text/plain響應(yīng)轉(zhuǎn)變成大latin的中間件元素的例子,此例使用Joe Strout的piglatin.py。(注意:一個(gè)真實(shí)的中間件可能會(huì)用一種更強(qiáng)健的方式來(lái)檢查內(nèi)容的類型,也應(yīng)該檢查內(nèi)容編碼。而且,這個(gè)簡(jiǎn)單的例子忽略了一個(gè)詞語(yǔ)可能通過(guò)一塊內(nèi)容被分離的可能性)
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))
說(shuō)明細(xì)節(jié)
應(yīng)用對(duì)象必須接收兩個(gè)位置參數(shù),為了說(shuō)明,我們將它們命名為environ和start_response,但是它們不需要被命名。一個(gè)服務(wù)器/網(wǎng)關(guān)必須用位置(不是關(guān)鍵字)參數(shù)調(diào)用應(yīng)用對(duì)象。(例如:上面所示的賦值result = application(environ, start_response)。)
參數(shù)environ是一個(gè)字典對(duì)象,包含CGI類環(huán)境變量。這個(gè)對(duì)象必須是一個(gè)內(nèi)嵌的python字典(不是一個(gè)子類,用戶詞典或者其他虛擬字典),而且這個(gè)對(duì)象可以以任何它希望的方式修飾這個(gè)字典。這個(gè)字典必須也包含確定的WSGI需要的變量(后面的部分會(huì)介紹),而且也可能包含服務(wù)器特定的擴(kuò)展變量,同過(guò)下面會(huì)介紹到的一個(gè)習(xí)慣命名。
Start_response參數(shù)是一個(gè)接收兩個(gè)必須位置參數(shù)可回調(diào)函數(shù),也是一個(gè)可選參數(shù)。為了說(shuō)明,我們將這些參數(shù)命名為status,response_headers和exc_info,但是它們本不需要被命名,而且應(yīng)用必須用位置參數(shù)調(diào)用start_response回調(diào)函數(shù)(例如:start_response(status,response_headers)。)
參數(shù)status是一個(gè)“999 Message here”表里的狀態(tài)字符串,response_headers是一個(gè)描述HTTP響應(yīng)頭的(header_name, header_value)形式的元組的列表??蛇x參數(shù)exc_info在下面的“start_response() 回調(diào)函數(shù)”和“錯(cuò)誤處理”部分會(huì)講到。只有在應(yīng)用捕獲到錯(cuò)誤并試圖向?yàn)g覽器展示一天錯(cuò)誤信息的時(shí)候才會(huì)用到它。
Start_response回調(diào)函數(shù)必須返回一個(gè)有一個(gè)位置參數(shù)的write(body_data)回調(diào)函數(shù):一個(gè)二進(jìn)制字符串被寫進(jìn)HTTP響應(yīng)體。(注意:write()回調(diào)函數(shù)只用來(lái)支持特定的現(xiàn)存的框架的必要輸出API;如果可以避免的話它不應(yīng)該被新的應(yīng)用和框架使用。欲知詳情看Buffering和Streaming部分。)
當(dāng)被服務(wù)器調(diào)用的時(shí)候,應(yīng)用對(duì)象必須返回一個(gè)可迭代的屈從零或者更多的二進(jìn)制字符串。這可以通過(guò)多種方式實(shí)現(xiàn),比如通過(guò)返回一個(gè)二進(jìn)制字符串的列表,或者通過(guò)把應(yīng)用當(dāng)做輸出二進(jìn)制字符串的生成器函數(shù)來(lái)用,或者通過(guò)將應(yīng)用當(dāng)做一個(gè)實(shí)例是迭代器的類。不管是如何實(shí)現(xiàn)的,應(yīng)用對(duì)象都必須總是返回一個(gè)可迭代的屈從零或者更多的二進(jìn)制字符串。
服務(wù)器/網(wǎng)關(guān)必須傳輸輸出的二進(jìn)制字符串到一個(gè)無(wú)緩沖機(jī)制里的客戶端,在請(qǐng)求另一個(gè)二進(jìn)制字符串之前要先完成這個(gè)的傳輸。(換句話說(shuō),應(yīng)用應(yīng)該表現(xiàn)自己的緩沖作用,看下面的“緩沖”和“流”部分來(lái)了解關(guān)于必須怎么樣處理應(yīng)用輸出的信息。)
服務(wù)器/網(wǎng)關(guān)應(yīng)該將屈從二進(jìn)制字符串看成是二進(jìn)制的字節(jié)結(jié)果:特別是應(yīng)該保證行尾不可選。應(yīng)用得負(fù)責(zé)保證二進(jìn)制字符串是以適合客戶端的形式書寫的。(為了實(shí)現(xiàn)例如字節(jié)范圍轉(zhuǎn)換這樣的http特征,服務(wù)器/網(wǎng)關(guān)可能應(yīng)用http轉(zhuǎn)換編碼,或者表現(xiàn)其他的轉(zhuǎn)換??聪旅娴摹捌渌鸋TTP特點(diǎn)”來(lái)獲取更多細(xì)節(jié))
如果調(diào)用len成功,服務(wù)器必須能夠依賴精確的結(jié)果。也就是說(shuō),如果應(yīng)用返回的迭代器提供一個(gè)有效的len()方法,那它必須返回一個(gè)精確的結(jié)果。(看“處理‘內(nèi)容長(zhǎng)度’頭”部分來(lái)獲取關(guān)于這通常是怎樣使用的信息。)
如果應(yīng)用返回的迭代器有一個(gè)close()方法,那么一旦完成當(dāng)前請(qǐng)求后服務(wù)器/網(wǎng)關(guān)必須調(diào)用這個(gè)方法,不管請(qǐng)求是正常完成,還是在迭代或者是與服務(wù)器的提前斷線過(guò)程中由于應(yīng)用錯(cuò)誤提前終止了。close()方法需要支持由應(yīng)用釋放的資源。此協(xié)議試圖用close()方法完成PEP 342的生成器支持,還有其他的公共迭代器。
應(yīng)用返回一個(gè)生成器或者其他的習(xí)慣迭代器不應(yīng)該假定整個(gè)迭代器都被消耗,因?yàn)樗赡鼙环?wù)器早早地關(guān)掉。
(注意:應(yīng)用必須在迭代器輸出它的第一條體二進(jìn)制字符串之前調(diào)用start_response()回調(diào)函數(shù),這樣服務(wù)器才能在任意體內(nèi)容前發(fā)送頭信息。然而,這次調(diào)用可能由迭代器的第一次迭代實(shí)現(xiàn),所以服務(wù)器不能假定start_response()方法在迭代器上開(kāi)始迭代前被調(diào)用了。)
最后,服務(wù)器/網(wǎng)關(guān)禁止直接使用任何其他的應(yīng)用返回的迭代器的貢獻(xiàn),除非它對(duì)那個(gè)服務(wù)器/網(wǎng)關(guān)來(lái)說(shuō)明確是一個(gè)類型實(shí)例,例如一個(gè)由wsgi.file_wrapper返回的文件裝飾器(看“可選平臺(tái)-特定的文件處理”部分)。在通常情況下,只有屬性明確了,或者通過(guò)例如PEP 234里面的迭代器接口可獲得才行。
環(huán)境變量
環(huán)境子彈需要包含這些CGI環(huán)境變量,就像公共網(wǎng)關(guān)接口協(xié)議定義的那樣。下列變量必須展示,除非他們的值是一個(gè)空的字符串,在某些情況下他們可能會(huì)被忽略,除了下面標(biāo)出的情況。
???????HTTP請(qǐng)求方法,例如“GET”或者“POST”。這個(gè)不可能是一個(gè)空的字符串,所以一直是必須的。
???????路由路徑的最初的部分對(duì)應(yīng)應(yīng)用對(duì)象,這樣的話應(yīng)用才知道它的虛擬的位置。如果應(yīng)用對(duì)應(yīng)的是服務(wù)器的根目錄的話這個(gè)可能是一個(gè)空的字符串。
<dt><tt class="docutils literal">QUERY_STRING</tt></dt>
<dd>The portion of the request URL that follows the <tt class="docutils literal">"?"</tt>, if any. May be empty or absent.</dd>
<dt><tt class="docutils literal">CONTENT_TYPE</tt></dt>
<dd>The contents of any <tt class="docutils literal">Content-Type</tt> fields in the HTTP request. May be empty or absent.</dd>
<dt><tt class="docutils literal">CONTENT_LENGTH</tt></dt>
<dd>The contents of any <tt class="docutils literal">Content-Length</tt> fields in the HTTP request. May be empty or absent.</dd>
<dt><tt class="docutils literal">SERVER_NAME</tt>, <tt class="docutils literal">SERVER_PORT</tt></dt>
<dd>When combined with <tt class="docutils literal">SCRIPT_NAME</tt> and <tt class="docutils literal">PATH_INFO</tt>, these two strings can be used to complete the URL. Note, however, that <tt class="docutils literal">HTTP_HOST</tt>, if present, should be used in preference to <tt class="docutils literal">SERVER_NAME</tt> for reconstructing the request URL. See the URL Reconstruction section below for more detail. <tt class="docutils literal">SERVER_NAME</tt> and <tt class="docutils literal">SERVER_PORT</tt> can never be empty strings, and so are always required.</dd>
<dt><tt class="docutils literal">SERVER_PROTOCOL</tt></dt>
<dd>The version of the protocol the client used to send the request. Typically this will be something like <tt class="docutils literal">"HTTP/1.0"</tt> or <tt class="docutils literal">"HTTP/1.1"</tt> and may be used by the application to determine how to treat any HTTP request headers. (This variable should probably be called <tt class="docutils literal">REQUEST_PROTOCOL</tt>, 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.)</dd>
<dt><tt class="docutils literal">HTTP_</tt> Variables</dt>
<dd>Variables corresponding to the client-supplied HTTP request headers (i.e., variables whose names begin with <tt class="docutils literal">"HTTP_"</tt>). The presence or absence of these variables should correspond with the presence or absence of the appropriate HTTP header in the request.</dd>
</dl>
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 <tt class="docutils literal">HTTPS=on</tt> and <tt class="docutils literal">SSL_PROTOCOL</tt>. 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 <tt class="docutils literal">DOCUMENT_ROOT</tt> or <tt class="docutils literal">PATH_TRANSLATED</tt>.)
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 <tt class="docutils literal">REMOTE_USER</tt> when no authentication has occurred) should be left out of the <tt class="docutils literal">environ</tt> 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 <tt class="docutils literal">str</tt>.
In addition to the CGI-defined variables, the <tt class="docutils literal">environ</tt> dictionary may also contain arbitrary operating-system "environment variables", and mustcontain the following WSGI-defined variables:
Variable | Value |
---|---|
<tt class="docutils literal">wsgi.version</tt> | The tuple <tt class="docutils literal">(1, 0)</tt>, representing WSGI version 1.0. |
<tt class="docutils literal">wsgi.url_scheme</tt> | A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value <tt class="docutils literal">"http"</tt> or <tt class="docutils literal">"https"</tt>, as appropriate. |
<tt class="docutils literal">wsgi.input</tt> | 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.) |
<tt class="docutils literal">wsgi.errors</tt> |
<colgroup><col width="28%"><col width="72%"></colgroup>
Variable | Value |
---|---|
<tt class="docutils literal">wsgi.version</tt> | The tuple <tt class="docutils literal">(1, 0)</tt>, representing WSGI version 1.0. |
<tt class="docutils literal">wsgi.url_scheme</tt> | A string representing the "scheme" portion of the URL at which the application is being invoked. Normally, this will have the value <tt class="docutils literal">"http"</tt> or <tt class="docutils literal">"https"</tt>, as appropriate. |
<tt class="docutils literal">wsgi.input</tt> | 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.) |
<tt class="docutils literal">wsgi.errors</tt> |
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 <tt class="docutils literal">"\n"</tt> as a line ending, and assume that it will be converted to the correct line ending by the server/gateway.
(On platforms where the <tt class="docutils literal">str</tt> 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, <tt class="docutils literal">wsgi.errors</tt> will be the server's main error log. Alternatively, this may be <tt class="docutils literal">sys.stderr</tt>, 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.
|
| <tt class="docutils literal">wsgi.multithread</tt> | 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. |
| <tt class="docutils literal">wsgi.multiprocess</tt> | This value should evaluate true if an equivalent application object may be simultaneously invoked by another process, and should evaluate false otherwise. |
| <tt class="docutils literal">wsgi.run_once</tt> | 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 <tt class="docutils literal">environ</tt> 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, <tt class="docutils literal">mod_python</tt>might define variables with names like <tt class="docutils literal">mod_python.some_variable</tt>.
Input and Error Streams
The input and error streams provided by the server must support the following methods:
Method | Stream | Notes |
---|---|---|
<tt class="docutils literal">read(size)</tt> | <tt class="docutils literal">input</tt> | 1 |
<tt class="docutils literal">readline()</tt> | <tt class="docutils literal">input</tt> | 1, 2 |
<tt class="docutils literal">readlines(hint)</tt> | <tt class="docutils literal">input</tt> | 1, 3 |
<tt class="docutils literal">iter()</tt> | <tt class="docutils literal">input</tt> | |
<tt class="docutils literal">flush()</tt> | <tt class="docutils literal">errors</tt> | 4 |
<tt class="docutils literal">write(str)</tt> | <tt class="docutils literal">errors</tt> | |
<tt class="docutils literal">writelines(seq)</tt> | <tt class="docutils literal">errors</tt> |
<colgroup><col width="51%"><col width="27%"><col width="22%"></colgroup>
Method | Stream | Notes |
---|---|---|
<tt class="docutils literal">read(size)</tt> | <tt class="docutils literal">input</tt> | 1 |
<tt class="docutils literal">readline()</tt> | <tt class="docutils literal">input</tt> | 1, 2 |
<tt class="docutils literal">readlines(hint)</tt> | <tt class="docutils literal">input</tt> | 1, 3 |
<tt class="docutils literal">iter()</tt> | <tt class="docutils literal">input</tt> | |
<tt class="docutils literal">flush()</tt> | <tt class="docutils literal">errors</tt> | 4 |
<tt class="docutils literal">write(str)</tt> | <tt class="docutils literal">errors</tt> | |
<tt class="docutils literal">writelines(seq)</tt> | <tt class="docutils literal">errors</tt> |
The semantics of each method are as documented in the Python Library Reference, except for these notes as listed in the table above:
-
The server is not required to read past the client's specified <tt class="docutils literal">Content-Length</tt>, 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 <tt class="docutils literal">CONTENT_LENGTH</tt> variable.
A server should allow <tt class="docutils literal">read()</tt> 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.
-
Servers should support the optional "size" argument to <tt class="docutils literal">readline()</tt>, 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 <tt class="docutils literal">cgi</tt> module started using it, and so practical servers had to start supporting it anyway!)
Note that the <tt class="docutils literal">hint</tt> argument to <tt class="docutils literal">readlines()</tt> 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.
Since the <tt class="docutils literal">errors</tt> stream may not be rewound, servers and gateways are free to forward write operations immediately, without buffering. In this case, the <tt class="docutils literal">flush()</tt> method may be a no-op. Portable applications, however, cannot assume that output is unbuffered or that <tt class="docutils literal">flush()</tt> is a no-op. They must call <tt class="docutils literal">flush()</tt> 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 <tt class="docutils literal">input</tt> or <tt class="docutils literal">errors</tt> objects. In particular, applications must not attempt to close these streams, even if they possess <tt class="docutils literal">close()</tt> methods.
The <tt class="docutils literal">start_response()</tt> Callable
The second parameter passed to the application object is a callable of the form <tt class="docutils literal">start_response(status, response_headers, exc_info=None)</tt>. (As with all WSGI callables, the arguments must be supplied positionally, not by keyword.) The <tt class="docutils literal">start_response</tt> callable is used to begin the HTTP response, and it must return a <tt class="docutils literal">write(body_data)</tt> callable (see the Buffering and Streaming section, below).
The <tt class="docutils literal">status</tt> argument is an HTTP "status" string like <tt class="docutils literal">"200 OK"</tt> or <tt class="docutils literal">"404 Not Found"</tt>. 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 <tt class="docutils literal">response_headers</tt> argument is a list of <tt class="docutils literal">(header_name, header_value)</tt> tuples. It must be a Python list; i.e. <tt class="docutils literal">type(response_headers) is ListType</tt>, and the server may change its contents in any way it desires. Each <tt class="docutils literal">header_name</tt> must be a valid HTTP header field-name (as defined by RFC 2616, Section 4.2), without a trailing colon or other punctuation.
Each <tt class="docutils literal">header_value</tt> 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 <tt class="docutils literal">Date:</tt> and <tt class="docutils literal">Server:</tt> 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 <tt class="docutils literal">start_response()</tt>. (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 <tt class="docutils literal">start_response</tt> is called, so that an error can be raised while the application is still running.
However, the <tt class="docutils literal">start_response</tt> 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 <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">Content-Length</tt> 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 <tt class="docutils literal">exc_info</tt> argument, if supplied, must be a Python <tt class="docutils literal">sys.exc_info()</tt> tuple. This argument should be supplied by the application only if <tt class="docutils literal">start_response</tt> is being called by an error handler. If <tt class="docutils literal">exc_info</tt> is supplied, and no HTTP headers have been output yet, <tt class="docutils literal">start_response</tt> 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 <tt class="docutils literal">exc_info</tt> is provided, and the HTTP headers have already been sent, <tt class="docutils literal">start_response</tt> must raise an error, and should re-raise using the <tt class="docutils literal">exc_info</tt> tuple. That is:
<pre class="literal-block" style="padding: 10px; font-size: 13.679642677307129px; background-color: rgb(224, 224, 255);">raise exc_info[1].with_traceback(exc_info[2])
</pre>
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 <tt class="docutils literal">start_response</tt>, if it called <tt class="docutils literal">start_response</tt> with <tt class="docutils literal">exc_info</tt>. 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 <tt class="docutils literal">start_response</tt> more than once, if and only if the <tt class="docutils literal">exc_info</tt> argument is provided. More precisely, it is a fatal error to call <tt class="docutils literal">start_response</tt> without the <tt class="docutils literal">exc_info</tt> argument if <tt class="docutils literal">start_response</tt> has already been called within the current invocation of the application. This includes the case where the first call to <tt class="docutils literal">start_response</tt> raised an error. (See the example CGI gateway above for an illustration of the correct logic.)
Note: servers, gateways, or middleware implementing <tt class="docutils literal">start_response</tt> should ensure that no reference is held to the <tt class="docutils literal">exc_info</tt> 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:
<pre class="literal-block" style="padding: 10px; font-size: 13.679642677307129px; background-color: rgb(224, 224, 255);">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.
</pre>
The example CGI gateway provides another illustration of this technique.
Handling the <tt class="docutils literal">Content-Length</tt> Header
If the application supplies a <tt class="docutils literal">Content-Length</tt> 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 <tt class="docutils literal">write()</tt> past that point. (Of course, if the application does not provide enough data to meet its stated <tt class="docutils literal">Content-Length</tt>, the server should close the connection and log or otherwise report the error.)
If the application does not supply a <tt class="docutils literal">Content-Length</tt> 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 <tt class="docutils literal">Content-Length</tt> header, or at least avoid the need to close the client connection. If the application does not call the <tt class="docutils literal">write()</tt> callable, and returns an iterable whose <tt class="docutils literal">len()</tt> is 1, then the server can automatically determine <tt class="docutils literal">Content-Length</tt> 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 <tt class="docutils literal">write()</tt> call or bytestring yielded by the iterable, thus generating a <tt class="docutils literal">Content-Length</tt> 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 <tt class="docutils literal">Content-Length</tt>.
(Note: applications and middleware must not apply any kind of <tt class="docutils literal">Transfer-Encoding</tt> 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:
- Send the entire block to the operating system (and request that any O/S buffers be flushed) before returning control to the application, OR
- Use a different thread to ensure that the block continues to be transmitted while the application produces the next block.
- (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 <tt class="docutils literal">write()</tt> callable to transmit data that is yielded by an underlying application. Middleware may only use their parent server's <tt class="docutils literal">write()</tt> callable to transmit data that the underlying application sent using a middleware-provided <tt class="docutils literal">write()</tt> callable.
The <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">write()</tt> callable, returned by the <tt class="docutils literal">start_response</tt> callable.
New WSGI applications and frameworks should not use the <tt class="docutils literal">write()</tt> callable if it is possible to avoid doing so. The <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">write()</tt> callable is returned by the <tt class="docutils literal">start_response()</tt> 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 <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">write()</tt> from within their return iterable, and therefore any bytestrings yielded by the iterable are transmitted after all bytestrings passed to <tt class="docutils literal">write()</tt> 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 <tt class="docutils literal">str</tt> or <tt class="docutils literal">bytes</tt>, never <tt class="docutils literal">unicode</tt>. The result of using a <tt class="docutils literal">unicode</tt> object where a string object is required, is undefined.
Note also that strings passed to <tt class="docutils literal">start_response()</tt> 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 <tt class="docutils literal">str</tt> or <tt class="docutils literal">StringType</tt> 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 (<tt class="docutils literal">\u0000</tt> through <tt class="docutils literal">\u00FF</tt>, 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 <tt class="docutils literal">str</tt> or <tt class="docutils literal">StringType</tt>, and must not be of type <tt class="docutils literal">unicode</tt> or <tt class="docutils literal">UnicodeType</tt>. And, even if a given platform allows for more than 8 bits per character in <tt class="docutils literal">str</tt>/<tt class="docutils literal">StringType</tt> 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 <tt class="docutils literal">wsgi.input</tt>, passed to <tt class="docutils literal">write()</tt> or yielded by the application), the value must be of type <tt class="docutils literal">bytes</tt> under Python 3, and <tt class="docutils literal">str</tt> 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 <tt class="docutils literal">exc_info</tt> argument to <tt class="docutils literal">start_response</tt>. Here is an example of its use:
<pre class="literal-block" style="padding: 10px; font-size: 13.679642677307129px; background-color: rgb(224, 224, 255);">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"]
</pre>
If no output has been written when an exception occurs, the call to <tt class="docutils literal">start_response</tt> 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, <tt class="docutils literal">start_response</tt> 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 <tt class="docutils literal">text/*</tt> 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 <tt class="docutils literal">exc_info</tt> supplied to <tt class="docutils literal">start_response</tt>, 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 <tt class="docutils literal">write()</tt>), allowing the middleware to capture and modify the error output. These techniques will work as long as application authors:
- Always provide <tt class="docutils literal">exc_info</tt> when beginning an error response
- Never trap errors raised by <tt class="docutils literal">start_response</tt> when <tt class="docutils literal">exc_info</tt> 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:
- Respond to requests containing an <tt class="docutils literal">Expect: 100-continue</tt> request with an immediate "100 Continue" response, and proceed normally.
- Proceed with the request normally, but provide the application with a <tt class="docutils literal">wsgi.input</tt> 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.
- 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 <tt class="docutils literal">environ</tt> dictionary. WSGI servers must handle any supported inbound "hop-by-hop" headers on their own, such as by decoding any inbound <tt class="docutils literal">Transfer-Encoding</tt>, 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 <tt class="docutils literal">If-None-Match</tt> and <tt class="docutils literal">If-Modified-Since</tt> request headers and the <tt class="docutils literal">Last-Modified</tt> and <tt class="docutils literal">ETag</tt> 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.