Transfer-Encoding 是HTTP header 中的 entity header,所谓entity header就是专门用来描述消息体(message body)的头,像 Content-Length, Content-Language, Content-Encoding 都属于 entity header这一类,他们分别用来描述消息体的长度、语言、编码格式。那么 Transfer-Encoding 是用来干什么的呢?
简介
通常,HTTP Response 中的 message body 是整个包一起发送到客户端的,Content-Length 消息头字段就是用来表示消息体的长度, 服务端必须精确地告诉客户端这个 message body 的长度是多少, 如果Content-Length 比实际长度短,会造成内容被截断,如果比实体内容长,客户端就一直处于pendding状态,直到所有的消息体都返回了才结束请求。但是对于HTTP持久连接,服务端发送消息体之前就要提前计算出消息体的长度作为Content-Length 发送给客户端,对于动态生成的内容在完全创建好之前是无法预知内容的长度的,这就要服务器缓冲内容直到完整的生成内容后计算出Content-Length的值才能发送给客户端。然而使用分块传输编码(Transfer-Encoding),数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小,这就是Transfer-Encoding 的作用。
在消息头中指定Transfer-Encoding: chunked 就表示整个response将使用分块传输编码来传输内容,一个完整的消息体由n个块组成,并以最后一个大小为0的块为结束。每个非空的块包括两部分,分别为:块的长度(用十六进制表示)后面跟一个CRLF (回车及换行),长度并不包括结尾的回车换行符。第二部分就是数据本身,同样以CRLF (回车及换行)结束。最后一块是单行,只由块大小(0)以及CRLF组成,不包含任何数据。
现在就可以用Python实现一个简单的 HTTP Server 来指定 Response 的头 Transfer-Encoding。
# -*- coding:utf-8 -*-
import socket
if __name__ == '__main__':
PORT = 8000
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', PORT))
sock.listen(1)
print 'Serving HTTP on port %s ...' % PORT
while 1:
conn, addr = sock.accept()
print conn, addr
request = conn.recv(1024)
# HTTP响应消息
conn.sendall("HTTP/1.1 200 OK\r\n") # status line
conn.sendall("Content-Type: text/plain\r\n")
conn.sendall("Transfer-Encoding: chunked\r\n") # 分块传输编码
conn.sendall("\r\n") # 空行
conn.sendall("b\r\n") # 11个字节的长度
conn.sendall("hello world\r\n") # 消息体
conn.sendall("0\r\n") # 最后一块0长度
conn.sendall("\r\n")
conn.close()
用telnet请求测试:
telnet localhost 8000
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying fe80::1...
telnet: connect to address fe80::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
get HTTP/1.1 / # 发送GET请求
HTTP/1.1 200 OK
Content-Type: text/plain
Transfer-Encoding: chunked
b
hello world
0
最后引用Stack Overflow 上一句非常精辟的话作为总结:
Transfer-Encoding: chunked is needed when the total content length is unknown before the first bytes are sent.