http2 - frame - HEADER

HEADERS Frame 報頭幀

報頭幀(類型=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 (*)                       ...
 +---------------------------------------------------------------+

字段

  • Pad Length : 字節填充長度字段。8 bit。可選。Flags:PADDED 設置後要求有此字段
  • E : 1位的標記用於標識流依賴是不是專用的。可選。Flags:PRIORITY 設置後要有此字段
  • Stream Dependency : StreamID。31位流 .可選。Flags:PRIORITY 設置後要有此字段
  • Weight : 流的8位權重標記。添加一個1-256的值來存儲流的權重。這個字段是可選的,而且只在優先級標記設置的狀況下才呈現。
  • Header Block Fragment : 報頭塊碎片。
  • Padding : 填充字節

Stream Dependency,Weight** 字段被用於作流依賴,流優先級,屬於高級的話題,將來會作介紹。xml

標誌

  • END_STREAM (0x1) : 位1用來標識這是發送端對肯定的流發送的最後報頭區塊.設置這個標記將使流進入一種半封閉狀態
  • END_SEGMENT (0x2) : 位2表示這是當前端的最後一幀。中介端絕對不能跨片斷來合併幀,且在轉發幀的時候必須保持片斷的邊界。
  • END_HEADERS (0x4) : 位3表示幀包含了整個的報頭塊 ,且後面沒有延續幀。
  • PADDED (0x8) : 位4表示Pad Length字段會呈現。
  • PRIORITY (0x8) : 位6設置指示專用標記(E)

報頭幀的主體包含一個報頭塊碎片。報頭區塊大於一個報頭幀的將在延續幀中繼續傳送。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);
  }
};
相關文章
相關標籤/搜索