HTTP 1.1 之前的實現就不討論了,因為它們已經過時太久了,我上網的時候就已經接觸不到了,所以主要說說 HTTP/1.1、HTTP/2.
HTTP/1.1#
HTTP/1.1 協議報文簡介#
CRLF:
\r\n
METHOD: HTTP 請求,
GET
、POST
、PUT
、DELETE
...URI: 統一資源標識符,例如
/
,/index.html
...HTTPVersion: HTTP 協議的版本號,例如
HTTP/1.1
,HTTP/2
HEADERS: 請求頭,例如
Host:localhost
,Accept: */*
.BODY: 請求體,例如說一個 JSON 數據
{"name":"fzdwx"}
HTTPStatus: HTTP 響應狀態,常見的有
200
,404
等HTTPStatusDesc: HTTP 響應狀態描述,
200
對應的OK
.
請求#
METHOD<SPACE>URI<SPACE>HTTPVersion
HEADERS
<CRLF>
BODY
示例:
GET /hello HTTP/1.1
Host: 192.168.1.107:8889
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.82 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
響應#
HTTPVersion HTTPStatus HTTPStatusDesc
HEADERS
<CRLF>
BODY
示例:
如果響應中使用了
transfer-encoding: chunked
這個來替代Content-Length
, 就表示這是一個不固定大小的響應,結尾通常用0\r\n
來分割.
HTTP/1.1 200 OK
transfer-encoding: chunked
content-type: application/json; charset=utf-8
0/r/n
HTTP/1.1 主要新特性#
- 默認是長連接 (
Connection: Keep-alive
), 支持一個 TCP 連接處理多個請求. - 快取策略,在請求頭中使用
Cache-Control
,Expires
,Last-Modified
,ETag
等來控制. - 允許響應分塊,就是上面提到的
transfer-encoding: chunked
, 允許服務端可以多次返回響應體.
但是還是存在一定的問題,例如說如果有一個 TCP 連接阻塞了,還是會開啟新的 TCP 連接進行處理請求.
H2#
HTTP2 中的主要概念:
Connection
: 一個 TCP 連接包含一個或多個Stream
, 所有的通訊都在一個 TCP 連接上完成.Stream
: 一個可以雙向通訊的數據流,包含一條或多條Message
, 每個數據流都一個唯一標識符以及可選的優先級信息.Message
: 對應 HTTP/1.1 中的請求或響應,包含一條或多條Frame
.Frame
: 最小傳輸單位, 它以二進制進行編碼.
在 HTTP/1.1 中是有Start Line
+ header
+ body
組成的,而在 H2 中是由一個HEADER Frame
以及多個DATA Frame
組成的.
Frame#
通常有一些公共的字段,例如Length
,Type
,Flags
以及Stream Id
;也各個類型所獨有的字段.
分類如下:
- DATA: 用於傳輸 http 消息體.
- HEADERS: 用於傳輸首部字段.
- PRIORITY: 用於指定或重新指定引用資源的優先級.
- RST_STREAM: 用於通知流的非正常終止.
- SETTINGS: 用於約定客戶端和服務端的配置數據。例如設置初識的雙向流量控制窗口大小.
- PUSH_PROMISE: 服務端推送許可.
- PING: 用於計算往返時間,執行 “活性” 檢活.
- GOAWAY: 用於通知對端停止在當前連接中創建流.
- WINDOW_UPDATE: 用於調整個別流或個別連接的流量.
- CONTINUATION: 專門用於傳遞較大 HTTP 頭部時的持續幀.
為什麼 H2 必須要走 HTTPS?#
這其實在 H2 標準中沒有規定,主要是為了更方便的進行 HTTP 協議的 升級 / 協商,確認一個 Web 服務器是否支持 H2 通常有兩種方式:
- 在請求頭中設置
Upgrade: HTTP/2.0
以及Connection: Upgrade,HTTP2-Settings
等,類似升級到Websocket
. - 使用
TLS
中的ALPN
(Application Layer Protocol Negotiation, 應用層協議協商) 中的ALPN Next Protocol
字段,在Client Hello
與Server Hello
這個階段就可以確定下來.
而現在的瀏覽器基本都是實現的方式二, 即與 HTTPS 綁定在一起. 但是如果我們不用瀏覽器進行訪問,當然也可以不用 HTTPS.
詳細可參考.
為什麼 H2 能實現並行響應請求?#
在 HTTP/1.1 中,請求與響應是一一對應的,在同一個連接裡,客戶端依次發送兩個請求,一段時間以後收到來自服務器的一個響應,這個響應一定是對應於第一個發出去的請求的.
因為沒有一個標誌來表示哪個響應對應哪個請求.
而在 H2 中基於Stream
和Frame
的設計: 每個Frame
都帶有Stream Id
來標識是否為同一個Stream
裡面的數據, 每個Stream
互不影響,這樣就能做到在一個 TCP 裡面連接裡傳輸多對請求 / 響應.
H2 的新特性#
H2 的對 HTTP/1.1 優化的核心就是 使用盡可能少的連接數.
- 多路復用:只用一個 TCP 連接就能處理多對 請求 / 響應,不用在開啟另外的 TCP 連接,就是通過
Stream
與Frame
來實現的. - 二進制分幀:使用
Frame
為最小單位進行通訊,並採用二進制編碼. - 頭部壓縮: 使用
HPACK
算法進行優化.在 HTTP/1 中消息體可以用 gzip 進行壓縮,但是請求頭通常沒有任何壓縮,有時候請求頭的數據可能比請求體的數據還多.
- 請求優先級:一般在
HEADERS
幀與PRIORITY
幀中攜帶,通常依賴於服務端的支持程度.
工具#
生成測試簽名#
go run $GOROOT/src/crypto/tls/generate_cert.go --host localhost
使用 curl 調試 HTTPS#
curl https://zcygov.cn -vv