@@ -12,6 +12,7 @@ use prosa_utils::config::ssl::SslConfig;
1212#[ cfg( feature = "openssl" ) ]
1313use prosa_utils:: config:: ssl:: SslConfigContext ;
1414
15+ use base64:: { Engine as _, engine:: general_purpose:: STANDARD } ;
1516use serde:: { Deserialize , Serialize } ;
1617use tokio:: {
1718 io:: { AsyncRead , AsyncWrite , ReadBuf } ,
@@ -41,7 +42,7 @@ pub enum Stream {
4142}
4243
4344impl Stream {
44- /// Returns the local address that this stream is bound to .
45+ /// Returns the socket address of the remote peer of this TCP connection .
4546 ///
4647 /// ```
4748 /// use tokio::io;
@@ -51,10 +52,41 @@ impl Stream {
5152 /// use std::net::{Ipv4Addr, SocketAddrV4};
5253 ///
5354 /// async fn accepting() -> Result<(), io::Error> {
54- /// let stream: Stream = Stream::connect_tcp("127.0.0.1:80").await?;
55+ /// let stream: Stream = Stream::connect_tcp("127.0.0.1:8080").await?;
56+ ///
57+ /// assert_eq!(stream.peer_addr()?,
58+ /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 8080)));
59+ ///
60+ /// Ok(())
61+ /// }
62+ /// ```
63+ pub fn peer_addr ( & self ) -> Result < SocketAddr , io:: Error > {
64+ match self {
65+ #[ cfg( target_family = "unix" ) ]
66+ Stream :: Unix ( s) => s. peer_addr ( ) . map ( |addr| addr. into ( ) ) ,
67+ Stream :: Tcp ( s) => s. peer_addr ( ) . map ( |addr| addr. into ( ) ) ,
68+ #[ cfg( feature = "openssl" ) ]
69+ Stream :: OpenSsl ( s) => s. get_ref ( ) . peer_addr ( ) . map ( |addr| addr. into ( ) ) ,
70+ #[ cfg( feature = "http-proxy" ) ]
71+ Stream :: TcpHttpProxy ( s) => s. peer_addr ( ) . map ( |addr| addr. into ( ) ) ,
72+ #[ cfg( all( feature = "openssl" , feature = "http-proxy" ) ) ]
73+ Stream :: OpenSslHttpProxy ( s) => s. get_ref ( ) . peer_addr ( ) . map ( |addr| addr. into ( ) ) ,
74+ }
75+ }
76+
77+ /// Returns the local address that this stream is bound to.
78+ ///
79+ /// ```
80+ /// use tokio::io;
81+ /// use url::Url;
82+ /// use prosa::io::stream::Stream;
83+ /// use prosa::io::SocketAddr;
84+ /// use std::net::{IpAddr, Ipv4Addr, SocketAddrV4};
85+ ///
86+ /// async fn accepting() -> Result<(), io::Error> {
87+ /// let stream: Stream = Stream::connect_tcp("127.0.0.1:8080").await?;
5588 ///
56- /// assert_eq!(stream.local_addr()?,
57- /// SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 80)));
89+ /// assert_eq!(stream.local_addr()?.ip(), IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
5890 ///
5991 /// Ok(())
6092 /// }
@@ -752,6 +784,36 @@ impl TargetSetting {
752784 self . ssl . is_some ( ) || url_is_ssl ( & self . url )
753785 }
754786
787+ /// Method to get authentication value out of URL username/password
788+ ///
789+ /// - If user password is provided, it return *Basic* authentication with base64 encoded username:password
790+ /// - If only password is provided, it return *Bearer* authentication with the password as token
791+ ///
792+ /// ```
793+ /// use url::Url;
794+ /// use prosa::io::stream::TargetSetting;
795+ ///
796+ /// let basic_auth_target = TargetSetting::from(Url::parse("http://user:pass@localhost:8080").unwrap());
797+ /// assert_eq!(Some(String::from("Basic dXNlcjpwYXNz")), basic_auth_target.get_authentication());
798+ ///
799+ /// let bearer_auth_target = TargetSetting::from(Url::parse("http://:token@localhost:8080").unwrap());
800+ /// assert_eq!(Some(String::from("Bearer token")), bearer_auth_target.get_authentication());
801+ /// ```
802+ pub fn get_authentication ( & self ) -> Option < String > {
803+ if let Some ( password) = self . url . password ( ) {
804+ if self . url . username ( ) . is_empty ( ) {
805+ Some ( format ! ( "Bearer {password}" ) )
806+ } else {
807+ Some ( format ! (
808+ "Basic {}" ,
809+ STANDARD . encode( format!( "{}:{}" , self . url. username( ) , password) )
810+ ) )
811+ }
812+ } else {
813+ None
814+ }
815+ }
816+
755817 /// Method to init the ssl context out of the ssl target configuration.
756818 /// Must be call when the configuration is retrieved
757819 pub fn init_ssl_context ( & mut self ) {
@@ -876,6 +938,14 @@ impl fmt::Display for TargetSetting {
876938 }
877939 }
878940
941+ // Mask username and password to avoid data leak in logs
942+ if !url. username ( ) . is_empty ( ) {
943+ let _ = url. set_username ( "***" ) ;
944+ }
945+ if url. password ( ) . is_some ( ) {
946+ let _ = url. set_password ( Some ( "***" ) ) ;
947+ }
948+
879949 if let Some ( proxy_url) = & self . proxy {
880950 write ! ( f, "{url} -proxy {proxy_url}" )
881951 } else {
0 commit comments