文章來源:https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md
本文用於詳細描述gRPC承載HTTP2幀的實現。
Outline
以下是grpc請求和響應流中消息原子的一般順序:
- Request → Request-Headers *Length-Prefixed-Message EOS
- Response → (Response-Headers *Length-Prefixed-Message Trailers) / Trailers-Only
Request
- Request → Request-Headers *Length-Prefixed-Message EOS
Request-Headers 在 HEADERS + CONTINUATION 幀中被髮送給HTTP2的headers。 - Request-Headers → Call-Definition *Custom-Metadata
- Call-Definition → Method Scheme Path TE [Authority] [Timeout] Content-Type [Message-Type] [Message-Encoding] [Message-Accept-Encoding] [User-Agent]
- Method → “:method POST”
- Scheme → ":scheme " (“http” / “https”)
- Path → “:path” “/” Service-Name “/” {method name} # But see note below.
- Service-Name → {IDL-specific service name}
- Authority → “:authority” {virtual host name of authority}
- TE → “te” “trailers” # Used to detect incompatible proxies
- Timeout → “grpc-timeout” TimeoutValue TimeoutUnit
- TimeoutValue → {positive integer as ASCII string of at most 8 digits}
- TimeoutUnit → Hour / Minute / Second / Millisecond / Microsecond / Nanosecond
- Hour → “H”
- Minute → “M”
- Second → “S”
- Millisecond → “m”
- Microsecond → “u”
- Nanosecond → “n”
- Content-Type → “content-type” “application/grpc” [("+proto" / “+json” / {custom})]
- Content-Coding → “identity” / “gzip” / “deflate” / “snappy” / {custom}
- Message-Encoding → “grpc-encoding” Content-Coding
- Message-Accept-Encoding → “grpc-accept-encoding” Content-Coding *("," Content-Coding)
- User-Agent → “user-agent” {structured user-agent string}
- Message-Type → “grpc-message-type” {type name for message schema}
- Custom-Metadata → Binary-Header / ASCII-Header
- Binary-Header → {Header-Name “-bin” } {base64 encoded value}
- ASCII-Header → Header-Name ASCII-Value
- Header-Name → 1*( %x30-39 / %x61-7A / “_” / “-” / “.”) ; 0-9 a-z _ - .
- ASCII-Value → 1*( %x20-%x7E ) ; space and printable ASCII
HTTP2要求保留標頭,以“:”開頭的標頭出現在所有其他標頭之前。另外,實現應該在保留頭之後立即發送Timeout,並且他們應該在發送Custom-Metadata前Call-Definition頭。
某些gRPC實現可能允許覆蓋上面顯示的Path格式,但強烈建議不要使用此功能。gRPC不會破壞使用這種覆蓋用戶,但是我們不主動支持它,並且當路徑不是上面顯示的形式時,某些功能(如,服務配置支持)將不起作用。
如果省略Timeout,服務器應該假定無限超時。客戶端實現可以根據其部署要求自由發送默認的最小超時。
Custom-Metadata 是由應用程序層定義的一組任意鍵值對。以“grpc-”開頭但未在此處列出的標題名稱保留,用於將來的grpc使用,不應被應用程序用作Custom-Metadata。
請注意,HTTP2不允許任意八位字節序列用於標頭值,因此必須使用Base64對二進制標頭值進行編碼。實現必須接收填充和未填充的值,並應發出未填充的值。應用程序通過使其名稱以“-bin”結尾來定義二進制標頭。運行時庫使用此後綴來檢測二進制標頭,並在發送和接收標頭時正確應用base64編碼和解碼。
除了具有重複標題名稱的值之外,不保證自定義元數據標題順序。重複的標題名稱可以將其值與“,”作爲分隔符連接,並在語義上等效。在解碼Base64編碼值之前,實現必須在“,”拆分Binary-Headers。
ASCII值不應具有前導或尾隨空格。如果它包含前導或尾隨空格,則可能會將其刪除。定義的ASCII-Value字符範圍比HTTP更嚴格。由於接收到HTTP中有效字段值的無效ASCII值,實現不得出錯,但不嚴格定義精確行爲:它們可能拋棄值或接收該值。如果接收,必須注意確保允許應用程序將值作爲元數據回顯。如,如果原數據作爲請求中的列表提供給應用程序,則應用程序不應通過提供與響應中的元數據相同的列表來觸發錯誤。
服務器可能會限制Request-Headers的大小,建議默認值爲8 KiB。鼓勵應用實現中計算總標頭大小,如HttP/2的SETTINGS_MAX_HEADER_LIST_SIZE
:所有頭字段的總和,對於每個字段,未壓縮字段名稱和值長度加上32的總和,二進制值得長度爲post-Base64.
Length-Prefixed-Message的重複序列在DATA幀中傳遞
- Length-Prefixed-Message → 消息長度壓縮標誌信息
- Compressed-Flag → 0 / 1 # 1表示無符號整數
- Message-Length → {length of Message} # 4字節無符號整數(大端)
- Message → *{binary octet}
Compressed-Flag 值爲1表示使用Message-Encoding頭聲明的機制壓縮Message的二進制八位字節序列。值爲0表示沒有發生Message字節的編碼。壓縮上下文不是通過消息邊界維護的,實現必須爲流中的每個消息創建新的上下文。如果省略Message-Encoding標頭,則Compressed-Flag必須爲0.
對於請求,EOS(流結束)由最後接收的DATA幀上存在 END_STREAM 標誌指示。在需要關閉請求流但沒有數據要發送的情況下,實現必須發送一個空數據幀,並設置此標誌。
Responses
- Response → (Response-Headers *Length-Prefixed-Message Trailers) / Trailers-Only
- Response-Headers → HTTP-Status [Message-Encoding] [Message-Accept-Encoding] Content-Type *Custom-Metadata
- Trailers-Only → HTTP-Status Content-Type Trailers
- Trailers → Status [Status-Message] *Custom-Metadata
- HTTP-Status → “:status 200”
- Status → “grpc-status” 1*DIGIT ; 0-9
- Status-Message → “grpc-message” Percent-Encoded
- Percent-Encoded → 1*(Percent-Byte-Unencoded / Percent-Byte-Encoded)
- Percent-Byte-Unencoded → 1*( %x20-%x24 / %x26-%x7E ) ; space and VCHAR, except %
- Percent-Byte-Encoded → “%” 2HEXDIGIT ; 0-9 A-F
每個HTTP2 HEADERS幀中都提供 Response-Headers & Trailers-Only。預計大多數回覆都包含headers和trailers,但允許使用 Trailers-Only 在出現即時錯誤時進行調用。即使狀態碼爲OK,也必須在Trailers中發送狀態。
對於響應,在最後接收到的攜帶Trailers的HEADERS幀上存在END_STREAM標誌表示流末尾。
實現應該預期破壞的部署在響應中發送非200HTTP狀態碼,及各種非grpc內容類型並且省略Status & Status-Message。實現時必須合成 Status & Status-Message 以傳播到應用程序層。
clients可能會限制Response-Headers,Trailers和Trailers-Only的大小,每個建議的默認值爲8KiB。
Status的值部分是十進制編碼的整數,作爲ASCII字符串,沒有任何前導零。
Status-Message的值部分在概念上是錯誤的Unicode字符串描述,物理編碼爲UTF-8,後跟百分號編碼(url編碼)。在 RFC 3986 §2.1 中指定了百分號編碼,儘管此處使用的表單具有不同的受限字符。解碼爲無效值時,實現不能報錯或者丟棄message。在最壞的情況下,實現可以完全終止對狀態消息的解碼,使得用戶將接收原始的百分號編碼形式。或者,實現可以解碼有效部分,同時保留損壞的%-編碼,或者用替換字符(如,’?’ 或Unicode替換字符)替換它們。
Example
簡單的一元調用展示HTTP2的幀序列:
Request
HEADERS (flags = END_HEADERS)
:method = POST
:scheme = http
:path = /google.pubsub.v2.PublisherService/CreateTopic
:authority = pubsub.googleapis.com
grpc-timeout = 1S
content-type = application/grpc+proto
grpc-encoding = gzip
authorization = Bearer y235.wef315yfh138vh31hv93hv8h3v
DATA (flags = END_STREAM)
<Length-Prefixed Message>
Response
HEADERS (flags = END_HEADERS)
:status = 200
grpc-encoding = gzip
content-type = application/grpc+proto
DATA
<Length-Prefixed Message>
HEADERS (flags = END_STREAM, END_HEADERS)
grpc-status = 0 # OK
trace-proto-bin = jher831yy13JHy3hc
User Agents
雖然該協議不需要用戶代理來運行,但建議客戶端提供一個結構化的用戶處理字符串,該字符串提供調用庫、版本&平臺的基本描述,以促進異構環境中的問題診斷。建議庫開發人員使用以下結構
User-Agent → "grpc-" Language ?("-" Variant) "/" Version ?( " (" *(AdditionalProperty ";") ")" )
如:
grpc-java/1.2.3
grpc-ruby/1.2.3
grpc-ruby-jruby/1.3.4
grpc-java-android/0.9.1 (gingerbread/1.2.4; nexus5; tmobile)
Idempotency and Retries
除非明確定義,否則gRPC調用不被認爲是冪等的。特別:
- 無法驗證已啓動的調用將不會重試。
- 沒有重複抑制機制,因爲沒有必要。
- 標記爲冪等的調用可多次發送。
HTTP2 Transport Mapping
Stream Identification
所有GRPC調用都需要指定內部ID。我們將在此方案中使用HTTP2 stream-id作爲呼叫標識符。注意:這些ID是開放式HTTP2會話的上下文,並且在處理多個HTTP2會話的給定進程中不是唯一的,也不能用作GUID。
Data Frames
DATA幀邊界與Length-Prefixed-Message邊界無關,並且實現不應對其邊界對齊做出任何假設。
Errors
在RPC期間發生應用程序或運行時錯誤時,將在Trailers中傳遞Status和Status-Message。
在某些情況下,消息流的成幀可能已損壞,RPC運行時將選擇使用RST_STREAM幀向其peer指示此狀態。RPC運行時實現應該將RST_STREAM解釋爲立即完全關閉的流,並且應該將錯誤傳播到調用應用程序層。
採用如下的RST_STREAM錯誤代碼映射到GRPC錯誤代碼。
HTTP2 Code | GRPC Code |
---|---|
NO_ERROR(0) | INTERNAL - An explicit GRPC status of OK should have been sent but this might be used to aggressively lameduck in some scenarios. |
PROTOCOL_ERROR(1) | INTERNAL |
INTERNAL_ERROR(2) | INTERNAL |
FLOW_CONTROL_ERROR(3) | INTERNAL |
SETTINGS_TIMEOUT(4) | INTERNAL |
STREAM_CLOSED | No mapping as there is no open stream to propagate to. Implementations should log. |
FRAME_SIZE_ERROR | INTERNAL |
REFUSED_STREAM | UNAVAILABLE - Indicates that no processing occurred and the request can be retried, possibly elsewhere. |
CANCEL(8) | Mapped to call cancellation when sent by a client.Mapped to CANCELLED when sent by a server. Note that servers should only use this mechanism when they need to cancel a call but the payload byte sequence is incomplete. |
COMPRESSION_ERROR | INTERNAL |
CONNECT_ERROR | INTERNAL |
ENHANCE_YOUR_CALM | RESOURCE_EXHAUSTED …with additional error detail provided by runtime to indicate that the exhausted resource is bandwidth. |
INADEQUATE_SECURITY | PERMISSION_DENIED … with additional detail indicating that permission was denied as protocol is not secure enough for call. |
Security
當TLS與HTTP2一起使用時,HTTP2規範要求使用TLS 1.2或更高版本。它還對部署中允許的密碼施加了一些額外的限制,以避免已知問題以及需要SNI支持。期望HTTP2將與專有傳輸安全機制結合使用,規範不能對其提出任何有意義的建議。
Connection Management
GOAWAY Frame
由服務器發送到客戶端以指示它們將不再接受關聯連接上的任何新流。該幀包括服務器最後成功接受的流的id。客戶端應該將上次成功接受的流後啓動的任何流視爲UNAVAILABLE,並在其他位置重試該調用。客戶端可以繼續使用已接受的流,直到它們完成或連接終止。
服務器應在終止連接之前發送GOAWAY,以便可靠地通知客戶端哪些工作已被服務器接受並正在執行。
PING Frame
客戶端和服務器都可以發送一個PING幀,其peer必須應答,來精確迴應其接受到的是什麼。這用於斷言連接仍然存在,以及提供估算端到端延遲的方法。如果服務器啓動PING未在運行時期望的截止期限內收到響應,則服務器上的所有未完成調用將以CANCELED狀態關閉。過期的客戶端啓動PING將導致所有呼叫以UNAVAILABLE狀態關閉。注意,PING的頻率非常依賴於網絡環境,實現可以根據網絡和應用要求自由調整PING頻率。
Connection failure
如果客戶端上發生可檢測的連接故障,則將以UNAVAILABLE狀態關閉所有呼叫。對於服務器,將以CANCELED狀態關閉打開的呼叫。
附錄A - GRPC for Protobuf
protobuf聲明的服務接口很容易通過protoc的代碼生成擴展到GRPC上。以下定義要使用的映射。
- Service-Name → ?( {proto package name} “.” ) {service name}
- Message-Type → {fully qualified proto message name}
- Content-Type → “application/grpc+proto”