use std::sync::Arc;
use tonic::metadata::{Ascii, MetadataKey, MetadataValue};
const SPIFFE_HEADER_KEY: &str = "workload.spiffe.io";
const SPIFFE_HEADER_VALUE: &str = "true";
static PARSED_HEADER_KEY: std::sync::LazyLock<MetadataKey<Ascii>> =
std::sync::LazyLock::new(|| MetadataKey::from_static(SPIFFE_HEADER_KEY));
static PARSED_HEADER_VALUE: std::sync::LazyLock<MetadataValue<Ascii>> =
std::sync::LazyLock::new(|| MetadataValue::from_static(SPIFFE_HEADER_VALUE));
pub type InterceptorFn =
Arc<dyn Fn(&mut tonic::Request<()>) -> Result<(), tonic::Status> + Send + Sync>;
#[derive(Clone)]
pub(super) struct MetadataAdder {
extra: Option<InterceptorFn>,
}
impl MetadataAdder {
pub(super) fn new(extra: Option<InterceptorFn>) -> Self {
Self { extra }
}
}
impl tonic::service::Interceptor for MetadataAdder {
fn call(
&mut self,
mut request: tonic::Request<()>,
) -> Result<tonic::Request<()>, tonic::Status> {
if let Some(extra) = &self.extra {
extra(&mut request)?;
}
request
.metadata_mut()
.insert(PARSED_HEADER_KEY.clone(), PARSED_HEADER_VALUE.clone());
Ok(request)
}
}
#[cfg(test)]
mod tests {
use super::*;
use tonic::service::Interceptor as _;
#[test]
fn spiffe_header_always_inserted() {
let mut adder = MetadataAdder::new(None);
let request = tonic::Request::new(());
let result = adder.call(request);
let response = result.expect("interceptor should succeed");
let value = response
.metadata()
.get(SPIFFE_HEADER_KEY)
.expect("spiffe header should be present");
assert_eq!(value, SPIFFE_HEADER_VALUE);
}
#[test]
fn custom_interceptor_metadata_added() {
let interceptor: InterceptorFn = Arc::new(|req| {
req.metadata_mut().insert(
MetadataKey::from_static("authorization"),
MetadataValue::from_static("Bearer test-token"),
);
Ok(())
});
let mut adder = MetadataAdder::new(Some(interceptor));
let request = tonic::Request::new(());
let result = adder.call(request);
let response = result.expect("interceptor should succeed");
assert_eq!(
response.metadata().get(SPIFFE_HEADER_KEY).expect("present"),
SPIFFE_HEADER_VALUE,
);
assert_eq!(
response.metadata().get("authorization").expect("present"),
"Bearer test-token",
);
}
#[test]
fn spiffe_header_preserved_when_custom_interceptor_overwrites_it() {
let interceptor: InterceptorFn = Arc::new(|req| {
req.metadata_mut().remove(SPIFFE_HEADER_KEY);
req.metadata_mut().insert(
MetadataKey::from_static(SPIFFE_HEADER_KEY),
MetadataValue::from_static("false"),
);
Ok(())
});
let mut adder = MetadataAdder::new(Some(interceptor));
let response = adder
.call(tonic::Request::new(()))
.expect("interceptor should succeed");
assert_eq!(
response.metadata().get(SPIFFE_HEADER_KEY).expect("present"),
SPIFFE_HEADER_VALUE,
);
}
#[test]
fn custom_interceptor_error_propagates() {
let interceptor: InterceptorFn =
Arc::new(|_| Err(tonic::Status::internal("token expired")));
let mut adder = MetadataAdder::new(Some(interceptor));
let request = tonic::Request::new(());
let result = adder.call(request);
let err = result.expect_err("interceptor should fail");
assert_eq!(err.code(), tonic::Code::Internal);
assert_eq!(err.message(), "token expired");
}
}