@@ -18,12 +18,15 @@ use crate::observability::http_tracing::sanitize_url;
1818use crate :: options:: InstrumentationClientInfo ;
1919#[ cfg( feature = "_internal-http-client" ) ]
2020use google_cloud_gax:: error:: Error ;
21+ use http:: Uri ;
2122use reqwest:: Method ;
2223use std:: future:: Future ;
2324use std:: net:: SocketAddr ;
2425use std:: sync:: { Arc , Mutex } ;
2526use tokio:: time:: Instant ;
2627
28+ const HTTPS_PORT : u16 = 443 ;
29+
2730tokio:: task_local! {
2831 static RECORDER : RequestRecorder ;
2932}
@@ -300,22 +303,40 @@ impl ClientSnapshot {
300303 ///
301304 /// If no address is known, use the target address from `info.default_host`.
302305 pub fn server_address ( & self ) -> String {
303- self . transport_snapshot
306+ if let Some ( address) = self
307+ . transport_snapshot
304308 . as_ref ( )
305309 . and_then ( |s| s. server_address )
306310 . map ( |a| a. ip ( ) . to_string ( ) )
307- . unwrap_or_else ( || self . info . default_host . to_string ( ) )
311+ {
312+ return address;
313+ }
314+ if let Some ( uri) = self . sanitized_url ( ) . and_then ( |u| u. parse :: < Uri > ( ) . ok ( ) ) {
315+ if let Some ( host) = uri. authority ( ) . map ( |a| a. host ( ) . to_string ( ) ) {
316+ return host;
317+ }
318+ }
319+ self . info . default_host . to_string ( )
308320 }
309321
310322 /// Returns the server port used in the last low-level request.
311323 ///
312324 /// If no port is known, use the port implied by `info.default_host`.
313325 pub fn server_port ( & self ) -> u16 {
314- self . transport_snapshot
326+ if let Some ( port) = self
327+ . transport_snapshot
315328 . as_ref ( )
316329 . and_then ( |s| s. server_address )
317330 . map ( |a| a. port ( ) )
318- . unwrap_or ( 443 )
331+ {
332+ return port;
333+ }
334+ if let Some ( uri) = self . sanitized_url ( ) . and_then ( |u| u. parse :: < Uri > ( ) . ok ( ) ) {
335+ if let Some ( host) = uri. authority ( ) . and_then ( |a| a. port_u16 ( ) ) {
336+ return host;
337+ }
338+ }
339+ HTTPS_PORT
319340 }
320341
321342 /// Returns the URL template (e.g. "/v1/storage/b/{bucket}") used in the last low-level request.
@@ -397,6 +418,8 @@ mod tests {
397418 use httptest:: matchers:: request:: method_path;
398419 use httptest:: responders:: status_code;
399420 use httptest:: { Expectation , Server } ;
421+ use pretty_assertions:: assert_eq;
422+ use std:: net:: { Ipv4Addr , SocketAddrV4 } ;
400423
401424 const TEST_METHOD_NAME : & str = "google.test.v1.Service/SomeMethod" ;
402425 const TEST_PATH_TEMPLATE : & str = "/v42/{parent}" ;
@@ -490,11 +513,33 @@ mod tests {
490513
491514 assert_eq ! ( snap. attempt_count, 1 , "{snap:?}" ) ;
492515 assert ! ( snap. http_status_code( ) . is_none( ) , "{snap:?}" ) ;
493- assert_eq ! (
494- snap. server_address( ) . as_str( ) ,
495- TEST_INFO . default_host,
496- "{snap:?}"
497- ) ;
516+ assert_eq ! ( snap. server_address( ) . as_str( ) , "127.0.0.1" , "{snap:?}" ) ;
517+ assert_eq ! ( snap. server_port( ) , 1 , "{snap:?}" ) ;
518+ }
519+
520+ #[ tokio:: test( start_paused = true ) ]
521+ async fn http_bad_url ( ) {
522+ const BAD_URL : & str = "bad-url" ;
523+
524+ let recorder = RequestRecorder :: new ( TEST_INFO ) ;
525+ let scoped = recorder. clone ( ) ;
526+ // Normally this code would be in the `tracing.rs` layer. Inline it here so we can examine the
527+ // effects on the `RequestRecorder`.
528+ let got = scoped
529+ . scope ( simulate_http_client_transport_layer ( BAD_URL ) )
530+ . await ;
531+ assert ! ( matches!( got, Err ( ref e) if e. is_io( ) ) , "{got:?}" ) ;
532+ let snap = recorder. client_snapshot ( ) ;
533+
534+ assert_eq ! ( snap. start, Instant :: now( ) , "{snap:?}" ) ;
535+ assert_eq ! ( snap. rpc_method( ) , Some ( TEST_METHOD_NAME ) , "{snap:?}" ) ;
536+ assert_eq ! ( snap. url_template( ) , Some ( TEST_PATH_TEMPLATE ) , "{snap:?}" ) ;
537+ assert ! ( snap. rpc_system( ) . is_none( ) , "{snap:?}" ) ;
538+ assert ! ( snap. sanitized_url( ) . is_none( ) , "{snap:?}" ) ;
539+
540+ assert_eq ! ( snap. attempt_count, 1 , "{snap:?}" ) ;
541+ assert ! ( snap. http_status_code( ) . is_none( ) , "{snap:?}" ) ;
542+ assert_eq ! ( snap. server_address( ) . as_str( ) , "example.com" , "{snap:?}" ) ;
498543 assert_eq ! ( snap. server_port( ) , 443 , "{snap:?}" ) ;
499544 }
500545
@@ -561,4 +606,35 @@ mod tests {
561606 assert_eq ! ( snap. sanitized_url( ) , Some ( WANT_URL ) , "{snap:?}" ) ;
562607 Ok ( ( ) )
563608 }
609+
610+ #[ test]
611+ fn address_sources ( ) -> anyhow:: Result < ( ) > {
612+ const RAW_URL : & str = "https://127.0.0.1:1/v42/unused" ;
613+
614+ let recorder = RequestRecorder :: new ( TEST_INFO ) ;
615+ let snap = recorder. client_snapshot ( ) ;
616+ assert_eq ! ( snap. server_address( ) , TEST_INFO . default_host, "{snap:?}" ) ;
617+ assert_eq ! ( snap. server_port( ) , HTTPS_PORT , "{snap:?}" ) ;
618+
619+ let url = reqwest:: Url :: parse ( RAW_URL ) ?;
620+ let request = reqwest:: Request :: new ( reqwest:: Method :: GET , url) ;
621+ recorder. on_http_request ( & request) ;
622+ let snap = recorder. client_snapshot ( ) ;
623+ assert_eq ! ( snap. server_address( ) , "127.0.0.1" , "{snap:?}" ) ;
624+ assert_eq ! ( snap. server_port( ) , 1 , "{snap:?}" ) ;
625+
626+ {
627+ let mut guard = recorder. inner . lock ( ) . expect ( "never poisoned" ) ;
628+ let s = guard. transport_snapshot . as_mut ( ) . expect ( "already set" ) ;
629+ s. server_address = Some ( SocketAddr :: V4 ( SocketAddrV4 :: new (
630+ Ipv4Addr :: new ( 127 , 0 , 0 , 234 ) ,
631+ 234 ,
632+ ) ) ) ;
633+ }
634+ let snap = recorder. client_snapshot ( ) ;
635+ assert_eq ! ( snap. server_address( ) , "127.0.0.234" , "{snap:?}" ) ;
636+ assert_eq ! ( snap. server_port( ) , 234 , "{snap:?}" ) ;
637+
638+ Ok ( ( ) )
639+ }
564640}
0 commit comments