fix(server): preserve non-S3 trace context (#5450)

This commit is contained in:
Zhengchao An
2026-07-30 07:24:23 +08:00
committed by GitHub
parent abc5f2e818
commit d77439929c
2 changed files with 51 additions and 10 deletions
+34 -1
View File
@@ -254,7 +254,7 @@ where
req.headers_mut().insert(REQUEST_ID_HEADER, request_id);
}
}
RequestContext::from_headers_without_trace_context(req.headers())
RequestContext::from_propagated_headers(req.headers())
};
let request_id = if is_s3 {
HeaderValue::from_str(&request_context.request_id).ok()
@@ -2369,6 +2369,39 @@ mod tests {
}
}
#[tokio::test]
async fn non_s3_request_context_preserves_propagated_trace_context() {
global::set_text_map_propagator(TraceContextPropagator::new());
let capture = HeaderCaptureService::default();
let captured_context = capture.request_context();
let mut service = ExternalRequestContextLayer::default().layer(capture);
let request = Request::builder()
.uri("/rustfs/admin/v3/info")
.header(REQUEST_ID_HEADER, "client-request-id")
.header("traceparent", "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
.body(())
.expect("build admin request");
let response = service.call(request).await.expect("admin response");
let context = captured_context
.lock()
.expect("captured request context")
.clone()
.expect("admin request context");
assert_eq!(
response
.headers()
.get(REQUEST_ID_HEADER)
.and_then(|value| value.to_str().ok()),
Some("client-request-id")
);
assert_eq!(context.request_id, "client-request-id");
assert_eq!(context.x_amz_request_id, "client-request-id");
assert_eq!(context.trace_id.as_deref(), Some("4bf92f3577b34da6a3ce929d0e0e4736"));
assert_eq!(context.span_id.as_deref(), Some("00f067aa0ba902b7"));
}
#[test]
fn console_redirect_request_id_contract_follows_redirect_enablement() {
for path in ["/", "/rustfs", "/index.html"] {
+17 -9
View File
@@ -100,15 +100,18 @@ impl RequestContext {
)
}
/// Create a context from propagated request headers without copying trace
/// state into the request context.
pub(crate) fn from_headers_without_trace_context(headers: &HeaderMap) -> Self {
/// Create a context for a non-S3 request while mirroring the propagated
/// canonical request ID into the compatibility alias.
pub(crate) fn from_propagated_headers(headers: &HeaderMap) -> Self {
let request_id = extract_request_id_from_headers(headers);
let (trace_id, span_id) = extract_trace_context_ids_from_headers(headers)
.map(|(trace_id, span_id)| (Some(trace_id), Some(span_id)))
.unwrap_or((None, None));
Self {
x_amz_request_id: request_id.clone(),
request_id,
trace_id: None,
span_id: None,
trace_id,
span_id,
start_time: Instant::now(),
}
}
@@ -329,17 +332,22 @@ mod tests {
}
#[test]
fn test_propagated_request_context_mirrors_canonical_request_id() {
fn test_propagated_request_context_mirrors_canonical_id_and_preserves_trace_context() {
global::set_text_map_propagator(TraceContextPropagator::new());
let mut headers = HeaderMap::new();
headers.insert("x-request-id", HeaderValue::from_static("canonical-request-id"));
headers.insert("x-amz-request-id", HeaderValue::from_static("untrusted-amz-request-id"));
headers.insert(
"traceparent",
HeaderValue::from_static("00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"),
);
let ctx = RequestContext::from_headers_without_trace_context(&headers);
let ctx = RequestContext::from_propagated_headers(&headers);
assert_eq!(ctx.request_id, "canonical-request-id");
assert_eq!(ctx.x_amz_request_id, "canonical-request-id");
assert!(ctx.trace_id.is_none());
assert!(ctx.span_id.is_none());
assert_eq!(ctx.trace_id.as_deref(), Some("4bf92f3577b34da6a3ce929d0e0e4736"));
assert_eq!(ctx.span_id.as_deref(), Some("00f067aa0ba902b7"));
}
#[test]