Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

http 协议的结束符 #34

Open
jinhailang opened this issue Aug 23, 2018 · 4 comments
Open

http 协议的结束符 #34

jinhailang opened this issue Aug 23, 2018 · 4 comments

Comments

@jinhailang
Copy link
Owner

jinhailang commented Aug 23, 2018

http 协议的结束符

突然想起很久之前一次面试,面试官问我,当请求头没有 content-length 时,怎么知道请求体结束了?

http 的 headerbody 之间空行分割的,又因为每个头部项是以 \r\n 作为结束符,所以,数据流中是以 \r\n\r\n 来分割解析请求头(响应头)与请求体(响应体)的。如下图所示:

image

那么怎么知道(请求体)响应体结束了呢? http 协议规定,响应头的字段 content-length 用来表示响应体长度大小,但是,有可能发送请求头时,并不能知道完整的响应体长度(比如当响应数据太大,服务端流式处理的情况),这时需要设置请求头Transfer-Encoding: chunked,使用数据块的方式传输,数据块格式如下图所示:

image

每个数据块分为两个部分:数据长度和数据内容,以 \r\n 分割,最后长度为 0 的数据块,内容为空行(\r\n),表示没有数据再传输了,响应结束。需要注意的是,此时, content-length 不应该被设置,就算设置了,也会被忽略掉。

回到最开始的那个问题,我当时对 http 协议不太清楚,回答不上来,那位面试官就告诉我,可以使用 \r\n\r\n 来判断,现在看来,他说的并不严谨。首先,http 协议并没有规定请求体(响应体)要以 \r\n\r\n 作为结束符,其次,很重要的一点是,响应体(请求体)的内容是多种多样的,你没法做限制,当数据内容包含\r\n\r\n 时,显然解析出来的响应体就是不全的

当然,如果是自己实现 http 服务端的话,怎么兼容这种情况呢?

如果是短连接的话,比较简单,连接关闭就表示数据传输完成了。如果是长连接的话,一种不太优雅的方式就是使用超时机制,当读取超过一定时间,就认为数据已经传输完成。

总之,判断数据(块)结束最严谨的方式是计算长度,而不是使用结束符,但是,一般可控的场景下(双方约定),还是可以选择使用结束符来判断的,这样实现起来会更简洁。此时,为了防止内容中包含约定的结束符,导致数据内容被提前截断,客户端可以在发送数据时先对内容中的约定结束符进行编码。

扩展

因为服务端在解析请求头和请求体时,都需要依据以上协议,来读取完整数据。Slow HeadersSlow POST 两类 DDoS 慢速攻击正是利用了这个原理。

正常的 HTTP 报文中请求头部的后面会有结束符 0x0d0a(\r\n 的十六进制表示方式),
而攻击报文中不包含结束符,并且攻击者会持续发送不包含结束符的 HTTP 头部报文,
维持连接状态,消耗目标服务器的资源。
@zhouweihui
Copy link

mark

@haixuxu
Copy link

haixuxu commented Jul 7, 2020

mark

@wbswjc
Copy link

wbswjc commented Mar 30, 2021

有关消息体长度的 RFC 链接:https://httpwg.org/specs/rfc7230.html#rfc.section.3.3.3

其中完整说明了确定消息体长度,即确定消息结束的所有情况,除了 Transfer-Encoding 和 Content-Length 外,还有一些其他情况需要考虑。当然最为常见的情况是 chunked Transfer-Encoding, Content-Length,以及消息体为空的情况。🤖

@Jervain-Y
Copy link

chunk sizechunk data长度的16进制,示例:chunk data长度为15个字符123456789012345,那发送的数据为f\r\n123456789012345\r\n0\r\n\r\n

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants