首页 > programming > 在 Django 中处理 HTTP Transfer-Encoding

在 Django 中处理 HTTP Transfer-Encoding

什么是 Transfer-Encoding

在使用 HTTP 协议 传送数据时, 有时不能事先确定 body 的长度(比如边压缩边传输的 gzip 报文), 因此无法得到 Content-Length 的值, 所以发送方不能在 header 中指定 Content-Length. 接收方也无法通过 Content-Length 得到报文体的长度. 因此 HTTP/1.1 协议在 header 中引入了 Transfer-Encoding(分块传输编码), 当 Transfer-Encoding 值为 chunked 时, 表明采用 chunked 编码方式来进行报文体的传输. chunked 编码的基本方法是将大块数据分解成多块小数据. 每块都可以自指定长度.

Django 中的问题

目前, django 只支持正常的带 content-length 字段的 HTTP 请求, 如果 HTTP 请求中 有字段, Transfer-Encoding 且值为 chunked, 那么 django 的 view 得到的请求的 HTTP content 为空.

这其实不是 django 的问题, 这是由于 WSGI 的限制.

解决方法

但是如果使用 Apache 的 mod_wsgi 来部署 django 程序. 就可以在程序中的 WSGI 封装中把原始的数据读出来, 然后得到其长度, 并把该长度作为字段 Content-Length 的值传递给 django 程序要使用这种方式, 首先得在 Apache 的配置文件中打开 WSGI 的 Chunked 支持.

WSGIChunkedRequest On

然后在 WSGI 的封装中调用 wsgi.input.read() 读出整个数据, 把该数据的长度赋值给 CONTENT_LENGTH.

#+BEGIN_SRC python
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))
        # wsgi.input 已经被消耗了, 重新赋值
        environ["wsgi.input"] = StringIO.StringIO(data)
 
    return django_application(environ, start_response)

遗留问题

  • gzip 的请求需要重新计算 Content-Length
  • 该方法只适用于 embedded 模式, 对 daemon 模式会有问题.

分类: programming 标签: ,
  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.