報頭幀(類型=0x1)用來打開一個流 ,而且同時能夠攜帶頭塊的碎片。報頭幀能在流打開或者半封閉(遠程)的狀態下發送。html
頭塊碎片(Header Block Fragment),名字古怪甚至有點嚇人,可實際上也沒有更好的表達方法。須要咱們稍有耐心,一步步的去了解。前端
當咱們使用chrome訪問https://ietf.org/時,能夠在chrome開發工具中看到,chrome發出以下樣式請求給服務器:chrome
Accept:text/html,application/xhtml+xml,... Accept-Encoding:gzip, deflate, sdch Accept-Language:en-US,en;q=0.8,... Connection:keep-alive Host:ietf.org RA-Sid:... RA-Ver:2.8.9 User-Agent:Mozilla/5.0 (Windows NT 6.1; ...
其中的每一行都是一個鍵值對的映射被稱爲頭字段(head field)。一組頭字段一塊兒構成一個頭字段表(head field list),經過序列化和壓縮變成一個或者幾個幀。服務器
如此,一個頭字段表在http2的場景下,爲了高效傳輸,會被序列化、壓縮以後打碎爲多個幀,每一個幀有它的一個碎片。接收方會把這些碎片經過組裝,反壓縮,反序列化變成原始的頭塊表。這就是頭塊碎片這個名字的由來。app
完整的頭塊(head block)由以下兩種狀況構成:工具
o 一個單獨的 HEADERS 或者 PUSH_PROMISE 幀(設置了END_HEADERS 標誌)開發工具
o 一個單獨的 HEADERS 或者 PUSH_PROMISE 幀(未設置END_HEADERS 標誌)加上一個或者多個CONTINUATION 幀,最後一個, CONTINUATION 設置了END_HEADERS 標誌code
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Pad Length? (8)| +-+-------------+-----------------------------------------------+ |E| Stream Dependency? (31) | +-+-------------+-----------------------------------------------+ | Weight? (8) | +-+-------------+-----------------------------------------------+ | Header Block Fragment (*) ... +---------------------------------------------------------------+ | Padding (*) ... +---------------------------------------------------------------+
Stream Dependency,Weight** 字段被用於作流依賴,流優先級,屬於高級的話題,將來會作介紹。xml
報頭幀的主體包含一個報頭塊碎片。報頭區塊大於一個報頭幀的將在延續幀中繼續傳送。htm
報頭幀必須與一個流相關聯。若是一個接收到一個流標示識0x0的報頭幀,接收端必須響應一個類型爲協議錯誤的鏈接錯誤
Serializer.HEADERS = function writeHeadersPriority(frame, buffers) { if (frame.flags.PRIORITY) { var buffer = new Buffer(5); assert((0 <= frame.priorityDependency) && (frame.priorityDependency <= 0x7fffffff), frame.priorityDependency); buffer.writeUInt32BE(frame.priorityDependency, 0); if (frame.exclusiveDependency) { buffer[0] |= 0x80; } assert((0 <= frame.priorityWeight) && (frame.priorityWeight <= 0xff), frame.priorityWeight); buffer.writeUInt8(frame.priorityWeight, 4); buffers.push(buffer); } buffers.push(frame.data); }; Deserializer.HEADERS = function readHeadersPriority(buffer, frame) { var dataOffset = 0; var paddingLength = 0; if (frame.flags.PADDED) { paddingLength = (buffer.readUInt8(dataOffset) & 0xff); dataOffset = 1; } if (frame.flags.PRIORITY) { var dependencyData = new Buffer(4); buffer.copy(dependencyData, 0, dataOffset, dataOffset + 4); dataOffset += 4; frame.exclusiveDependency = !!(dependencyData[0] & 0x80); dependencyData[0] &= 0x7f; frame.priorityDependency = dependencyData.readUInt32BE(0); frame.priorityWeight = buffer.readUInt8(dataOffset); dataOffset += 1; } if (paddingLength) { frame.data = buffer.slice(dataOffset, -1 * paddingLength); } else { frame.data = buffer.slice(dataOffset); } };