//! For binding to sockets use super::*; use futures::{ prelude::*, }; use std::{ marker::{ Send, Unpin, }, fmt, error, path::{ Path, PathBuf, }, }; use tokio::{ io::{ self, AsyncRead, AsyncWrite, }, }; #[derive(Debug)] pub enum BindError { IO(io::Error), Warp(warp::Error), Other(E), } impl error::Error for BindError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(match &self { Self::IO(io) => io, Self::Other(o) => o, Self::Warp(w) => w, }) } } impl fmt::Display for BindError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::IO(io) => write!(f, "io error: {}", io), Self::Other(other) => write!(f, "{}", other), Self::Warp(warp) => write!(f, "server error: {}", warp), } } } #[derive(Debug)] pub struct BindpointParseError; impl error::Error for BindpointParseError{} impl fmt::Display for BindpointParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Failed to parse bindpoint as IP or unix domain socket") } } #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd)] pub enum Bindpoint { Unix(PathBuf), TCP(SocketAddr), } impl fmt::Display for Bindpoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Unix(unix) => write!(f, "unix:/{}", unix.to_string_lossy()), Self::TCP(tcp) => write!(f, "{}", tcp), } } } impl std::str::FromStr for Bindpoint { type Err = BindpointParseError; fn from_str(s: &str) -> Result { Ok(if let Ok(ip) = s.parse::() { Self::TCP(ip) } else if s.starts_with("unix:/") { Self::Unix(PathBuf::from(&s[6..].to_owned())) } else { return Err(BindpointParseError); }) } } fn bind_unix(to: impl AsRef) -> io::Result>>> { debug!("Binding to AF_UNIX: {:?}", to.as_ref()); let listener = tokio::net::UnixListener::bind(to)?; Ok(listener) } pub fn serve(server: warp::Server, bind: Bindpoint, signal: impl Future + Send + 'static) -> Result<(Bindpoint, BoxFuture<'static, ()>), BindError> where F: Filter + Clone + Send + Sync + 'static, ::Ok: warp::Reply, { Ok(match bind { Bindpoint::TCP(sock) => server.try_bind_with_graceful_shutdown(sock, signal).map(|(sock, fut)| (Bindpoint::TCP(sock), fut.boxed())).map_err(BindError::Warp)?, Bindpoint::Unix(unix) => { (Bindpoint::Unix(unix.clone()), server.serve_incoming_with_graceful_shutdown(bind_unix(unix).map_err(BindError::IO)?, signal).boxed()) }, }) } impl From for Bindpoint { fn from(from: SocketAddr) -> Self { Self::TCP(from) } } pub fn try_serve(server: warp::Server, bind: impl TryBindpoint, signal: impl Future + Send + 'static) -> Result<(Bindpoint, BoxFuture<'static, ()>), BindError> where F: Filter + Clone + Send + Sync + 'static, ::Ok: warp::Reply, { serve(server, bind.try_parse().map_err(BindError::Other)?, signal).map_err(BindError::coerce) } pub trait TryBindpoint: Sized { type Err: error::Error + 'static; fn try_parse(self) -> Result; } impl TryBindpoint for Bindpoint { type Err = std::convert::Infallible; fn try_parse(self) -> Result { Ok(self) } } impl> TryBindpoint for T { type Err = BindpointParseError; fn try_parse(self) -> Result { self.as_ref().parse() } } impl BindError { pub fn coerce(self) -> BindError { match self { Self::Warp(w) => BindError::Warp(w), Self::IO(w) => BindError::IO(w), #[cold] _ => unreachable!(), } } }