【发布时间】:2021-09-28 18:41:17
【问题描述】:
TCP 不是基于消息的协议,但它是一个简单的字节流。 HTTP 协议实际上是基于 TCP 的基于消息的协议。那么,如何解析来自 TCP 流连接的原始 HTTP 数据呢?
例如,我们通过python中的TCP套接字连接到代理服务器:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port)) # host and port are proxy's address
然后,我们询问代理,是否可以通过它CONNECT 到目标主机(例如google.com):
request = b'CONNECT %s:%i HTTP/1.0\r\n\r\n' % ("google.com".encode(), 443)
s.sendall(request)
然后,我们需要从socket 接收数据。 但是如何?如果我们recv 数据,我们会将其保存到缓冲区中,如下所示:
buffer = s.recv(1024)
我检查过,当主机关闭连接时,它会发送一条 0 字节长的消息(例如 404、502、400 状态代码)。但是当连接处于活动状态时(主机返回状态码 200),它不发送终止的 0 字节。当然,它不应该,但是,我们怎么知道,这是信息的结尾?
我对 HTTP 协议所做的是,标头由\r\n 划分,正文与标头由\r\n\r\n 划分。 HTTP 消息总是以\r\n 结尾。因此,理论上,我们可以只阅读消息,直到遇到 \r\n\r\n,然后我们知道消息的其余部分,直到另一个 \r\n,是响应的正文。
但是,如果某个小丑服务器想要在 http 响应正文中添加另一个 \r\n inside 怎么办?然后整个解析就坏了! 现在算法认为正文结束了,消息的其余部分是下一条消息的标头并抛出异常,试图解析它!如果某个有趣的人编写了一个服务器,其中放置了 @987654339 @ 在自定义响应标头中?
那么我们如何从原始套接字进行解析,它是如何正确完成的?我们如何避免在某些错误配置的服务器响应上出现失误?
【问题讨论】:
-
“HTTP 消息总是以 \r\n 结尾” - 不,它不是。长度由
Content-length标头定义,或者在分块传输编码的情况下使用其他方式定义。详情请see the actual standard. -
我投票结束这个问题,因为它基于错误的假设,即
\r\n结束了 HTTP 正文。由于这个假设是错误的,因此问题中提出的整个问题是无关紧要的。
标签: python http parsing tcp stream