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 <pje at telecommunity.com>
討論: 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 <tt class="docutils literal">sys.stderr</tt> 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”。這個不可能是一個空的字符串,所以一直是必須的。

腳本名

???????路由路徑的最初的部分對應應用對象,這樣的話應用才知道它的虛擬的位置。如果應用對應的是服務器的根目錄的話這個可能是一個空的字符串。

路徑信息

<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:

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

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

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

  4. 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:

  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 <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:

  1. Always provide <tt class="docutils literal">exc_info</tt> when beginning an error response
  2. 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:

  1. Respond to requests containing an <tt class="docutils literal">Expect: 100-continue</tt> request with an immediate "100 Continue" response, and proceed normally.
  2. 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.
  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 <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.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,505評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,556評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,463評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,009評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,778評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,218評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,281評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,436評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,969評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,795評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,993評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,537評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,229評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,659評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,917評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,687評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,990評論 2 374

推薦閱讀更多精彩內容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi閱讀 7,392評論 0 10
  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的閱讀 13,511評論 5 6
  • 這一天就吃了一頓飯,然後窩在沙發上開始發呆寫字,偶爾會起來喝杯橘普,吃了幾個小芒果,一顆棗想你。晚飯時仍然是很飽的...
    林素兮閱讀 249評論 0 1
  • 豌豆純真 是誰驚擾了你的夢? 綠意中那抹安靜的純真 你懵懵懂懂的憨笑 招來了春光明媚與彩蝶翩翩 你是一個乖...
    京都物語閱讀 180評論 0 2
  • 一 “人文五班陳XX同學,請你做我的女朋友!” “人文五班陳XX同學,請你做XXX的女朋友!” ...... 樓下...
    貓口閱讀 438評論 0 1