? RFC:https://tools.ietf.org/html/rfc7540
O'Reilly :https://hpbn.co/http2/
SPDY:http://www.chromium.org/spdy/
隨著Web應(yīng)用日漸廣泛,復(fù)雜程度和重要性也在不斷的增長,也因此對開發(fā)人員和用戶帶來了負擔,HTTP2 支持所有的HTTP/1.1的核心特征,提供了HTTP語義的傳輸優(yōu)化,并且在各方面做到更高效。
HTTP 2 基本概念
HTTP2 是運行在TCP或者SSL協(xié)議之上,屬于應(yīng)用層的協(xié)議。HTTP2.0消息包以二進制幀的形式進行封裝。
HTTP2引入了一下的三個新概念:
Stream: 已經(jīng)建立連接的雙向字節(jié)流,用唯一ID標示,可以傳輸一個或多個消息
Message:邏輯上的HTTP消息,請求或者響應(yīng),可以包含多個 frame
Frame:HTTP2通信的最小單位,二進制頭封裝,封裝HTTP頭部或body
HTTP2是把一個HTTP數(shù)據(jù)包分成多個幀發(fā)送,每個幀有一個二進制頭,并把HTTP分成多個獨立小幀,多個幀組成一個Message在流中發(fā)送。不同流的幀有可能交錯到達,幀的報文頭中標示了屬于哪一個流。
HTTP2 的幀格式
HTTP2的最小數(shù)據(jù)單位是幀,所有幀以9字節(jié)的幀頭并跟著0-16,383字節(jié)的數(shù)據(jù)。
Length: unsigned 24-bit integer,最大值為 2^24(16384),指的是不包括頭部的部分
Type:幀的類型
Flags:為幀類型保留的8字節(jié)字段有具體的布爾標識
R:1位的保留字段
Stream Identifier:31字節(jié)的流標識符。0是保留的,標明幀是與連接相關(guān)作為一個整體而不是一個單獨的流。
幀主體的結(jié)構(gòu)和內(nèi)容完全取決于幀類型。
HTTP2 有以下十種幀類型:
1、DATA :數(shù)據(jù)幀,Type=0x0,主要用來傳遞消息體
Pad Length : 8位,可選,只在設(shè)置了PADDED標記時呈現(xiàn)。表示幀填充的字節(jié)為單位的長度。
Data : 應(yīng)用數(shù)據(jù)。
Padding : 填充字節(jié)不包含任何應(yīng)用語義值。填充字節(jié)在發(fā)送時設(shè)為0,接收時忽略。
DATA 幀定義了以下的 Flags:
END_STREAM (0x1): 位1,表示當前幀是相應(yīng)流發(fā)送的最后一幀。設(shè)置時流進入半封閉或關(guān)閉狀態(tài)。
PADDED (0x8): 位4,表示Pad Length 字段啟用與否。
2、HEADERS :頭部幀,Type=0x1,主要用于傳遞消息頭
Pad Length:8位可選,在設(shè)置PADDED 標記時呈現(xiàn)。表示幀填充的字節(jié)為單位的長度。
E?: 1位可選,在優(yōu)先級標記設(shè)置時呈現(xiàn)。表示用于標識流依賴是否是專用的。
Stream Dependency?: 31位可選,在優(yōu)先級標記設(shè)置時呈現(xiàn)。流依賴的流的標識符。
Weight?: 8位可選,在優(yōu)先級標記設(shè)置時呈現(xiàn)。流的8位權(quán)重標記,1-256的值。
Header Block Fragment?: 報頭塊。
Padding?: 填充字節(jié)
HEADERS 幀定義了以下的 Flags:
END_STREAM (0x1) : 位1,標識是此發(fā)送端對流發(fā)送的最后報頭區(qū)塊。設(shè)置這標記使流進入半封閉狀態(tài)。
END_HEADERS (0x4)?: 位3,表示幀包含了整個的報頭塊,且后面沒有延續(xù)幀。 END_HEADERS為0的報頭幀后面必須跟著延續(xù)幀。
PADDED (0x8)?: 位4,表示Pad Length字段會呈現(xiàn)。
PRIORITY (0x20)?: 位6,優(yōu)先級標記(E), 流依賴及權(quán)重字段將會呈現(xiàn)。
3、PRIORITY :優(yōu)先級幀,Type=0x2,用于設(shè)置流的優(yōu)先級
E : 1位標記,指示流的依賴是專有的。
Stream Dependency : 流所依賴流的31位標識符。
Weight: 流的權(quán)重(8位)。1-256的權(quán)重值。
4、RST_STREAM :流結(jié)束幀,Type=0x3,用于終止異常流
RST_STREAM 幀由一個32位整數(shù)標記錯誤碼,指明流被終止的原因。
RST_STREAM 幀必須與流相關(guān)聯(lián),就是說stream id 不能為 0x0
RST_STREAM幀絕對不能在流處于“空閑”狀態(tài)下發(fā)送。
5、SETTINGS :連接配置參數(shù)幀,Type=0x4,用于設(shè)置參數(shù)
載體包含0或多個參數(shù),每個包含一個16位標識以及一個32位的值。
1)設(shè)置幀由兩個終端在連接開始時發(fā)送,連接生存期的任意時間發(fā)送。
2)設(shè)置幀的參數(shù)將替換參數(shù)中現(xiàn)有值,不能識別的忽略。
3)設(shè)置幀總是應(yīng)用于連接,而不是一個單獨的流。流ID必須為0;
SETTINGS 幀定義了以下的 Flags:
ACK (0x1)?: 位1,表示設(shè)置幀被接收端接收并應(yīng)用。如果設(shè)置了ACK,設(shè)置幀的載體必須為空。
SETTINGS_HEADER_TABLE_SIZE (0x1)?: 發(fā)送端通知遠端報頭壓縮表的最大承載量。初始值是4,096個字節(jié)。
SETTINGS_ENABLE_PUSH (0x2)?: 用來關(guān)閉服務(wù)器推送。0時不能發(fā)送PUSH_PROMISE。1表示可以推送。
SETTINGS_MAX_CONCURRENT_STREAMS (0x3)?: 標明發(fā)送端允許接收端創(chuàng)建的最大并發(fā)流的數(shù)量。沒有限制(<100) 。 0值阻止新流的創(chuàng)建。
SETTINGS_INITIAL_WINDOW_SIZE (0x4)?: 表示發(fā)送端流量控制的初始窗口大小(字節(jié))。初始值是65,535。 影響所有流的窗口大小.
SETTINGS_MAX_FRAME_SIZE (0x5): 接收最大幀大小。初始值為2^14 (16,384)字節(jié),最大值為2^24-1 or 16,777,215字節(jié)。
SETTINGS_MAX_HEADER_LIST_SIZE (0x6): 可接收的header列表長度(字節(jié)),基于非壓縮的列表大小。
6、PUSH_PROMISE:推送承諾幀,Type=0x5,Server推送之前告知Client端
1) 被推送的流并不需要按照順序使用。
2) 接收端可以給推送端返回一個RST_STREAM拒絕接收。
Pad Length : 8位,只在PADDED標記設(shè)置時才呈現(xiàn)。
R?: 1bit保留位。Padding?: 填充字節(jié)。
Promised Stream ID?: 31位整數(shù)表示終端準備發(fā)送的流標記。
Header Block Fragment?: 包含請求頭字段的報頭區(qū)塊。
PUSH_PROMISE 幀定義了以下的Flags:
END_HEADERS (0x4)?: 位3, 表明幀包含了整個報頭區(qū)塊。
PADDED (0x8)?: 位4, 表明Pad Length字段是已設(shè)置。
7、PING:Type=0x6,發(fā)送端測量最小的RTT時間,檢測連接是否可用
PING 幀定義了以下的Flags:
ACK (0x1)?: 位1表示PING幀是一個PING響應(yīng)。
1)PING幀可以被任何終端任何時刻發(fā)送。
2)PING幀必須在載體中包含一個8字節(jié)長度的數(shù)據(jù)。
收到不含ACK的PING幀必須發(fā)送一個有ACK的PING響應(yīng),帶相同的載荷。PING響應(yīng)應(yīng)設(shè)置比其他幀更高的優(yōu)先級。流ID為0,不和任何流關(guān)聯(lián);
8、GOAWAY:超時幀,Type=0x7,通知對端不要在連接上建新流
可以由客戶端或服務(wù)端發(fā)送。發(fā)動端將忽略連接上流標示符大于Last-Stream-ID的流。
接收端接收到超時幀后不能在這個連接上打開新流,可以創(chuàng)建新連接。
終端在關(guān)閉連接之前總是應(yīng)當發(fā)送一個超時幀;
適用于連接而不是特定的流。流標識符必須是0x0,否則錯誤處理;
連接關(guān)閉前小于或等于標識符上的流沒有完全關(guān)閉的,重試請求;
小于或等于最后流標識符的流可能仍然能成功完成,保持連接在打開狀態(tài)直到正在處理的流全部處理完成。
在發(fā)送超時幀后,發(fā)送端能丟棄流標識符大于最終流標識的流的幀。但任何修改流狀態(tài)的幀不能被忽略。
超時幀包含一個32位錯誤碼,包含關(guān)閉連接的原因。
9、WINDOW_UPDATE:Type=0x8,實現(xiàn)流量控制
1) 可以作用單獨的流(ID!=0) 或 整個連接(ID==0)
2) 所有類型的流量控制都是逐跳的(hop-by-hop), 中介端不會轉(zhuǎn)發(fā)
3) 流量控制只適用于Data幀
4) 一個保留字節(jié),一個31位整數(shù)( 發(fā)送端被允許傳輸?shù)淖止?jié)數(shù),它的大小是接收端的緩存能力的衡量)。
5) 流和連接的初始值都是65535;流的窗口大小可以用SETTING幀設(shè)置大小SETTINGS_INITIAL_WINDOW_SIZE;
6) 通過設(shè)置窗口大小,可能導(dǎo)致窗口大小為負數(shù)(當前有10字節(jié)數(shù)據(jù),設(shè)置為5字節(jié),則剩余-5字節(jié)的長度)。
10、CONTINUATION:延續(xù)幀,Type=0x9,延續(xù)一個報頭區(qū)塊
只要流上前一幀是不帶END_HEADERS的HEADERS幀、PUSH_PROMISE幀或者不帶有END_HEADERS標記的CONTINUATION幀,可以發(fā)送任意多個延續(xù)幀。
CONTINUATION?幀定義了以下的Flags:
END_HEADERS (0x4)?: 位3,指示幀是否是報頭區(qū)塊的終止。 如果END_HEADERS位沒有被設(shè)置,這個幀必須跟著另一個延續(xù)幀。
HTTP2 錯誤碼
HTTP2 連接過程
http2 的版本標識:
h2:基于TLS之上構(gòu)建的HTTP/2,作為ALPN的標識符,兩個字節(jié)表示,0x68, 0x32,即https
h2c:直接在TCP之上構(gòu)建的HTTP/2,缺乏安全保證,即http
HTTP版本的請求過程:
在不知道服務(wù)器是否支持http2的情況下,可以利用http的升級機制發(fā)送試探包
1、客戶端發(fā)起請求
2、服務(wù)器不支持 http2,直接按照 http/1.1響應(yīng)
3、服務(wù)器支持 http2,通知客戶端切換到http2
4、服務(wù)器發(fā)送的第一個http2幀,必須為SETTINGS幀做為連接序言
5、客戶端接收到101響應(yīng)后,也必須發(fā)送一個序言作為響應(yīng),其邏輯結(jié)構(gòu):
6、客戶端可以馬上發(fā)送請求幀或其它幀過去,不用等待來自服務(wù)器端的SETTINGS幀
7、任一端接收到SETTINGS幀之后,都需要返回一個包含確認標志位SETTIGN作為確認
8、其它幀的正常傳輸
HTTPS 版本的建立連接:
1、客戶端和服務(wù)器端TLS層協(xié)商
2、客戶端發(fā)送連接序言(同上表示,PRI + SETTINGS)
3、接收到客戶端連接序言之后,服務(wù)器端發(fā)送連接序言
4、雙方各自確認SETTINGS幀
5、其它幀的正常傳輸
HTTP/2的直接連接:
1、客戶端必須首先發(fā)送一個連接序言,其邏輯結(jié)構(gòu):
2、發(fā)送完畢序言之后,客戶端可以不用等待來自服務(wù)器端響應(yīng),馬上發(fā)送HTTP/2其它幀
3、服務(wù)器端接收到客戶端的連接序言之后,需要發(fā)送一個SETTINGS幀作為連接序言
4、任一端接收到SETTINGS幀之后,都需要返回一個包含確認標志位SETTIGN作為確認
5、其它幀的正常傳輸
對比明文版的HTTP/1.1和HTTP/2完成一次請求-響應(yīng):
1、HTTP/1.1在建立建立之后,只需要發(fā)送請求報文數(shù)據(jù)
2、HTTP/2客戶端需要在連接建立之初馬上發(fā)送一個連接序言過去,然后才是正常請求
3、兩端(客戶端+服務(wù)器端)的兩次完整的連接序言+確認的交互流程,多了兩次往返過程