在 HTTP 协议中,最为核心的部分就是客户端和服务器之间通讯时传输的报文了。HTTP 报文是由多行数据构成的字符串文本。一般状况下,一个 HTTP 报文由如下 4 部分构成:html
起始行(start line)git
头部字段(header fields)github
一个空行(CRLF)安全
报文主体(message body)bash
在以上四部分中,起始行与头部字段,又常常被称做“请求头”或“响应头”,而报文主体常常被称为“实体(entity)”,二者由最初出现的空行(CRLF)来划分。而最后的报文主体是一个可选项,并不必定存在。服务器
一般状况下,咱们都采用上面这种口语化的形式来描述 HTTP 报文的格式,可是这种口语化的表达并不十分严谨。好比,咱们一般会这样描述一个请求头:请求头中包含了请求方法、请求 URI、HTTP 版本号,可是它们之间是否要加空格呢?加两个空格能够么?再好比上图中的 Host 头部字段,冒号后面必须加一个空格么?加两个空格能够么?不加空格,而是用 Tab 制表符能够么?冒号前面能够加空格么?spa
若是咱们用口语化的表达来描述 HTTP 报文,就很难说清楚上面这些问题。所以,RFC 7230 文档采用了 ABNF 范式来严谨的描述 HTTP 报文。code
ABNF 范式大致上分为操做符和核心规则两大方面,这里咱们不作区分,统一介绍一下与 HTTP 协议相关的描述。cdn
规则1 / 规则2
start-line = request-line / status-line // 起始行能够是一个请求行,也能够是一个状态行
复制代码
m * n
* 表示零个或多个元素:*(header field CRLF) // 能够有零或多个,以 CRLF 结尾的头部字段
1* // 表示一个或多个元素
复制代码
(规则1 规则2)
*(SP / HTAB) // 零个或多个(空格或者横向制表符)
复制代码
[]
[ message-body ]: 报文主体是一个可选参数
复制代码
SP %x20 空格
request-line = method SP request-target SP HTTP-Version CRLF
复制代码
HTAB %x09 水平tab
header-field = field-name ":" *(SP / HTAB) field-value *(SP / HTAB)
// 头部字段由字段名称和字段值组成,中间以冒号分隔,冒号后面能够有零个或多个空格或者横向制表符
复制代码
CRLF
start-line
header-filed
CRLF
body-message
// 头部字段和报文主体之间必须有一个 CRLF
复制代码
了解上面这些 ABNF 范式中的操做符和核心规则后,咱们就能够用 ABNF 范式来严谨的定义 HTTP 报文了:htm
// HTTP 报文构成:一个起始行;零个或多个头部字段;一个空行;一个可选的报文主体
HTTP-message = start-line
*( header-field CRLF )
CRLF
[ message-body ]
// 起始行构成:请求行或者状态行
start-line = request-line / status-line
// 请求行构成:请求方法;空格;请求目标;空格;协议版本;换行
request-line = method SP request-target SP HTTP-version CRLF
// 状态行构成:协议版本;空格;状态码;空格;缘由短语;换行
status-line = HTTP-version SP status-code SP reason-phrase CRLF
// 头部字段构成:
// 一个不区分大小写的字段名称;一个英文冒号;零个或多个空格或横向制表符;字段值;零个或多个空格或横向制表符
header-field = field-name ":" OWS field-value OWS
field-name = token
OWS = *(SP / HTAB)
field-value = *(field-content / obs-fold)
// 报文主体构成:用于携带请求或响应的有效载荷体
message-body = *OCTET
复制代码
一个 HTTP 报文能够是从客户端到服务器的请求报文,也能够是从服务器到客户端的响应报文。一般状况下,对于请求报文来讲,咱们称它的起始行为请求行;而对于响应报文来讲,咱们称它的起始行为状态行。
请求行描述了客户端想要如何操做服务器上的资源。它一般包括:
以实际的例子来讲:
GET /index.html HTTP/1.1
复制代码
"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
复制代码
"HTTP/1.1" 是协议版本,"200" 是状态码,"ok" 是缘由短语。意思就是告诉客户端:找到了相应资源,我已经处理好了你的请求。
从上面的图中能够知道:每一个头部字段是一个典型的 key-value 格式,最后以 CRLF 表示结束,而且在整个头部字段的最后,必须由一个 CRLF 表示头部字段的结束。
Host: 127.0.0.1:9090
Content-Type: text/html
...
复制代码
对于头部字段来讲,有一些特色须要咱们注意:
头部字段一般分为如下四种:
HTTP 报文是 HTTP 协议的核心,而头部字段就是 HTTP 报文的核心。充分理解了常见的头部字段,HTTP 协议就不在话下了,后面的文章会重点介绍常见的重要头部字段。
HTTP 协议中不要求报文主体必须存在,若是存在的话,报文主体用于携带请求或响应的有效载荷体。
一般状况下,首部字段中的 Content-Length 或 Transfer-Encoding 是请求中报文主体存在的信号。而响应中报文主体的存在取决于响应的请求方法和状态码。如 HEAD 请求方法的响应从不包括报文主体,而全部的 1xx,204 以及 304 的响应也不包含报文主体。
本文详细介绍了 HTTP 的报文结构,除了常见的口语化表达外,还引入了 RFC 7230 文档中用于描述 HTTP 报文的 ABNF 范式,进行严谨的描述。
你的点赞会给我一天好心情,若是能顺手 来个 star,再顺便关注下公众号(零幺小馆)就更完美了。