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阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,312评论 0 10
  • PLEASE READ THE FOLLOWING APPLE DEVELOPER PROGRAM LICENSE...
    念念不忘的阅读 13,444评论 5 6
  • 這一天就吃了一頓飯,然後窩在沙發上開始發呆寫字,偶爾會起來喝杯橘普,吃了幾個小芒果,一顆棗想你。晚飯時仍然是很飽的...
    林素兮阅读 244评论 0 1
  • 豌豆纯真 是谁惊扰了你的梦? 绿意中那抹安静的纯真 你懵懵懂懂的憨笑 招来了春光明媚与彩蝶翩翩 你是一个乖...
    京都物语阅读 171评论 0 2
  • 一 “人文五班陈XX同学,请你做我的女朋友!” “人文五班陈XX同学,请你做XXX的女朋友!” ...... 楼下...
    猫口阅读 426评论 0 1