http 协议的结束符 · Issue #34 · jinhailang/blog · GitHub

github.com · · 5007 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

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 头部报文,
维持连接状态,消耗目标服务器的资源。

本文来自:github.com

感谢作者:github.com

查看原文:http 协议的结束符 · Issue #34 · jinhailang/blog · GitHub

5007 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传