检测 HTTP 请求体的结束

我正在尝试编写自己的 HTTP 客户机和服务器,希望客户机在请求中包含一个可选的主体。在服务器端,我希望在发送 HTTP 响应之前读取整个主体。我的问题是,在服务器上,我怎么知道我已经阅读了整个身体?

即使在这种情况下,我同时控制客户机和服务器,我仍然在寻找一种“标准”的方法。但是,因为 Content-Llength 是可选的,所以我想要一个不需要它的方法。如果客户端关闭连接,那么很容易读取所有可用的数据,但是客户端需要保持连接打开以等待响应,因此此方法无法工作。

我所能想到的就是了解身体的形态,并检测出终结者(例如:。</HTML>).理想情况下,我不想要求那些知识。

有什么办法我没注意到吗?

79415 次浏览

我认为你是在阻止自己使用最明显的选择当你说“内容长度是可选的”。

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13的 HTTP 规范

应用程序应该使用这个字段 表示传输长度 信息主体,除非这是 禁止的规则在部分 4.4.

如果你知道长度,而且听起来你会知道,那么在 Content-Length 头中指定它,然后完成它,因为 spec 基本上是在请求你这样做(假设你没有做任何违反 http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4中提到的规则的事情)。

RFC

简单的方法: 使用 HTTP 1.0并要求内容长度

为了与 HTTP/1.0应用程序兼容,包含消息正文的 HTTP/1.1请求必须包含有效的 Content-Llength 头字段,除非服务器已知与 HTTP/1.1兼容。如果一个请求包含一个消息主体,并且没有给出一个 Content-Llength,如果服务器不能确定消息的长度,它应该响应400(坏请求) ,或者如果它想要坚持接收一个有效的 Content-Llength,它应该响应411(所需长度)。

假设您希望您的客户端与其他服务器一起工作,而服务器与其他客户端一起工作,那么您的服务器不能指望得到很好的对待。

有两种方法可以判断身体什么时候结束。它们都不需要像您建议的那样了解主体的内容类型(例如,不必费心寻找 </html>——它远远超出了 HTTP 协议的范围)。

  1. 如果客户机使用 Transfer-Encoding: Chunked发送消息,则需要解析有些复杂的 分块传输编码句法。在这种情况下,您实际上没有太多的选择——如果客户机发送这种格式的邮件,那么您必须接收它。当客户机使用这种方法时,您可以通过一个长度为0的块来检测主体的末尾。
  2. 如果客户机发送的是 Content-Length,则必须使用它。

正如您所建议的,用于检测结束的第三种方法——当连接关闭时——只对响应有效,而不对请求有效(因为那样就没有办法发送响应)。

If a request contains a message-body and a Content-Length is not given,
the server SHOULD respond with 400 (bad request) if it cannot determine
the length of the message, or with 411 (length required) if it wishes
to insist on receiving a valid Content-Length.

也就是说,你有权坚持 Transfer-Encoding: chunkedContent-Length,所以你不必担心确定长度在任何其他情况下

我添加另一个答案,主要是因为我没有足够的代表评论 Mgiuca的。我知道这个问题有点老套,但还没有明确的答案。

正如前面提到的,要考虑的主要事情是 你的服务器与无法控制的其他人互动,意思是你 不能知道他们将发送什么 完全没有,并且必须准备好管理任何通过那个门的东西。考虑到这一点,坚持标准和通用实践可能是最佳选择。

如果客户端发送一个“ Content-Llength”头,服务器必须解析它并使用它来确定请求的结束。如果没有这样的头,但是出现了“ Transfer-Encoding: chunked”头,那么服务器必须能够解析一个分块请求(Mgiuca应答中的 链接)。最后,如果两者都不存在,则“连接结束”表示请求结束。

我认为您忽略了一个事实,即客户端可以结束连接,但仍然可以从服务器获得响应。“结束联系”是什么意思?请记住,HTTP 是一种通过 TCP 传输(通常)的应用层协议。探索 TCP 的功能(特别是它的 连接终止协议)揭示了一些有趣的信息:

  • 为了主动地结束连接,客户机发送一个带有 FIN标志的数据包,这是四方握手的一部分。因为终止协议还没有完成,所以连接仍被认为是打开的。
  • 服务器接收这个数据包并通知客户端(包)。服务器现在知道客户端将不再传输任何数据。
  • 客户机进入 FIN _ WAIT2状态,等待从服务器发出的带有 FIN标志的数据包正确关闭连接。

但它就在那里!客户端已经通知他想要终止连接,服务器也知道这一点,但是连接仍然在客户端打开(他还没有关闭连接,因为他没有收到 FIN数据包)。服务器现在应答请求,然后正确关闭连接。重要的是要注意,客户机将 每个带有额外 RST标志的服务器数据包,告诉服务器他仍然期望 FIN关闭连接。

当服务器完成后(在我们的小例子中,在发送 HTTP Response 之后) ,他关闭自己这边的连接,发送 FIN数据包。当客户端收到它时,它关闭它的端,并用一个 通知服务器。

另外,我不知道您正在编程的上下文,但是大多数情况下,您最终会在套接字上调用 关闭()POSIX 关闭了(至少是 视窗)将要关闭的连接的哪个接口作为函数参数。这些规范清楚地表明,您可以只关闭发送方部分(这正是客户端将要做的事情) ,禁用数据发送,同时允许客户端接收进一步的数据。

关于 TCP 连接的更多细节超出了这个问题的范围,但我建议阅读有关它的内容,以便更好地理解使用它的更高层的协议。

这个:

Http://greenbytes.de/tech/webdav/rfc7230.html#message.body.length

应该是权威的答案(不,不需要了解有效载荷格式)