Source: docs/protocol-mapping.md · Edit on GitHub
Protocol mapping
How lean-grpc implements gRPC over HTTP/2 and related conventions. This is the lean-grpc-owned mapping doc; the upstream PROTOCOL-HTTP2 text remains normative for the wire format.
Request
| Protocol atom | lean-grpc |
|---|---|
:method |
POST by default; GET for the wire-level cacheable_unary check |
:scheme |
http on h2c; https when channel credentials are TLS |
:path |
/{service}/{method} |
:authority |
Dial host / DialOptions.authority |
te |
trailers |
content-type |
application/grpc+proto |
user-agent |
grpc-lean/<version> |
grpc-timeout |
Parsed by Metadata.parseTimeoutMs (H/M/S/m/u/n) |
grpc-encoding / grpc-accept-encoding |
identity, gzip, deflate, snappy |
| Custom metadata | ASCII via Metadata.add; binary via addBin (*-bin, unpadded base64 preferred) |
Length-prefixed messages: 1-byte compressed flag + 4-byte big-endian length + payload (Grpc.Message). Compression contexts are not shared across messages.
Response
| Protocol atom | lean-grpc |
|---|---|
:status 200 |
Normal success and most gRPC errors |
| HTTP 415 | Content-type does not start with application/grpc |
| Response headers | :status, content-type, optional grpc-encoding |
| Trailers | grpc-status, optional grpc-message (percent-encoded), optional grpc-status-details-bin |
| Trailers-only | Single HEADERS+END_STREAM with :status + content-type + status trailers (unimplemented, zero timeout) |
grpc-status-details-bin carries serialized google.rpc.Status (Proto.WellKnown.RpcStatus). Details are rejected when status is OK or when the embedded code contradicts grpc-status.
RST_STREAM → gRPC status
Implemented in H2.Client.rstToTrailers / stream rstErrorCode:
| HTTP/2 code | gRPC |
|---|---|
| NO_ERROR, PROTOCOL_ERROR, INTERNAL_ERROR, FLOW_CONTROL_ERROR, SETTINGS_TIMEOUT, FRAME_SIZE_ERROR, COMPRESSION_ERROR, CONNECT_ERROR | INTERNAL |
| REFUSED_STREAM | UNAVAILABLE |
| CANCEL | CANCELLED |
| ENHANCE_YOUR_CALM | RESOURCE_EXHAUSTED |
| INADEQUATE_SECURITY | PERMISSION_DENIED |
Client-initiated cancel sets local rstErrorCode so waiters observe CANCELLED without hanging.
Connection management
| Feature | Behavior |
|---|---|
| SETTINGS | Exchanged after preface; duplicate SETTINGS ACK avoided (C-core/Python) |
| WINDOW_UPDATE / flow control | Connection + stream windows; pending send queue |
| PING | Keepalive; unanswered → GOAWAY + drop pool |
| GOAWAY | wentAway; in-flight may finish; new streams → UNAVAILABLE |
| PUSH_PROMISE | Connection error (PROTOCOL_ERROR) — clients do not accept push |
| CONTINUATION | Supported for large header blocks |
HTTP/2 conformance is gated by h2spec (scripts/h2spec.sh): 145 passed, 1 skipped, 0 failed.
Timeouts and deadlines
- Client sends
grpc-timeoutand enforces wall-clock deadline inawaitResponse(RST + DEADLINE_EXCEEDED). - Server treats parsed
0timeout as immediate DEADLINE_EXCEEDED (trailers-only). - Nanosecond unit maps to
0ms (immediate); microseconds coarsen to ≥1 ms when non-zero.
Compression
| Algorithm | Notes |
|---|---|
| identity | Default |
| gzip | Peer path via zlib helper / system gzip; stored-deflate fallback |
| deflate | zlib / RFC 1950-style container |
| snappy | Literal-block framing for interop |
Negotiation preference: gzip > deflate > snappy > identity. Message Compressed-Flag must be 0 when encoding is omitted.
Security
| Mode | Mapping |
|---|---|
| h2c | Cleartext HTTP/2 (dev / mesh sidecar) |
TLS 1.2+ ALPN h2 |
Grpc.Native.Tls / OpenSSL |
| mTLS | Client cert+key; server clientCaPath |
| Call credentials | authorization: Bearer … |
| ADC | SA JWT exchange or GCE metadata (mock in CI) |
| ALTS | Not implemented — allowlisted |
HTTP CONNECT proxying is out of scope (direct dial only).
Name resolution and LB
| Mechanism | Mapping |
|---|---|
host:port / dns:///host:port |
Resolver + optional multi-A lookup |
pick_first / round_robin |
Balancer; RR picks per RPC |
| Service config JSON | timeout, retry, hedging, LB policy |
xds:///name |
ADS LDS→RDS→CDS→EDS (+ SDS stub) |
| grpclb | Optional thin BalanceLoad client |
Standard services
| Service | Support |
|---|---|
grpc.health.v1.Health |
Check + Watch |
| Server reflection v1 / v1alpha | list_services, file_by_filename, file_containing_symbol (registry-backed) |
| channelz | GetTopChannels / GetServers with live counters |
| ORCA | Per-RPC trailer + OOB stream |
Interop coverage
Official non-auth cases are exercised Lean↔︎Lean, Go↔︎Lean, Python↔︎Lean, and Rust (tonic)↔︎Lean (see conformance.md). Intentionally limited or skipped:
| Case | Reason |
|---|---|
Full cacheable_unary |
Needs caching proxy; Python rejects GET |
| Live Google auth / ADC | Mock CI; live script manual |
| ALTS / GCE channel | Allowlisted |
| CONNECT | N/A |
References
- Upstream: PROTOCOL-HTTP2.md
- Interop: interop-test-descriptions.md
- HTTP/2: RFC 9113
- Status details:
google.rpc.Statusin googleapis
Source: docs/protocol-mapping.md in RileyBetts/lean-grpc. Hosted docs may lag the repository slightly.