use axum::{ Router, extract::connect_info, }; use futures::ready; use hyper::{ client::connect::{Connected, Connection}, server::accept::Accept, }; use snafu::prelude::*; use std::{ io, path::Path, pin::Pin, sync::Arc, task::{Context, Poll}, }; use tokio::{ io::{AsyncRead, AsyncWrite}, net::{unix::UCred, UnixListener, UnixStream}, }; use tower::BoxError; use crate::{ error::*, utils::fs::FSPermissions, }; pub struct ServerAccept { uds: UnixListener, } impl ServerAccept { pub fn new(uds: UnixListener) -> ServerAccept { ServerAccept { uds, } } } impl Accept for ServerAccept { type Conn = UnixStream; type Error = BoxError; fn poll_accept( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll>> { let (stream, _addr) = ready!(self.uds.poll_accept(cx))?; Poll::Ready(Some(Ok(stream))) } } pub struct ClientConnection { stream: UnixStream, } impl AsyncWrite for ClientConnection { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { Pin::new(&mut self.stream).poll_write(cx, buf) } fn poll_flush( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut self.stream).poll_flush(cx) } fn poll_shutdown( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll> { Pin::new(&mut self.stream).poll_shutdown(cx) } } impl AsyncRead for ClientConnection { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut tokio::io::ReadBuf<'_>, ) -> Poll> { Pin::new(&mut self.stream).poll_read(cx, buf) } } impl Connection for ClientConnection { fn connected(&self) -> Connected { Connected::new() } } #[derive(Clone, Debug)] #[allow(dead_code)] pub struct UdsConnectInfo { peer_addr: Arc, peer_cred: UCred, } impl connect_info::Connected<&UnixStream> for UdsConnectInfo { fn connect_info(target: &UnixStream) -> Self { let peer_addr = target.peer_addr().unwrap(); let peer_cred = target.peer_cred().unwrap(); Self { peer_addr: Arc::new(peer_addr), peer_cred, } } } /// Serve a webapp on UNIX socket path pub async fn serve(path: &Path, app: Router) -> Result<(), Error> { let _ = tokio::fs::remove_file(&path).await; tokio::fs::create_dir_all(path.parent().unwrap()) .await .unwrap(); // TODO: make proper permissions // Apply 777 permissions FSPermissions::new().chmod(0o777).apply_to(&path).await?; let uds = UnixListener::bind(path.clone()) .context(SocketCreateSnafu { path: path.clone() })?; hyper::Server::builder(ServerAccept::new(uds)) .serve(app.into_make_service_with_connect_info::()) .await.context(ServerSnafu)?; Ok(()) }