-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
Feature Request
gRPC Rust requires a Metadata API that is consistent with the gRPC over HTTP/2 protocol and capable of supporting true binary metadata (gRFC G1) in the future.
While evaluating the feasibility of reusing tonic's MetadataMap, I identified two major areas requiring modification:
- Lazy encoding of binary values.
- Stricter validation of header keys and values.
Motivation
In gRPC, binary metadata (keys ending with the -bin suffix) must be base64 encoded because the HTTP/2 specification discourages specific special characters in header values.
gRFC G1 describes an extension to the gRPC over HTTP/2 protocol where peers can use the HTTP/2 Settings frame to indicate support for raw (non-base64 encoded) metadata. This eliminates the CPU overhead of base64 encoding binary data. To implement this, the sender must inspect the peer's Settings frame to decide whether or not to encode the data.
Currently, MetadataMap encodes data immediately upon insertion into the map, incurring the CPU cost regardless of the peer's capabilities.
Proposal
Lazily encode binary data
To support gRFC G1 in the future, we must modify the internal structure of MetadataMap to store unencoded header values. This could remain an http::HeaderMap<UnencodedHeaderValue>, where UnencodedHeaderValue wraps the raw bytes.
The MetadataMap::into_headers and MetadataMap::from_headers methods would be updated to convert the internal http::HeaderMap<UnencodedHeaderValue> to/from an http::HeaderMap<http::HeaderValue>.
Because the encoded data would no longer be stored directly in the MetadataMap, APIs that provide a reference to encoded data must be removed. While they could theoretically be retained for Ascii values, keeping them for binary values would require returning unencoded data, which constitutes a larger behaviour change.
APIs to remove:
Trait implementations to remove:
The following implementations expose the internal http::HeaderMap. These must be removed because the map will now hold unencoded values requiring conversion:
Stricter header validation
To ensure compliance with the gRPC over HTTP/2 protocol, the following changes regarding header validation are required:
- ASCII Values: Disallow extended ASCII characters (128-255).
- ASCII Values: Strip leading and trailing whitespace.
- Key Validation: Header keys must only contain characters from the set
0-9 a-z _ - .. - Import Logic: In
MetadataMap::from_headers, headers containing extended ASCII characters (128-255) or keys with invalid characters will be dropped/ignored.
Alternatives
I could implement a simplified MetadataMap for gRPC Rust that exposes an API similar to HashMap<String, Vec<String>> while hiding the internal data structure. For comparison, gRPC C++ and Go do not provide typed APIs for ASCII and binary metadata, instead operating directly on strings.
The following shows the proposed changes: arjan-bal#8