在 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,再順便關注下公衆號(零幺小館)就更完美了。