Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 465d830

Browse files
committed
Autopropagating versioning headers everywhere
1 parent 84d51a2 commit 465d830

File tree

6 files changed

+238
-51
lines changed

6 files changed

+238
-51
lines changed

crates/store/re_protos/src/headers.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,18 @@ pub const RERUN_HTTP_HEADER_ENTRY_ID: &str = "x-rerun-entry-id";
1212
/// while HTTP2 headers only support ASCII.
1313
pub const RERUN_HTTP_HEADER_ENTRY_NAME: &str = "x-rerun-entry-name-bin";
1414

15+
/// The HTTP header key that all our official gRPC clients use to specify their identity and version.
16+
///
17+
/// All our official gRPC servers make sure to always return a copy of this header to the client as-is, in
18+
/// addition to propagating it into our gRPC metrics, traces and metrics.
19+
pub const RERUN_HTTP_HEADER_CLIENT_VERSION: &str = "x-rerun-client-version";
20+
21+
/// The HTTP header key that all our official gRPC servers use to specify their identity and version.
22+
///
23+
/// All our official gRPC servers always set this header in all their responses, in addition to
24+
/// propagating it into our gRPC metrics, traces and metrics.
25+
pub const RERUN_HTTP_HEADER_SERVER_VERSION: &str = "x-rerun-server-version";
26+
1527
/// Extension trait for [`tonic::Request`] to inject Rerun Data Protocol headers into gRPC requests.
1628
///
1729
/// Example:
@@ -127,19 +139,114 @@ impl<T> RerunHeadersExtractorExt for tonic::Request<T> {
127139
}
128140
}
129141

142+
// ---
143+
144+
pub type RerunHeadersLayer = tower::layer::util::Stack<
145+
PropagateHeadersLayer,
146+
tower::layer::util::Stack<
147+
tonic::service::InterceptorLayer<RerunVersionInterceptor>,
148+
tower::layer::util::Identity,
149+
>,
150+
>;
151+
152+
/// Instantiates a compound [`tower::Layer`] that handles all things related to Rerun headers.
153+
pub fn new_rerun_headers_layer(
154+
name: Option<String>,
155+
version: Option<String>,
156+
is_client: bool,
157+
) -> RerunHeadersLayer {
158+
tower::ServiceBuilder::new()
159+
.layer(tonic::service::interceptor::InterceptorLayer::new({
160+
RerunVersionInterceptor::new(is_client, name, version)
161+
}))
162+
.layer(new_rerun_headers_propagation_layer())
163+
.into_inner()
164+
}
165+
130166
/// Creates a new [`tower::Layer`] middleware that always makes sure to propagate Rerun headers
131167
/// back and forth across requests and responses.
132168
pub fn new_rerun_headers_propagation_layer() -> PropagateHeadersLayer {
133169
PropagateHeadersLayer::new(
134170
[
135171
http::HeaderName::from_static(RERUN_HTTP_HEADER_ENTRY_ID),
136-
http::HeaderName::from_static("x-request-id"),
172+
http::HeaderName::from_static(RERUN_HTTP_HEADER_CLIENT_VERSION),
173+
http::HeaderName::from_static(RERUN_HTTP_HEADER_SERVER_VERSION),
137174
]
138175
.into_iter()
139176
.collect(),
140177
)
141178
}
142179

180+
/// Implements a `[tonic::service::Interceptor]` that records the identity and version of the client and/or server
181+
/// in well-known headers.
182+
///
183+
/// See also [`RERUN_HTTP_HEADER_CLIENT_VERSION`] & [`RERUN_HTTP_HEADER_SERVER_VERSION`].
184+
#[derive(Clone)]
185+
pub struct RerunVersionInterceptor {
186+
is_client: bool,
187+
name: String,
188+
version: String,
189+
}
190+
191+
impl RerunVersionInterceptor {
192+
pub fn new_client(name: Option<String>, version: Option<String>) -> Self {
193+
Self::new(true, name, version)
194+
}
195+
196+
pub fn new_server(name: Option<String>, version: Option<String>) -> Self {
197+
Self::new(false, name, version)
198+
}
199+
200+
pub fn new(is_client: bool, name: Option<String>, version: Option<String>) -> Self {
201+
let mut name = name
202+
.or_else(|| std::env::var("OTEL_SERVICE_NAME").ok())
203+
.or_else(|| {
204+
let path = std::env::current_exe().ok()?;
205+
path.file_stem()
206+
.map(|stem| stem.to_string_lossy().to_string())
207+
})
208+
.unwrap_or_else(|| env!("CARGO_PKG_NAME").to_owned());
209+
210+
if !name.is_ascii() {
211+
// Cannot have non ASCII data in HTTP headers.
212+
name = "<non_ascii_name_redacted>".to_owned();
213+
}
214+
215+
let version = version.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_owned());
216+
217+
Self {
218+
is_client,
219+
name,
220+
version,
221+
}
222+
}
223+
}
224+
225+
impl tonic::service::Interceptor for RerunVersionInterceptor {
226+
fn call(&mut self, mut req: tonic::Request<()>) -> Result<tonic::Request<()>, tonic::Status> {
227+
let Self {
228+
is_client,
229+
name,
230+
version,
231+
} = self;
232+
233+
let version = format!("{name}/{version}");
234+
235+
req.metadata_mut().insert(
236+
if *is_client {
237+
RERUN_HTTP_HEADER_CLIENT_VERSION
238+
} else {
239+
RERUN_HTTP_HEADER_SERVER_VERSION
240+
},
241+
version
242+
.parse()
243+
.expect("cannot fail, checked in constructor"),
244+
);
245+
246+
Ok(req)
247+
}
248+
}
249+
143250
// ---
144251

145252
// NOTE: This if a fork of <https://docs.rs/tower-http/0.6.6/tower_http/propagate_header/struct.PropagateHeader.html>.

crates/store/re_redap_client/src/grpc.rs

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,12 @@ pub async fn channel(origin: Origin) -> Result<tonic::transport::Channel, Connec
9595
}
9696

9797
#[cfg(target_arch = "wasm32")]
98-
pub type RedapClientInner = re_protos::headers::PropagateHeaders<
99-
tonic::service::interceptor::InterceptedService<tonic_web_wasm_client::Client, AuthDecorator>,
98+
pub type RedapClientInner = tonic::service::interceptor::InterceptedService<
99+
tonic::service::interceptor::InterceptedService<
100+
re_protos::headers::PropagateHeaders<tonic_web_wasm_client::Client>,
101+
re_protos::headers::RerunVersionInterceptor,
102+
>,
103+
re_auth::client::AuthDecorator,
100104
>;
101105

102106
#[cfg(target_arch = "wasm32")]
@@ -109,8 +113,13 @@ pub(crate) async fn client(
109113
let auth = AuthDecorator::new(token);
110114

111115
let middlewares = tower::ServiceBuilder::new()
112-
.layer(re_protos::headers::new_rerun_headers_propagation_layer())
113-
.layer(tonic::service::interceptor::InterceptorLayer::new(auth));
116+
.layer(tonic::service::interceptor::InterceptorLayer::new(auth))
117+
.layer({
118+
let name = Some("rerun-web".to_owned());
119+
let version = None;
120+
let is_client = true;
121+
re_protos::headers::new_rerun_headers_layer(name, version, is_client)
122+
});
114123

115124
let svc = tower::ServiceBuilder::new()
116125
.layer(middlewares.into_inner())
@@ -120,28 +129,32 @@ pub(crate) async fn client(
120129
}
121130

122131
#[cfg(all(not(target_arch = "wasm32"), feature = "perf_telemetry"))]
123-
pub type RedapClientInner = re_protos::headers::PropagateHeaders<
124-
re_perf_telemetry::external::tower_http::trace::Trace<
125-
tonic::service::interceptor::InterceptedService<
126-
tonic::service::interceptor::InterceptedService<
127-
tonic::transport::Channel,
128-
re_auth::client::AuthDecorator,
132+
pub type RedapClientInner = tonic::service::interceptor::InterceptedService<
133+
tonic::service::interceptor::InterceptedService<
134+
re_protos::headers::PropagateHeaders<
135+
re_perf_telemetry::external::tower_http::trace::Trace<
136+
tonic::service::interceptor::InterceptedService<
137+
tonic::transport::Channel,
138+
re_perf_telemetry::TracingInjectorInterceptor,
139+
>,
140+
re_perf_telemetry::external::tower_http::classify::SharedClassifier<
141+
re_perf_telemetry::external::tower_http::classify::GrpcErrorsAsFailures,
142+
>,
143+
re_perf_telemetry::GrpcMakeSpan,
129144
>,
130-
re_perf_telemetry::TracingInjectorInterceptor,
131-
>,
132-
re_perf_telemetry::external::tower_http::classify::SharedClassifier<
133-
re_perf_telemetry::external::tower_http::classify::GrpcErrorsAsFailures,
134145
>,
135-
re_perf_telemetry::GrpcMakeSpan,
146+
re_protos::headers::RerunVersionInterceptor,
136147
>,
148+
re_auth::client::AuthDecorator,
137149
>;
138150

139151
#[cfg(all(not(target_arch = "wasm32"), not(feature = "perf_telemetry")))]
140-
pub type RedapClientInner = re_protos::headers::PropagateHeaders<
152+
pub type RedapClientInner = tonic::service::interceptor::InterceptedService<
141153
tonic::service::interceptor::InterceptedService<
142-
tonic::transport::Channel,
143-
re_auth::client::AuthDecorator,
154+
re_protos::headers::PropagateHeaders<tonic::transport::Channel>,
155+
re_protos::headers::RerunVersionInterceptor,
144156
>,
157+
re_auth::client::AuthDecorator,
145158
>;
146159

147160
pub type RedapClient = RerunCloudServiceClient<RedapClientInner>;
@@ -153,16 +166,20 @@ pub(crate) async fn client(
153166
) -> Result<RedapClient, ConnectionError> {
154167
let channel = channel(origin).await?;
155168

156-
let auth = AuthDecorator::new(token);
157-
158169
let middlewares = tower::ServiceBuilder::new()
159-
.layer(re_protos::headers::new_rerun_headers_propagation_layer());
170+
.layer(tonic::service::interceptor::InterceptorLayer::new(
171+
AuthDecorator::new(token),
172+
))
173+
.layer({
174+
let name = None;
175+
let version = None;
176+
let is_client = true;
177+
re_protos::headers::new_rerun_headers_layer(name, version, is_client)
178+
});
160179

161180
#[cfg(feature = "perf_telemetry")]
162181
let middlewares = middlewares.layer(re_perf_telemetry::new_client_telemetry_layer());
163182

164-
let middlewares = middlewares.layer(tonic::service::interceptor::InterceptorLayer::new(auth));
165-
166183
let svc = tower::ServiceBuilder::new()
167184
.layer(middlewares.into_inner())
168185
.service(channel);

crates/store/re_server/src/server.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ impl Server {
117117
});
118118

119119
let middlewares = tower::ServiceBuilder::new()
120+
.layer({
121+
let name = Some("rerun-oss".to_owned());
122+
let version = None;
123+
let is_client = false;
124+
re_protos::headers::new_rerun_headers_layer(name, version, is_client)
125+
})
120126
.layer(tonic_web::GrpcWebLayer::new()) // Support `grpc-web` clients
121127
.into_inner();
122128

crates/top/rerun/src/commands/entrypoint.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,15 @@ where
591591
re_crash_handler::install_crash_handlers(build_info.clone());
592592
}
593593

594+
// There is always value in setting this, even if `re_perf_telemetry` is disabled. For example,
595+
// the Rerun versioning headers will automatically pick it up.
596+
//
597+
// Safety: anything touching the env is unsafe, tis what it is.
598+
#[expect(unsafe_code)]
599+
unsafe {
600+
std::env::set_var("OTEL_SERVICE_NAME", "rerun");
601+
}
602+
594603
use clap::Parser as _;
595604
let mut args = Args::parse_from(args);
596605

@@ -655,12 +664,6 @@ where
655664
} else {
656665
#[cfg(all(not(target_arch = "wasm32"), feature = "perf_telemetry"))]
657666
let mut _telemetry = {
658-
// Safety: anything touching the env is unsafe, tis what it is.
659-
#[expect(unsafe_code)]
660-
unsafe {
661-
std::env::set_var("OTEL_SERVICE_NAME", "rerun");
662-
}
663-
664667
// NOTE: We're just parsing the environment, hence the `vec![]` for CLI flags.
665668
use re_perf_telemetry::external::clap::Parser as _;
666669
let args = re_perf_telemetry::TelemetryArgs::parse_from::<_, String>(vec![]);

0 commit comments

Comments
 (0)