搞定 HTTP 协议(三):如何严谨地描述一个 HTTP 报文?
在 HTTP 协议中,最为核心的部分就是客户端和服务器之间通信时传输的报文了。HTTP 报文是由多行数据构成的字符串文本。通常情况下,一个 HTTP 报文由以下 4 部分构成:
起始行(start line)
头部字段(header fields)
一个空行(CRLF)
报文主体(message body)
在以上四部分中,起始行与头部字段,又经常被称作“请求头”或“响应头”,而报文主体经常被称为“实体(entity)”,两者由最初出现的空行(CRLF)来划分。而最后的报文主体是一个可选项,并不一定存在。
通常情况下,我们都采用上面这种口语化的形式来描述 HTTP 报文的格式,但是这种口语化的表达并不十分严谨。比如,我们通常会这样描述一个请求头:请求头中包含了请求方法、请求 URI、HTTP 版本号,但是它们之间是否要加空格呢?加两个空格可以么?再比如上图中的 Host 头部字段,冒号后面必须加一个空格么?加两个空格可以么?不加空格,而是用 Tab 制表符可以么?冒号前面可以加空格么?
如果用口语化的表达来描述 HTTP 报文,就很难说清楚上面这些问题。因此,RFC 7230 文档采用了 ABNF 范式来严谨的描述 HTTP 报文。
ABNF 范式
ABNF 范式大体上分为操作符和核心规则两大方面,这里我们不做区分,统一介绍一下与 HTTP 协议相关的描述。
选择:使用反斜杠"/",表示多个规则可选其一
可变重复
序列组合:使用小括号"()",将规则放在括号内组合起来,视作一个整体。
可选序列:用中括号表示"[]"
空白符:使用"SP"来表示,用来分隔定义的元素
水平 tab:用"HTAB"来表示
CRLF:互联网标准换行,由 CR(回车)和 LF(换行)组成
了解上面这些 ABNF 范式中的操作符和核心规则后,我们就可以用 ABNF 范式来严谨的定义 HTTP 报文了:
起始行
一个 HTTP 报文可以是从客户端到服务器的请求报文,也可以是从服务器到客户端的响应报文。通常情况下,对于请求报文来说,我们称它的起始行为请求行;而对于响应报文来说,我们称它的起始行为状态行。
请求行
请求行描述了客户端想要如何操作服务器上的资源。它通常包括:
请求方法(method):希望如何操作资源
请求目标(request-target):通常是一个 URI,表示资源的位置
协议版本(HTTP-version):使用的 HTTP 版本
以实际的例子来说:
“GET” 是请求方法, “/index.html” 是请求目标,“HTTP/1.1”是协议版本。利用这一行请求行,就可以明确的告诉服务器:我想获取根目录下的 index.html 文件,我的 HTTP 版本号是 1.1。
状态行
状态行描述了服务器的响应状态。它通常包括:
协议版本(HTTP-version):使用 HTTP 的版本
状态码(status-code):状态码其实也有对应的 ABNF 描述
3DIGIT
, 表示一个三位整数,比如常见的 200
描述状态码的原因短语(reason-phrase):用来解释状态码的具体原因
还是以实际的状态行来说:
“HTTP/1.1” 是协议版本,“200” 是状态码,“ok” 是原因短语。意思就是告诉客户端:找到了相应资源,我已经处理好了你的请求。
头部字段
从上面的图中可以知道:每个头部字段是一个典型的 key-value 格式,最后以 CRLF 表示结束,并且在整个头部字段的最后,必须由一个 CRLF 表示头部字段的结束。
对于头部字段来说,有一些特点需要我们注意:
头部字段是完全可扩展的,使用新字段名称是没有限制的
字段名称大小写均可,但通常情况下首字母需要大写
不同字段名称的字段顺序不重要,但是先发送包含控制数据的头部字段是一个良好的实践。例如请求中的 Host 和响应中的 Date,这样当实现不处理一个报文的时候,可以尽可能早的做出判断
字段名称和冒号之间不允许出现空白,因为可能会导致安全漏洞。冒号后可以有一个或多个空格(也可以是横向制表符)。字段值后也可以跟一个或多个空格(也可以是横向制表符),但最后要有一个 CRLF。通常情况,在冒号后面加一个空格是良好的习惯
头部字段通常分为以下四种:
通用头部:既可以出现在请求头中,也可以出现在响应头中,如 Date 字段;
请求头部:只能出现在请求头中,用于解释说明请求信息,如 Host 字段;
响应头部:只能出现在响应头中,用于解释说明响应信息,如 Server 字段;
实体头部:用于表述报文主体,如 Content-Length 字段,表示报文主体的长度。
HTTP 报文是 HTTP 协议的核心,而头部字段就是 HTTP 报文的核心。充分理解了常见的头部字段,HTTP 协议就不在话下了,后面的文章会重点介绍常见的重要头部字段。
报文主体
HTTP 协议中不要求报文主体必须存在,如果存在的话,报文主体用于携带请求或响应的有效载荷体。
通常情况下,首部字段中的 Content-Length 或 Transfer-Encoding 是请求中报文主体存在的信号。而响应中报文主体的存在取决于响应的请求方法和状态码。如 HEAD 请求方法的响应从不包括报文主体,而所有的 1xx,204 以及 304 的响应也不包含报文主体。
小结
本文详细介绍了 HTTP 的报文结构,除了常见的口语化表达外,还引入了 RFC 7230 文档中用于描述 HTTP 报文的 ABNF 范式,进行严谨的描述。
HTTP 报文主要由起始行、零个或多个头部字段、CRLF 以及可选的报文主体构成
起始行与头部字段经常被称为请求头或响应头
请求中的起始行叫做请求行,由请求方法、请求目标、协议版本组成
响应中的起始行叫做状态行,由协议版本、状态码、原因短语组成
头部字段的字段名一般大写第一个字母,后面紧跟冒号,不允许有空格;冒号与字段值直接可以有零个或多个空格或横向制表符
头部字段通常分为四种:通用头部、请求头部、响应头部以及实体头部
头部字段与报文主体之间,必须以一个 CRLF 区分
报文主体可以不存在
最后的话
你的点赞会给我一天好心情,如果能顺手 来个 star,再顺便关注下公众号(零幺小馆)就更完美了。
参考资料
RFC 7230 文档
极客时间 - 《透视 HTTP 协议》
极客时间 - 《Web 协议详解与抓包实战》
《图解 HTTP》
版权声明: 本文为 InfoQ 作者【零和幺】的原创文章。
原文链接:【http://xie.infoq.cn/article/d8074509c46116e85ceeca495】。文章转载请联系作者。
评论