33// SPDX-License-Identifier: AGPL-3.0-only 
44// Please see LICENSE in the repository root for full details. 
55
6- use  std:: { future:: Future ,  time:: Duration } ; 
6+ use  std:: { future:: Future ,  str :: FromStr ,  sync :: Arc ,   time:: Duration } ; 
77
8+ use  futures_util:: FutureExt  as  _; 
89use  headers:: { ContentLength ,  HeaderMapExt  as  _,  Host ,  UserAgent } ; 
9- use  hyper_util:: client:: legacy:: connect:: HttpInfo ; 
10+ use  hyper_util:: client:: legacy:: connect:: { 
11+     dns:: { GaiResolver ,  Name } , 
12+     HttpInfo , 
13+ } ; 
1014use  opentelemetry_http:: HeaderInjector ; 
1115use  opentelemetry_semantic_conventions:: { 
1216    attribute:: { HTTP_REQUEST_BODY_SIZE ,  HTTP_RESPONSE_BODY_SIZE } , 
@@ -16,21 +20,50 @@ use opentelemetry_semantic_conventions::{
1620        USER_AGENT_ORIGINAL , 
1721    } , 
1822} ; 
23+ use  tower:: { BoxError ,  Service  as  _} ; 
1924use  tracing:: Instrument ; 
2025use  tracing_opentelemetry:: OpenTelemetrySpanExt ; 
2126
2227static  USER_AGENT :  & str  = concat ! ( "matrix-authentication-service/" ,  env!( "CARGO_PKG_VERSION" ) ) ; 
2328
29+ struct  TracingResolver  { 
30+     inner :  GaiResolver , 
31+ } 
32+ 
33+ impl  TracingResolver  { 
34+     fn  new ( )  -> Self  { 
35+         let  inner = GaiResolver :: new ( ) ; 
36+         Self  {  inner } 
37+     } 
38+ } 
39+ 
40+ impl  reqwest:: dns:: Resolve  for  TracingResolver  { 
41+     fn  resolve ( & self ,  name :  reqwest:: dns:: Name )  -> reqwest:: dns:: Resolving  { 
42+         let  span = tracing:: info_span!( "dns.resolve" ,  name = name. as_str( ) ) ; 
43+         let  inner = & mut  self . inner . clone ( ) ; 
44+         Box :: pin ( 
45+             inner
46+                 . call ( Name :: from_str ( name. as_str ( ) ) . unwrap ( ) ) 
47+                 . map ( |result| { 
48+                     result
49+                         . map ( |addrs| -> reqwest:: dns:: Addrs  {  Box :: new ( addrs)  } ) 
50+                         . map_err ( |err| -> BoxError  {  Box :: new ( err)  } ) 
51+                 } ) 
52+                 . instrument ( span) , 
53+         ) 
54+     } 
55+ } 
56+ 
2457/// Create a new [`reqwest::Client`] with sane parameters 
2558/// 
2659/// # Panics 
2760/// 
2861/// Panics if the client fails to build, which should never happen 
2962#[ must_use]  
3063pub  fn  client ( )  -> reqwest:: Client  { 
31-     // TODO: make the resolver tracing again 
3264    // TODO: can/should we limit in-flight requests? 
3365    reqwest:: Client :: builder ( ) 
66+         . dns_resolver ( Arc :: new ( TracingResolver :: new ( ) ) ) 
3467        . use_preconfigured_tls ( rustls_platform_verifier:: tls_config ( ) ) 
3568        . user_agent ( USER_AGENT ) 
3669        . timeout ( Duration :: from_secs ( 60 ) ) 
@@ -74,7 +107,6 @@ async fn send_traced(
74107        {  USER_AGENT_ORIGINAL  }  = user_agent, 
75108        "rust.error"  = tracing:: field:: Empty , 
76109    ) ; 
77-     let  _guard = span. enter ( ) ; 
78110
79111    // Inject the span context into the request headers 
80112    let  context = span. context ( ) ; 
@@ -83,37 +115,42 @@ async fn send_traced(
83115        propagator. inject_context ( & context,  & mut  injector) ; 
84116    } ) ; 
85117
86-     match  client. execute ( request) . in_current_span ( ) . await  { 
87-         Ok ( response)  => { 
88-             span. record ( "otel.status_code" ,  "OK" ) ; 
89-             span. record ( HTTP_RESPONSE_STATUS_CODE ,  response. status ( ) . as_u16 ( ) ) ; 
118+     async  move  { 
119+         let  span = tracing:: Span :: current ( ) ; 
120+         match  client. execute ( request) . await  { 
121+             Ok ( response)  => { 
122+                 span. record ( "otel.status_code" ,  "OK" ) ; 
123+                 span. record ( HTTP_RESPONSE_STATUS_CODE ,  response. status ( ) . as_u16 ( ) ) ; 
90124
91-             if  let  Some ( ContentLength ( content_length) )  = response. headers ( ) . typed_get ( )  { 
92-                 span. record ( HTTP_RESPONSE_BODY_SIZE ,  content_length) ; 
93-             } 
125+                  if  let  Some ( ContentLength ( content_length) )  = response. headers ( ) . typed_get ( )  { 
126+                      span. record ( HTTP_RESPONSE_BODY_SIZE ,  content_length) ; 
127+                  } 
94128
95-             if  let  Some ( http_info)  = response. extensions ( ) . get :: < HttpInfo > ( )  { 
96-                 let  local = http_info. local_addr ( ) ; 
97-                 let  remote = http_info. remote_addr ( ) ; 
98- 
99-                 let  family = if  local. is_ipv4 ( )  {  "ipv4"  }  else  {  "ipv6"  } ; 
100-                 span. record ( NETWORK_TYPE ,  family) ; 
101-                 span. record ( CLIENT_ADDRESS ,  remote. ip ( ) . to_string ( ) ) ; 
102-                 span. record ( CLIENT_PORT ,  remote. port ( ) ) ; 
103-                 span. record ( SERVER_ADDRESS ,  local. ip ( ) . to_string ( ) ) ; 
104-                 span. record ( SERVER_PORT ,  local. port ( ) ) ; 
105-             }  else  { 
106-                 tracing:: warn!( "No HttpInfo injected in response extensions" ) ; 
107-             } 
129+                 if  let  Some ( http_info)  = response. extensions ( ) . get :: < HttpInfo > ( )  { 
130+                     let  local = http_info. local_addr ( ) ; 
131+                     let  remote = http_info. remote_addr ( ) ; 
108132
109-             Ok ( response) 
110-         } 
111-         Err ( err)  => { 
112-             span. record ( "otel.status_code" ,  "ERROR" ) ; 
113-             span. record ( "rust.error" ,  & err as  & dyn  std:: error:: Error ) ; 
114-             Err ( err) 
133+                     let  family = if  local. is_ipv4 ( )  {  "ipv4"  }  else  {  "ipv6"  } ; 
134+                     span. record ( NETWORK_TYPE ,  family) ; 
135+                     span. record ( CLIENT_ADDRESS ,  remote. ip ( ) . to_string ( ) ) ; 
136+                     span. record ( CLIENT_PORT ,  remote. port ( ) ) ; 
137+                     span. record ( SERVER_ADDRESS ,  local. ip ( ) . to_string ( ) ) ; 
138+                     span. record ( SERVER_PORT ,  local. port ( ) ) ; 
139+                 }  else  { 
140+                     tracing:: warn!( "No HttpInfo injected in response extensions" ) ; 
141+                 } 
142+ 
143+                 Ok ( response) 
144+             } 
145+             Err ( err)  => { 
146+                 span. record ( "otel.status_code" ,  "ERROR" ) ; 
147+                 span. record ( "rust.error" ,  & err as  & dyn  std:: error:: Error ) ; 
148+                 Err ( err) 
149+             } 
115150        } 
116151    } 
152+     . instrument ( span) 
153+     . await 
117154} 
118155
119156/// An extension trait implemented for [`reqwest::RequestBuilder`] to send a 
0 commit comments