如何使用python的http.client准确读取一个响应块?

Ben*_*rns 3 python http python-3.x http.client

使用http.client在Python 3.3+(或任何其他内置的Python HTTP客户端库),我怎么可以一次读取分块的HTTP响应正好一个HTTP块?

我正在扩展服务器的现有测试装置(使用python编写http.client),该服务器使用HTTP的分块传输编码来编写其响应。为了简单起见,假设我希望能够在客户端收到HTTP块时打印一条消息。

我的代码遵循相当标准的模式来读取较大的响应:

conn = http.client.HTTPConnection(...)
conn.request(...)
response = conn.getresponse()

resbody = []

while True:
    chunk = response.read(1024)
    if len(chunk):
        resbody.append(chunk)
    else:
        break

conn.close();
Run Code Online (Sandbox Code Playgroud)

但这将读取1024字节的块,无论服务器是否发送10字节的块或10MiB的块。

我正在寻找的东西将如下所示:

while True:
    chunk = response.readchunk()
    if len(chunk):
        resbody.append(chunk)
    else
        break
Run Code Online (Sandbox Code Playgroud)

如果无法使用http.client,则可以使用另一个内置的http客户端库吗?如果内置客户端库无法实现,可pip安装模块是否可以实现?

poi*_*ida 5

更新:

分块传输编码的好处是允许传输动态生成的内容。HTTP库是否允许您读取单个块是一个单独的问题(请参阅RFC 2616-第3.6.1节)。

我可以看到您尝试做的事情会很有用,但是标准的python http客户端库如果没有一些骇客功能,就不会做您想要的事情(请参阅http.clienthttplib)。

您尝试做的事情可能适合在您的测试治具中使用,但在野外并不能保证。客户端读取的数据分块可能与服务器发送的数据分块不同。例如,数据可能在到达之前已由代理服务器“重新分块”(请参阅RFC 2616-第3.2节-成帧技术)。


诀窍是告诉响应对象未分块(resp.chunked = False),以便它返回原始字节。这样,您就可以解析返回的每个块的大小和数据。

import http.client

conn = http.client.HTTPConnection("localhost")
conn.request('GET', "/")
resp = conn.getresponse()
resp.chunked = False

def get_chunk_size():
    size_str = resp.read(2)
    while size_str[-2:] != b"\r\n":
        size_str += resp.read(1)
    return int(size_str[:-2], 16)

def get_chunk_data(chunk_size):
    data = resp.read(chunk_size)
    resp.read(2)
    return data

respbody = ""
while True:
    chunk_size = get_chunk_size()
    if (chunk_size == 0):
        break
    else:
        chunk_data = get_chunk_data(chunk_size)
        print("Chunk Received: " + chunk_data.decode())
        respbody += chunk_data.decode()

conn.close()
print(respbody)
Run Code Online (Sandbox Code Playgroud)

  • 赞成,因为你实际上回答了我的问题。但是没有接受,因为它有点黑客。分块编码的好处应该是能够逐块读取。http.client 支持分块编码但显然没有公开它的事实有点令人难过...... (2认同)
  • 小错误:块大小以十六进制表示,因此应阅读`int(size_str[:-2], 16)`。参见 HTTP/1.1 https://tools.ietf.org/html/rfc7230#section-4.1(我编辑过)。否则:如果像 Ben 所说的那样 hacky,则效果很好。 (2认同)

Pr0*_*r0n 5

我发现像这样使用请求库更容易

r = requests.post(url, data=foo, headers=bar, stream=True)


for chunk in (r.raw.read_chunked()):
    print(chunk)
Run Code Online (Sandbox Code Playgroud)