Arg::new("bind")
.short('b')
.long("bind")
- .help("Specify bind address")
+ .help("Specify bind address or unix socket")
.multiple_values(true)
.value_delimiter(',')
.action(ArgAction::Append)
#[derive(Debug)]
pub struct Args {
- pub addrs: Vec<IpAddr>,
+ pub addrs: Vec<BindAddr>,
pub port: u16,
pub path: PathBuf,
pub path_is_file: bool,
.values_of("bind")
.map(|v| v.collect())
.unwrap_or_else(|| vec!["0.0.0.0", "::"]);
- let addrs: Vec<IpAddr> = Args::parse_addrs(&addrs)?;
+ let addrs: Vec<BindAddr> = Args::parse_addrs(&addrs)?;
let path = Args::parse_path(matches.value_of_os("root").unwrap_or_default())?;
let path_is_file = path.metadata()?.is_file();
let path_prefix = matches
})
}
- fn parse_addrs(addrs: &[&str]) -> BoxResult<Vec<IpAddr>> {
- let mut ip_addrs = vec![];
+ fn parse_addrs(addrs: &[&str]) -> BoxResult<Vec<BindAddr>> {
+ let mut bind_addrs = vec![];
let mut invalid_addrs = vec![];
for addr in addrs {
match addr.parse::<IpAddr>() {
Ok(v) => {
- ip_addrs.push(v);
+ bind_addrs.push(BindAddr::Address(v));
}
Err(_) => {
- invalid_addrs.push(*addr);
+ if cfg!(unix) {
+ bind_addrs.push(BindAddr::Path(PathBuf::from(addr)));
+ } else {
+ invalid_addrs.push(*addr);
+ }
}
}
}
if !invalid_addrs.is_empty() {
return Err(format!("Invalid bind address `{}`", invalid_addrs.join(",")).into());
}
- Ok(ip_addrs)
+ Ok(bind_addrs)
}
fn parse_path<P: AsRef<Path>>(path: P) -> BoxResult<PathBuf> {
Ok(path)
}
}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum BindAddr {
+ Address(IpAddr),
+ Path(PathBuf),
+}
mod streamer;
#[cfg(feature = "tls")]
mod tls;
+#[cfg(unix)]
+mod unix;
mod utils;
#[macro_use]
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
+use args::BindAddr;
use clap_complete::Shell;
use futures::future::join_all;
use tokio::net::TcpListener;
let inner = Arc::new(Server::new(args.clone(), running));
let mut handles = vec![];
let port = args.port;
- for ip in args.addrs.iter() {
+ for bind_addr in args.addrs.iter() {
let inner = inner.clone();
- let incoming = create_addr_incoming(SocketAddr::new(*ip, port))
- .map_err(|e| format!("Failed to bind `{}:{}`, {}", ip, port, e))?;
- let serve_func = move |remote_addr: SocketAddr| {
+ let serve_func = move |remote_addr: Option<SocketAddr>| {
let inner = inner.clone();
async move {
Ok::<_, hyper::Error>(service_fn(move |req: Request| {
}))
}
};
- match args.tls.as_ref() {
- #[cfg(feature = "tls")]
- Some((certs, key)) => {
- let config = ServerConfig::builder()
- .with_safe_defaults()
- .with_no_client_auth()
- .with_single_cert(certs.clone(), key.clone())?;
- let config = Arc::new(config);
- let accepter = TlsAcceptor::new(config.clone(), incoming);
- let new_service = make_service_fn(move |socket: &TlsStream| {
- let remote_addr = socket.remote_addr();
- serve_func(remote_addr)
- });
- let server = tokio::spawn(hyper::Server::builder(accepter).serve(new_service));
- handles.push(server);
+ match bind_addr {
+ BindAddr::Address(ip) => {
+ let incoming = create_addr_incoming(SocketAddr::new(*ip, port))
+ .map_err(|e| format!("Failed to bind `{}:{}`, {}", ip, port, e))?;
+ match args.tls.as_ref() {
+ #[cfg(feature = "tls")]
+ Some((certs, key)) => {
+ let config = ServerConfig::builder()
+ .with_safe_defaults()
+ .with_no_client_auth()
+ .with_single_cert(certs.clone(), key.clone())?;
+ let config = Arc::new(config);
+ let accepter = TlsAcceptor::new(config.clone(), incoming);
+ let new_service = make_service_fn(move |socket: &TlsStream| {
+ let remote_addr = socket.remote_addr();
+ serve_func(Some(remote_addr))
+ });
+ let server =
+ tokio::spawn(hyper::Server::builder(accepter).serve(new_service));
+ handles.push(server);
+ }
+ #[cfg(not(feature = "tls"))]
+ Some(_) => {
+ unreachable!()
+ }
+ None => {
+ let new_service = make_service_fn(move |socket: &AddrStream| {
+ let remote_addr = socket.remote_addr();
+ serve_func(Some(remote_addr))
+ });
+ let server =
+ tokio::spawn(hyper::Server::builder(incoming).serve(new_service));
+ handles.push(server);
+ }
+ };
}
- #[cfg(not(feature = "tls"))]
- Some(_) => {
- unreachable!()
- }
- None => {
- let new_service = make_service_fn(move |socket: &AddrStream| {
- let remote_addr = socket.remote_addr();
- serve_func(remote_addr)
- });
- let server = tokio::spawn(hyper::Server::builder(incoming).serve(new_service));
- handles.push(server);
+ BindAddr::Path(path) => {
+ if path.exists() {
+ std::fs::remove_file(path)?;
+ }
+ #[cfg(unix)]
+ {
+ let listener = tokio::net::UnixListener::bind(path)
+ .map_err(|e| format!("Failed to bind `{}`, {}", path.display(), e))?;
+ let acceptor = unix::UnixAcceptor::from_listener(listener);
+ let new_service = make_service_fn(move |_| serve_func(None));
+ let server = tokio::spawn(hyper::Server::builder(acceptor).serve(new_service));
+ handles.push(server);
+ }
}
- };
+ }
}
Ok(handles)
}
}
fn print_listening(args: Arc<Args>) -> BoxResult<()> {
- let mut addrs = vec![];
+ let mut bind_addrs = vec![];
let (mut ipv4, mut ipv6) = (false, false);
- for ip in args.addrs.iter() {
- if ip.is_unspecified() {
- if ip.is_ipv6() {
- ipv6 = true;
- } else {
- ipv4 = true;
+ for bind_addr in args.addrs.iter() {
+ match bind_addr {
+ BindAddr::Address(ip) => {
+ if ip.is_unspecified() {
+ if ip.is_ipv6() {
+ ipv6 = true;
+ } else {
+ ipv4 = true;
+ }
+ } else {
+ bind_addrs.push(bind_addr.clone());
+ }
}
- } else {
- addrs.push(*ip);
+ _ => bind_addrs.push(bind_addr.clone()),
}
}
if ipv4 || ipv6 {
for iface in ifaces.into_iter() {
let local_ip = iface.ip();
if ipv4 && local_ip.is_ipv4() {
- addrs.push(local_ip)
+ bind_addrs.push(BindAddr::Address(local_ip))
}
if ipv6 && local_ip.is_ipv6() {
- addrs.push(local_ip)
+ bind_addrs.push(BindAddr::Address(local_ip))
}
}
}
- addrs.sort_unstable();
- let urls = addrs
+ bind_addrs.sort_unstable();
+ let urls = bind_addrs
.into_iter()
- .map(|addr| match addr {
- IpAddr::V4(_) => format!("{}:{}", addr, args.port),
- IpAddr::V6(_) => format!("[{}]:{}", addr, args.port),
- })
- .map(|addr| match &args.tls {
- Some(_) => format!("https://{}", addr),
- None => format!("http://{}", addr),
+ .map(|bind_addr| match bind_addr {
+ BindAddr::Address(addr) => {
+ let addr = match addr {
+ IpAddr::V4(_) => format!("{}:{}", addr, args.port),
+ IpAddr::V6(_) => format!("[{}]:{}", addr, args.port),
+ };
+ let protocol = if args.tls.is_some() { "https" } else { "http" };
+ format!("{}://{}{}", protocol, addr, args.uri_prefix)
+ }
+ BindAddr::Path(path) => path.display().to_string(),
})
- .map(|url| format!("{}{}", url, args.uri_prefix))
.collect::<Vec<_>>();
if urls.len() == 1 {