RequestContext is a pub-fields struct without #[non_exhaustive]:
pub struct RequestContext {
pub headers: HeaderMap,
pub deadline: Option<Instant>,
pub extensions: http::Extensions,
}
This means adding a field is a SemVer-major break for anyone who constructed a RequestContext literal (e.g. in tests). We have already grown this struct once (the mTLS work added connection-scoped data via extensions precisely to avoid a new field), and the ergonomics review identified several more candidate fields: service name, method name, RPC kind, negotiated protocol — all of which generic middleware/logging needs and which neither tonic's Request<T> nor today's RequestContext carry.
We should decide the evolution policy now, before 1.0:
Option A — #[non_exhaustive] + accessors. Make fields pub(crate), add headers(), headers_mut(), deadline(), extensions(), extensions_mut() accessors plus a builder for tests. Future fields are additive. This is the conventional Rust-library answer.
Option B — commit to the open struct. Keep fields pub and accept that any new request-scoped data must flow through extensions. Cheaper today, but pushes useful fields (method name, protocol) into a typed-map lookup with no discoverability.
Either choice should also add accessor helpers for the well-known extension types so users don't write .extensions.get::<PeerAddr>().unwrap() (which compiles, runs in tests, and panics in production because the Server inserts PeerAddr but axum integrations may not):
impl RequestContext {
pub fn peer_addr(&self) -> Option<SocketAddr>;
pub fn peer_certs(&self) -> Option<&[CertificateDer]>;
}
Scope
Small but SemVer-relevant. Best landed before any field is added under pressure.
RequestContextis apub-fields struct without#[non_exhaustive]:This means adding a field is a SemVer-major break for anyone who constructed a
RequestContextliteral (e.g. in tests). We have already grown this struct once (the mTLS work added connection-scoped data viaextensionsprecisely to avoid a new field), and the ergonomics review identified several more candidate fields: service name, method name, RPC kind, negotiated protocol — all of which generic middleware/logging needs and which neither tonic'sRequest<T>nor today'sRequestContextcarry.We should decide the evolution policy now, before 1.0:
Option A —
#[non_exhaustive]+ accessors. Make fieldspub(crate), addheaders(),headers_mut(),deadline(),extensions(),extensions_mut()accessors plus a builder for tests. Future fields are additive. This is the conventional Rust-library answer.Option B — commit to the open struct. Keep fields
puband accept that any new request-scoped data must flow throughextensions. Cheaper today, but pushes useful fields (method name, protocol) into a typed-map lookup with no discoverability.Either choice should also add accessor helpers for the well-known extension types so users don't write
.extensions.get::<PeerAddr>().unwrap()(which compiles, runs in tests, and panics in production because theServerinsertsPeerAddrbut axum integrations may not):Scope
Small but SemVer-relevant. Best landed before any field is added under pressure.