2024-01-16 02:44:48 +00:00
//! A UNIX socket server to run a Tower Service.
2023-08-25 09:28:47 +00:00
use keyfork_frame ::asyncext ::{ try_decode_from , try_encode_to } ;
use std ::{
io ::Error ,
path ::{ Path , PathBuf } ,
} ;
2023-09-07 13:05:38 +00:00
use tokio ::net ::UnixListener ;
2023-08-25 06:32:21 +00:00
use tower ::{ Service , ServiceExt } ;
#[ cfg(feature = " tracing " ) ]
use tracing ::debug ;
2024-01-16 02:44:48 +00:00
/// A UNIX Socket Server.
2023-08-25 06:32:21 +00:00
#[ allow(clippy::module_name_repetitions) ]
pub struct UnixServer {
listener : UnixListener ,
}
impl UnixServer {
2024-01-16 02:44:48 +00:00
/// Bind a socket to the given `address` and create a [`UnixServer`]. This function also creates a ctrl_c handler to automatically clean up the socket file.
///
/// # Errors
/// This function may return an error if the socket can't be bound.
2023-08-25 06:32:21 +00:00
pub fn bind ( address : impl AsRef < Path > ) -> Result < Self , Error > {
let mut path = PathBuf ::new ( ) ;
path . extend ( address . as_ref ( ) . components ( ) ) ;
tokio ::spawn ( async move {
2023-09-01 04:10:56 +00:00
#[ cfg(feature = " tracing " ) ]
debug! ( " Binding tokio ctrl-c handler " ) ;
2023-08-25 06:32:21 +00:00
let result = tokio ::signal ::ctrl_c ( ) . await ;
#[ cfg(feature = " tracing " ) ]
2023-08-25 09:28:47 +00:00
debug! (
? result ,
" encountered ctrl-c, performing cleanup and exiting "
) ;
2023-08-25 06:32:21 +00:00
let result = tokio ::fs ::remove_file ( & path ) . await ;
#[ cfg(feature = " tracing " ) ]
if let Err ( error ) = result {
debug! ( % error , " unable to remove path: {} " , path . display ( ) ) ;
}
std ::process ::exit ( 0x80 ) ;
} ) ;
Ok ( Self {
listener : UnixListener ::bind ( address ) ? ,
} )
}
2024-01-16 02:44:48 +00:00
/// Given a Service, accept clients and use their input to call the Service.
///
/// # Errors
/// The method may return an error if the server becomes unable to accept new connections.
/// Errors while the server is running are logged using the `tracing` crate.
2023-09-07 13:05:38 +00:00
pub async fn run < S , R > ( & mut self , app : S ) -> Result < ( ) , Box < dyn std ::error ::Error > >
where
S : Service < R > + Clone + Send + 'static ,
R : From < Vec < u8 > > + Send ,
< S as Service < R > > ::Error : std ::error ::Error + Send ,
< S as Service < R > > ::Response : std ::convert ::Into < Vec < u8 > > + Send ,
< S as Service < R > > ::Future : Send ,
{
2023-09-01 04:10:56 +00:00
#[ cfg(feature = " tracing " ) ]
debug! ( " Listening for clients " ) ;
2023-08-25 06:32:21 +00:00
loop {
let mut app = app . clone ( ) ;
let ( mut socket , _ ) = self . listener . accept ( ) . await ? ;
#[ cfg(feature = " tracing " ) ]
debug! ( " new socket connected " ) ;
tokio ::spawn ( async move {
2023-09-07 13:05:38 +00:00
let bytes = match try_decode_from ( & mut socket ) . await {
Ok ( bytes ) = > bytes ,
2023-08-25 06:32:21 +00:00
Err ( e ) = > {
#[ cfg(feature = " tracing " ) ]
2023-09-01 04:10:56 +00:00
debug! ( % e , " Error reading DerivationPath from socket " ) ;
2023-08-25 06:32:21 +00:00
let content = e . to_string ( ) . bytes ( ) . collect ::< Vec < _ > > ( ) ;
let result = try_encode_to ( & content [ .. ] , & mut socket ) . await ;
#[ cfg(feature = " tracing " ) ]
if let Err ( error ) = result {
debug! ( % error , " Error sending error to client " ) ;
}
return ;
}
} ;
2023-09-07 13:05:38 +00:00
let app = match app . ready ( ) . await {
Ok ( app ) = > app ,
Err ( e ) = > {
#[ cfg(feature = " tracing " ) ]
debug! ( % e , " Could not poll ready " ) ;
let content = e . to_string ( ) . bytes ( ) . collect ::< Vec < _ > > ( ) ;
let result = try_encode_to ( & content [ .. ] , & mut socket ) . await ;
#[ cfg(feature = " tracing " ) ]
if let Err ( error ) = result {
debug! ( % error , " Error sending error to client " ) ;
}
return ;
}
} ;
let response = match app . call ( bytes . into ( ) ) . await {
2023-08-25 06:32:21 +00:00
Ok ( response ) = > response ,
Err ( e ) = > {
#[ cfg(feature = " tracing " ) ]
2023-09-01 04:10:56 +00:00
debug! ( % e , " Error reading DerivationPath from socket " ) ;
2023-08-25 06:32:21 +00:00
let content = e . to_string ( ) . bytes ( ) . collect ::< Vec < _ > > ( ) ;
let result = try_encode_to ( & content [ .. ] , & mut socket ) . await ;
#[ cfg(feature = " tracing " ) ]
if let Err ( error ) = result {
debug! ( % error , " Error sending error to client " ) ;
}
return ;
}
2023-09-07 13:05:38 +00:00
}
. into ( ) ;
2023-08-25 06:32:21 +00:00
if let Err ( e ) = try_encode_to ( & response [ .. ] , & mut socket ) . await {
#[ cfg(feature = " tracing " ) ]
debug! ( % e , " Error sending response to client " ) ;
}
} ) ;
}
}
}