@@ -6,19 +6,23 @@ use std::sync::Arc;
66
77use anyhow:: Context ;
88use bytes:: Bytes ;
9+ use http:: { HeaderMap , HeaderName , HeaderValue } ;
910use http_body_util:: combinators:: BoxBody ;
1011use http_body_util:: { BodyExt , Empty , Full } ;
1112use hyper:: body:: Incoming ;
1213use hyper:: client:: conn:: http2 as h2_client;
1314use hyper:: service:: Service ;
1415use hyper:: { Method , Request , Response , StatusCode } ;
1516use hyper_util:: rt:: { TokioExecutor , TokioIo } ;
17+ use opentelemetry:: global;
18+ use opentelemetry:: propagation:: Injector ;
1619use rustls:: ClientConfig ;
1720use rustls_pki_types:: ServerName ;
1821use tokio:: io:: copy_bidirectional;
1922use tokio:: net:: TcpStream ;
2023use tokio:: sync:: Mutex ;
21- use tracing:: { error, info, warn} ;
24+ use tracing:: { Instrument , error, info, warn} ;
25+ use tracing_opentelemetry:: OpenTelemetrySpanExt ;
2226
2327type Body = BoxBody < Bytes , Infallible > ;
2428
@@ -198,14 +202,34 @@ fn build_connect_request(authority: &str) -> anyhow::Result<Request<Body>> {
198202 . build ( )
199203 . context ( "building CONNECT URI" ) ?;
200204
201- let req = Request :: builder ( )
205+ let mut req = Request :: builder ( )
202206 . method ( Method :: CONNECT )
203207 . uri ( uri)
204208 . body ( Empty :: < Bytes > :: new ( ) . boxed ( ) )
205209 . context ( "building CONNECT request" ) ?;
210+ inject_current_trace_context ( req. headers_mut ( ) ) ;
206211 Ok ( req)
207212}
208213
214+ struct HeaderInjector < ' a > ( & ' a mut HeaderMap ) ;
215+
216+ impl Injector for HeaderInjector < ' _ > {
217+ fn set ( & mut self , key : & str , value : String ) {
218+ if let Ok ( name) = HeaderName :: from_bytes ( key. as_bytes ( ) )
219+ && let Ok ( value) = HeaderValue :: from_str ( & value)
220+ {
221+ self . 0 . insert ( name, value) ;
222+ }
223+ }
224+ }
225+
226+ fn inject_current_trace_context ( headers : & mut HeaderMap ) {
227+ let context = tracing:: Span :: current ( ) . context ( ) ;
228+ global:: get_text_map_propagator ( |propagator| {
229+ propagator. inject_context ( & context, & mut HeaderInjector ( headers) )
230+ } ) ;
231+ }
232+
209233#[ derive( Clone ) ]
210234struct SidecarService {
211235 connector : Arc < GatewayConnector > ,
@@ -238,106 +262,122 @@ async fn handle(
238262 None => return response ( StatusCode :: BAD_REQUEST , "missing authority" ) ,
239263 } ;
240264
241- info ! (
265+ let span = tracing:: info_span!(
266+ "sidecar CONNECT" ,
242267 source_identity = %connector. source_identity( ) ,
243268 source_peer_addr = %source_peer_addr,
244269 dest_authority = %authority,
245270 gateway_endpoint = %connector. gateway_endpoint( ) ,
246- "CONNECT request"
247271 ) ;
248272
249- let gw_response = match connector. send_connect ( & authority, source_peer_addr) . await {
250- Ok ( r) => r,
251- Err ( e) => {
252- error ! (
253- source_identity = %connector. source_identity( ) ,
254- source_peer_addr = %source_peer_addr,
255- dest_authority = %authority,
256- gateway_endpoint = %connector. gateway_endpoint( ) ,
257- error = %e,
258- "gateway connect failed"
259- ) ;
260- return response ( StatusCode :: BAD_GATEWAY , "gateway unreachable" ) ;
261- }
262- } ;
263-
264- let status = gw_response. status ( ) ;
265- if status != StatusCode :: OK {
266- warn ! (
273+ async move {
274+ info ! (
267275 source_identity = %connector. source_identity( ) ,
268276 source_peer_addr = %source_peer_addr,
269277 dest_authority = %authority,
270278 gateway_endpoint = %connector. gateway_endpoint( ) ,
271- %status,
272- "gateway rejected CONNECT"
279+ "CONNECT request"
273280 ) ;
274- return response ( status, "gateway denied request" ) ;
275- }
276281
277- let gw_upgraded = match hyper:: upgrade:: on ( gw_response) . await {
278- Ok ( u) => u,
279- Err ( e) => {
280- error ! (
282+ let gw_response = match connector. send_connect ( & authority, source_peer_addr) . await {
283+ Ok ( r) => r,
284+ Err ( e) => {
285+ error ! (
286+ source_identity = %connector. source_identity( ) ,
287+ source_peer_addr = %source_peer_addr,
288+ dest_authority = %authority,
289+ gateway_endpoint = %connector. gateway_endpoint( ) ,
290+ error = %e,
291+ "gateway connect failed"
292+ ) ;
293+ return response ( StatusCode :: BAD_GATEWAY , "gateway unreachable" ) ;
294+ }
295+ } ;
296+
297+ let status = gw_response. status ( ) ;
298+ if status != StatusCode :: OK {
299+ warn ! (
281300 source_identity = %connector. source_identity( ) ,
282301 source_peer_addr = %source_peer_addr,
283302 dest_authority = %authority,
284303 gateway_endpoint = %connector. gateway_endpoint( ) ,
285- error = %e ,
286- "gateway upgrade failed "
304+ %status ,
305+ "gateway rejected CONNECT "
287306 ) ;
288- return response ( StatusCode :: BAD_GATEWAY , "gateway tunnel failed " ) ;
307+ return response ( status , "gateway denied request " ) ;
289308 }
290- } ;
291-
292- let client_upgrade = hyper:: upgrade:: on ( req) ;
293- let source_identity = connector. source_identity ( ) . to_owned ( ) ;
294- let gateway_endpoint = connector. gateway_endpoint ( ) . to_owned ( ) ;
295309
296- tokio:: spawn ( async move {
297- let client_upgraded = match client_upgrade. await {
310+ let gw_upgraded = match hyper:: upgrade:: on ( gw_response) . await {
298311 Ok ( u) => u,
299312 Err ( e) => {
300- warn ! (
301- source_identity = %source_identity,
313+ error ! (
314+ source_identity = %connector . source_identity( ) ,
302315 source_peer_addr = %source_peer_addr,
303316 dest_authority = %authority,
304- gateway_endpoint = %gateway_endpoint,
317+ gateway_endpoint = %connector . gateway_endpoint( ) ,
305318 error = %e,
306- "client upgrade failed"
319+ "gateway upgrade failed"
307320 ) ;
308- return ;
321+ return response ( StatusCode :: BAD_GATEWAY , "gateway tunnel failed" ) ;
309322 }
310323 } ;
311324
312- let mut client_io = TokioIo :: new ( client_upgraded) ;
313- let mut gw_io = TokioIo :: new ( gw_upgraded) ;
325+ let client_upgrade = hyper:: upgrade:: on ( req) ;
326+ let source_identity = connector. source_identity ( ) . to_owned ( ) ;
327+ let gateway_endpoint = connector. gateway_endpoint ( ) . to_owned ( ) ;
314328
315- match copy_bidirectional ( & mut client_io, & mut gw_io) . await {
316- Ok ( ( up, down) ) => {
317- info ! (
318- source_identity = %source_identity,
319- source_peer_addr = %source_peer_addr,
320- dest_authority = %authority,
321- gateway_endpoint = %gateway_endpoint,
322- bytes_client_to_dest = up,
323- bytes_dest_to_client = down,
324- "tunnel closed"
325- ) ;
326- }
327- Err ( e) => {
328- error ! (
329- source_identity = %source_identity,
330- source_peer_addr = %source_peer_addr,
331- dest_authority = %authority,
332- gateway_endpoint = %gateway_endpoint,
333- error = %e,
334- "tunnel error"
335- ) ;
329+ let tunnel_span = tracing:: Span :: current ( ) ;
330+ tokio:: spawn (
331+ async move {
332+ let client_upgraded = match client_upgrade. await {
333+ Ok ( u) => u,
334+ Err ( e) => {
335+ warn ! (
336+ source_identity = %source_identity,
337+ source_peer_addr = %source_peer_addr,
338+ dest_authority = %authority,
339+ gateway_endpoint = %gateway_endpoint,
340+ error = %e,
341+ "client upgrade failed"
342+ ) ;
343+ return ;
344+ }
345+ } ;
346+
347+ let mut client_io = TokioIo :: new ( client_upgraded) ;
348+ let mut gw_io = TokioIo :: new ( gw_upgraded) ;
349+
350+ match copy_bidirectional ( & mut client_io, & mut gw_io) . await {
351+ Ok ( ( up, down) ) => {
352+ info ! (
353+ source_identity = %source_identity,
354+ source_peer_addr = %source_peer_addr,
355+ dest_authority = %authority,
356+ gateway_endpoint = %gateway_endpoint,
357+ bytes_client_to_dest = up,
358+ bytes_dest_to_client = down,
359+ "tunnel closed"
360+ ) ;
361+ }
362+ Err ( e) => {
363+ error ! (
364+ source_identity = %source_identity,
365+ source_peer_addr = %source_peer_addr,
366+ dest_authority = %authority,
367+ gateway_endpoint = %gateway_endpoint,
368+ error = %e,
369+ "tunnel error"
370+ ) ;
371+ }
372+ }
336373 }
337- }
338- } ) ;
374+ . instrument ( tunnel_span ) ,
375+ ) ;
339376
340- response ( StatusCode :: OK , "" )
377+ response ( StatusCode :: OK , "" )
378+ }
379+ . instrument ( span)
380+ . await
341381}
342382
343383fn response ( status : StatusCode , message : & str ) -> Response < Body > {
@@ -478,6 +518,38 @@ mod tests {
478518 assert_eq ! ( req. uri( ) . authority( ) . unwrap( ) . as_str( ) , "[::1]:8443" ) ;
479519 }
480520
521+ #[ test]
522+ fn connect_request_injects_trace_context_headers ( ) {
523+ use opentelemetry:: trace:: TracerProvider as _;
524+ use opentelemetry_sdk:: propagation:: TraceContextPropagator ;
525+ use opentelemetry_sdk:: trace:: SdkTracerProvider ;
526+ use tracing_subscriber:: layer:: SubscriberExt ;
527+
528+ opentelemetry:: global:: set_text_map_propagator ( TraceContextPropagator :: new ( ) ) ;
529+
530+ let provider = SdkTracerProvider :: builder ( ) . build ( ) ;
531+ let tracer = provider. tracer ( "test" ) ;
532+ let subscriber =
533+ tracing_subscriber:: registry ( ) . with ( tracing_opentelemetry:: layer ( ) . with_tracer ( tracer) ) ;
534+
535+ tracing:: subscriber:: with_default ( subscriber, || {
536+ let span = tracing:: info_span!( "sidecar test span" ) ;
537+ let _guard = span. enter ( ) ;
538+
539+ let req = build_connect_request ( "example.com:443" ) . unwrap ( ) ;
540+ let traceparent = req
541+ . headers ( )
542+ . get ( "traceparent" )
543+ . and_then ( |value| value. to_str ( ) . ok ( ) )
544+ . expect ( "traceparent should be injected" ) ;
545+
546+ assert ! ( traceparent. starts_with( "00-" ) ) ;
547+ assert_eq ! ( traceparent. len( ) , 55 ) ;
548+ } ) ;
549+
550+ let _ = provider. shutdown ( ) ;
551+ }
552+
481553 #[ test]
482554 fn response_ok_has_empty_body ( ) {
483555 let resp = response ( StatusCode :: OK , "" ) ;
0 commit comments