10

我在处理 http 分块传输编码时遇到问题。

我正在使用:

  • 阿帕奇。
  • mod_wsgi 插件。
  • django。

django 只能处理带有 content-length 标头字段的常规 http 请求,但是在处理 TE(Transfer-Encoding)、分块或 gzip 时,它会返回一个空结果。

我正在考虑两种方法:

  1. 对 django.wsgi python 文件进行一些修改
  2. 将一些中间件 python 文件添加到 django,以拦截任何分块的 http 请求,将其转换为带有 content-length 头字段的 requelar http 请求,然后将其传递给 django,它可以很好地处理它。

任何人都可以提供上述 2 个选项中的任何一个(当然欢迎更多选项)

谢谢!


这是格雷厄姆第一次回答后对我的问题的延伸:

首先,感谢您的快速回复。使用的客户端是 Axis,它是另一家公司与我们的系统通信的一部分。我已经WSGIChunkedRequest On设置好了,我还对我的 wsgi 包装器进行了一些修改,如下所示:

def application(environ, start_response):

    if environ.get("mod_wsgi.input_chunked") == "1":
        stream = environ["wsgi.input"]
        print stream
        print 'type: ', type(stream)
        length = 0
        for byte in stream:
            length+=1
        #print length    
        environ["CONTENT_LENGTH"] = len(stream.read(length))

    django_application = get_wsgi_application()
    return django_application(environ, start_response)

但它给了我这些错误(从 apache 的 error.log 文件中提取):

[Sat Aug 25 17:26:07 2012] [error] <mod_wsgi.Input object at 0xb6c35390>
[Sat Aug 25 17:26:07 2012] [error] type:  <type 'mod_wsgi.Input'>
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] mod_wsgi (pid=27210): Exception occurred processing WSGI script '/..../wsgi.py'.
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] Traceback (most recent call last):
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx]   File "/..../wsgi.py", line 57, in application
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx]     for byte in stream:
[Sat Aug 25 17:26:08 2012] [error] [client xxxxxxxxxxxxx] IOError: request data read error

我究竟做错了什么?!

4

2 回答 2

14

这不是 Django 问题。这是 WSGI 规范本身的一个限制,因为 WSGI 规范通过要求请求的 CONTENT_LENGTH 值来禁止使用分块的请求内容。

使用 mod_wsgi 时,有一个开关可以启用对分块请求内容的非标准支持,但这意味着您的应用程序不符合 WSGI,而且它需要自定义 Web 应用程序或 WSGI 包装器,因为它仍然无法与 Django 一起使用.

mod_wsgi 中允许分块请求内容的选项是:

WSGIChunkedRequest On

你的 WSGI 包装器应该调用 wsgi.input.read() 来获取全部内容,用它创建一个 StringIO 实例并用它来替换 wsgi.input ,然后在调用包装的应用程序之前添加一个具有实际长度的新 CONTENT_LENGTH 值到环境中。

请注意这是危险的,因为您不知道正在发送多少数据。

无论如何,您使用什么客户端只支持分块请求内容?


更新 1

您的代码因多种原因而损坏。你应该使用类似的东西:

import StringIO

django_application = get_wsgi_application()

def application(environ, start_response):

    if environ.get("mod_wsgi.input_chunked") == "1":
        stream = environ["wsgi.input"]
        data = stream.read()   
        environ["CONTENT_LENGTH"] = str(len(data))
        environ["wsgi.input"] = StringIO.StringIO(data)

    return django_application(environ, start_response)

请注意,这对 gzip 的请求内容没有帮助。您将需要额外检查以查看内容编码何时是压缩数据,然后执行与上述相同的操作。这是因为当 Apache 解压缩数据时,内容长度会发生变化,您需要重新计算它。

于 2012-08-23T12:21:27.143 回答
3

现在一切正常,问题出在守护程序模式下,因为它不适用于分块的 http 流量,可能在 mod_wsgi 4 中 - 根据 Graham Dumpleton。因此,如果您遇到此问题,请将 mod_wsgi 切换到嵌入式模式。

作为对 wsgi 包装器中 Graham 代码的修改,有两个选项可以读取缓冲在环境变量中的流:

第一:

try:
    while True:
        data+= stream.next()
except:
    print 'Done with reading the stream ...'

第二个:

try:
   data+= stream.read()
except:
   print 'Done with reading the stream ...' 

第一个代码存根能够在守护程序模式下读取缓冲区但在某处停止,并且程序没有继续运行(这让我有点困惑,因为我希望看到它运行良好),而另一个代码存根崩溃了带有 IOError,并且仅在嵌入式模式下工作。

还要补充一点,从 3.3 升级到 3.4 并没有解决问题,所以你必须切换到嵌入式模式。

这些是我的结果和观察。如果您有任何意见、补充或更正,请不要犹豫。

谢谢!

于 2012-08-26T17:00:28.997 回答