检测HTTP请求体的结束
我正在编写我自己的HTTP客户端和服务器,并希望客户端在请求中包含一个可选的主体。 在服务器端,我想在发送HTTP响应之前读取整个主体。 我的问题是在服务器上,我怎么知道我已经阅读了整个机构?
即使在这种情况下,我控制客户端和服务器,我正在寻找一个“标准”的方法。 但是,由于Content-Length是可选的,我想要一个不需要它的方法。 如果客户端closures连接,读取所有可用的数据很容易,但是客户端需要保持连接打开以等待响应,所以这种方法不起作用。
我能想到的剩下的就是了解身体的格式和检测终结者(例如</HTML>
)。 理想情况下,我不想要这些知识。
有没有我忽略的方法?
假设您希望您的客户端与其他服务器一起工作,并且服务器与其他客户端一起工作,那么您的服务器不能期待得到很好的处理。
有两种方法可以判断身体何时结束。 他们都不需要知道正文的内容types(例如,不要去寻找</html>
– 远远超出HTTP协议)。
- 如果客户端使用
Transfer-Encoding: Chunked
发送消息,则需要parsing有点复杂的分块传输编码语法 。 在这个问题上你没有太多的select – 如果客户以这种格式发送,你必须收到。 当客户端使用这种方法时,可以通过长度为0的块来检测正文的结束。 - 如果客户端发送
Content-Length
,则必须使用该内容。
正如你所build议的那样,检测结束的第三种方法 – 当连接closures时 – 只对响应起作用,而不是请求(因为那样就没有办法发送响应)。
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: chunked
或Content-Length
,所以您不必担心在任何其他情况下确定长度
我又增加了一个答案,主要是因为我没有足够的代表来评论麦库卡的。 我知道这个问题有点老,但是没有确定的答案。
如前所述,主要考虑的是你的服务器与不可控制的其他服务器交互 ,这意味着你根本不知道它将发送什么,并且必须准备好pipe理通过这个端口发送的任何信息。 考虑到这一点,坚持标准和常规做法可能是最好的select。
如果客户端发送“Content-Length”头,则服务器必须parsing并使用它来确定请求的结束。 如果没有这样的头部,但是“Transfer-Encoding:chunked”头部存在,则服务器必须能够parsing分块的请求(来自mgiuca的答案的链接 )。 最后,如果两者都不存在,则“连接结束”表示请求结束。
我认为你忽略了一个事实,即客户端可以结束连接,仍然可以从服务器获得响应。 我的意思是,“结束连接”是什么意思? 请记住,HTTP是一个通常通过TCP传输的应用层协议。 探索TCP的function(特别是连接终止协议)揭示了一些有趣的信息:
- 为了主动结束连接,客户端发送一个包含FIN标志的数据包,这是一个四次握手的一部分。 由于终止协议还没有完成,所以连接仍被认为是开放的。
- 服务器收到这个数据包并通知客户端( ACK包)。 服务器现在知道客户端将不会传输更多的数据。
- 客户端进入FIN_WAIT2状态,等待来自服务器的具有FIN标志的分组正确地closures连接。
但那里呢! 客户端已经通知他要结束连接,服务器知道,但客户端仍然打开连接(他没有closures它,因为他没有收到FIN数据包)。 服务器现在应答请求,然后正确closures连接。 需要注意的是客户端会用附加的RST标志来确认每个服务器数据包,告诉服务器他仍然期望FINclosures连接。
当服务器完成时(在我们的小例子中,在发送HTTP响应之后),他closures了他侧的连接,发送FIN数据包。 客户收到客户端时会closures,并通过ACK来通知服务器。
另外还有一点我不清楚你正在编程的上下文,但是大多数情况下,你最终会在套接字上调用shutdown() 。 POSIX的closures (至less是Windows )将把你想closures连接的那个接口作为一个函数参数。 这些规范清楚地表明,您可以closures发件人部分(这正是客户端所要做的),禁用数据发送,同时允许客户端接收更多的数据。
关于TCP连接的进一步的细节超出了这个问题的范围,但是我build议读一下它来更好地理解使用它的更高层的协议。
RFC
简单的方法:使用HTTP 1.0,并要求内容的长度
为了与HTTP / 1.0应用程序兼容,包含消息体的HTTP / 1.1请求必须包含一个有效的Content-Length头域,除非服务器被认为是符合HTTP / 1.1的。 如果一个请求包含一个消息主体,并且没有给出一个Content-Length,服务器应该回应400(坏请求),如果它不能确定消息的长度,或者如果它希望坚持接收有效的内容长度。
我认为当你说“Content-Length是可选的”时,你会停止使用最明显的select。
从HTTP规范http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.13
应用程序应该使用这个字段来表示消息体的传输长度,除非4.4节中的规则禁止这样做。
如果你知道这个长度,听起来就像你会的那样,在Content-Length头部指定它,并完成它,因为规范基本上是要求你这样做(假设没有其他的你违反了http:/ / /www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.4 )。