From d77439929c9a2101e4b3db817e0b2c9d1cccb9c1 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Thu, 30 Jul 2026 07:24:23 +0800 Subject: [PATCH] fix(server): preserve non-S3 trace context (#5450) --- rustfs/src/server/layer.rs | 35 ++++++++++++++++++++++++++- rustfs/src/storage/request_context.rs | 26 +++++++++++++------- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/rustfs/src/server/layer.rs b/rustfs/src/server/layer.rs index 41eabeb82..538dd1de5 100644 --- a/rustfs/src/server/layer.rs +++ b/rustfs/src/server/layer.rs @@ -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"] { diff --git a/rustfs/src/storage/request_context.rs b/rustfs/src/storage/request_context.rs index 6ef750c8f..d8bf66074 100644 --- a/rustfs/src/storage/request_context.rs +++ b/rustfs/src/storage/request_context.rs @@ -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]