使用django处理HTTP分块编码

sec*_*rve 7 apache django mod-wsgi wsgi chunked-encoding

我有一个问题handeling http chunked传输编码.

我正在使用:

  • 阿帕奇.
  • mod_wsgi插件.
  • Django的.

django只能处理带有content-length头字段的reqular http请求,但是当处理TE(Transfer-Encoding),chunked或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)
Run Code Online (Sandbox Code Playgroud)

但它给了我那些错误(从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
Run Code Online (Sandbox Code Playgroud)

我究竟做错了什么?!

Gra*_*ton 10

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

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

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

WSGIChunkedRequest On
Run Code Online (Sandbox Code Playgroud)

您的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)
Run Code Online (Sandbox Code Playgroud)

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

  • 如果您正在使用守护程序模式,则问题尚未解决.被推迟到mod_wsgi 4.0.所以只能使用嵌入式模式.https://groups.google.com/forum/?fromgroups=#!topic/modwsgi/Rk-cXTGSCHQ (3认同)
  • 您的代码因各种原因而中断.摆脱你的for循环和破碎的长度计算,这实际上是计算行而不是字符.你需要的只是'data = stream.read()'并从那里开始.您在Django的每个Web请求上调用get_wsgi_application()也会被破坏.我会修改我的答案. (2认同)