You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
genmarkov/src/bind.rs

172 lines
4.0 KiB

4 years ago
//! 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<E>
{
IO(io::Error),
Warp(warp::Error),
Other(E),
}
impl<E: error::Error + 'static> error::Error for BindError<E>
{
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(match &self {
Self::IO(io) => io,
Self::Other(o) => o,
Self::Warp(w) => w,
})
}
}
impl<E: fmt::Display> fmt::Display for BindError<E>
{
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<Self, Self::Err> {
Ok(if let Ok(ip) = s.parse::<SocketAddr>() {
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<Path>) -> io::Result<impl TryStream<Ok= impl AsyncRead + AsyncWrite + Send + Unpin + 'static + Send, Error = impl Into<Box<dyn std::error::Error + Send + Sync>>>>
{
debug!("Binding to AF_UNIX: {:?}", to.as_ref());
let listener = tokio::net::UnixListener::bind(to)?;
Ok(listener)
}
pub fn serve<F>(server: warp::Server<F>, bind: Bindpoint, signal: impl Future<Output=()> + Send + 'static) -> Result<(Bindpoint, BoxFuture<'static, ()>), BindError<std::convert::Infallible>>
where F: Filter + Clone + Send + Sync + 'static,
<F::Future as TryFuture>::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<SocketAddr> for Bindpoint
{
fn from(from: SocketAddr) -> Self
{
Self::TCP(from)
}
}
pub fn try_serve<F>(server: warp::Server<F>, bind: impl TryBindpoint, signal: impl Future<Output=()> + Send + 'static) -> Result<(Bindpoint, BoxFuture<'static, ()>), BindError<impl error::Error + 'static>>
where F: Filter + Clone + Send + Sync + 'static,
<F::Future as TryFuture>::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<Bindpoint, Self::Err>;
}
impl TryBindpoint for Bindpoint
{
type Err = std::convert::Infallible;
fn try_parse(self) -> Result<Bindpoint, Self::Err>
{
Ok(self)
}
}
impl<T: AsRef<str>> TryBindpoint for T
{
type Err = BindpointParseError;
fn try_parse(self) -> Result<Bindpoint, Self::Err>
{
self.as_ref().parse()
}
}
impl BindError<std::convert::Infallible>
{
pub fn coerce<T>(self) -> BindError<T>
{
match self {
Self::Warp(w) => BindError::Warp(w),
Self::IO(w) => BindError::IO(w),
#[cold] _ => unreachable!(),
}
}
}