use {
flowcontrol::superif,
http::{
Response,
StatusCode,
Uri,
header::HOST,
uri::{
Authority,
Scheme,
},
},
hyper::{
body::{
Buf,
Incoming,
},
service::service_fn,
},
hyper_util::rt::{
TokioExecutor,
TokioIo,
},
loga::{
Log,
ResultContext,
},
rustls::{
ServerConfig,
server::ResolvesServerCert,
},
std::{
collections::BTreeMap,
error::Error,
net::SocketAddr,
sync::Arc,
},
tokio::{
io::{
AsyncRead,
AsyncWrite,
},
net::TcpStream,
},
tokio_rustls::TlsAcceptor,
};
pub struct HandlerArgs<'a> {
pub peer_addr: SocketAddr,
pub subpath: &'a str,
pub query: &'a str,
pub url: Uri,
pub head: &'a http::request::Parts,
pub body: Incoming,
}
#[async_trait::async_trait]
pub trait Handler<O>: 'static + Send + Sync {
async fn handle(&self, args: HandlerArgs<'_>) -> Response<O>;
}
pub use async_trait;
#[macro_export]
macro_rules! handler{
(($($i: ident: $it: ty), *)($r: ident -> $o: ty) $b: expr) => {
{
struct Impl {
$($i: $it,) *
};
#[
$crate:: htserve:: handler:: async_trait:: async_trait
] impl $crate:: htserve:: handler:: Handler < $o > for Impl {
async fn handle(
&self,
$r: $crate:: htserve:: handler:: HandlerArgs < '_ >
) -> http:: response:: Response < $o > {
$(let $i =& self.$i;) *
$b
}
}
Impl {
$($i: $i.clone()),
*
}
}
};
}
fn check_path_router_key(k: &str) -> Result<(), String> {
if k == "" || (k.starts_with("/") && !k.ends_with("/")) {
return Ok(());
} else {
return Err(
format!(
"Router path [{}] doesn't match expected format: it must be an empty string, or else start with / and end with no /",
k
),
);
}
}
pub struct PathRouter<O>(BTreeMap<String, Box<dyn Handler<O>>>);
impl<O> Default for PathRouter<O> {
fn default() -> Self {
return Self(Default::default());
}
}
impl<O> PathRouter<O> {
pub fn new(routes: BTreeMap<String, Box<dyn Handler<O>>>) -> Result<Self, Vec<String>> {
let mut errors = vec![];
for key in routes.keys() {
if let Err(e) = check_path_router_key(&key) {
errors.push(e);
}
}
if !errors.is_empty() {
return Err(errors);
}
return Ok(Self(routes));
}
pub fn insert(&mut self, key: impl AsRef<str>, handler: Box<dyn Handler<O>>) -> Result<(), String> {
let key = key.as_ref().to_string();
if let Err(e) = check_path_router_key(&key) {
return Err(e)
}
self.0.insert(key, handler);
return Ok(());
}
}
#[async_trait::async_trait]
impl<O: 'static + Send + Default> Handler<O> for PathRouter<O> {
async fn handle(&self, args: HandlerArgs<'_>) -> Response<O> {
let Some((prefix, subhandler)) = self.0.range(..=args.subpath.to_string()).rev().next() else {
return Response::builder().status(StatusCode::NOT_FOUND).body(Default::default()).unwrap();
};
let Some(subpath) = args.subpath.strip_prefix(prefix) else {
return Response::builder().status(StatusCode::NOT_FOUND).body(Default::default()).unwrap();
};
return subhandler.handle(HandlerArgs {
peer_addr: args.peer_addr,
subpath: subpath,
query: args.query,
url: args.url,
head: args.head,
body: args.body,
}).await;
}
}
pub fn root_handle_http_inner<
I: 'static + Send + AsyncRead + AsyncWrite + Unpin,
OD: 'static + Send + Buf,
OE: 'static + Send + Sync + std::error::Error,
O: 'static + Send + http_body::Body<Data = OD, Error = OE>,
>(log: &Log, https: bool, peer_addr: SocketAddr, stream: I, handler: Arc<dyn Handler<O>>) {
let log = log.clone();
tokio::task::spawn(async move {
match async {
match hyper_util::server::conn::auto::Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(stream), service_fn({
move |req| {
let handler = handler.clone();
async move {
let (head, body) = req.into_parts();
let url;
let mut parts = head.uri.clone().into_parts();
if parts.scheme.is_none() {
parts.scheme = Some(if https {
Scheme::HTTPS
} else {
Scheme::HTTP
});
}
let mut authority: Vec<u8> = vec![];
if let Some(host1) = head.headers.get(HOST) {
authority.extend(host1.as_bytes());
} else if let Some(a) = &parts.authority {
authority.extend(a.host().as_bytes());
}
if let Some(a) = parts.authority {
if let Some(port) = a.port_u16() {
authority.extend(port.to_string().as_bytes());
}
}
parts.authority =
Some(
Authority::try_from(
authority.as_slice(),
).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?,
);
url =
Uri::from_parts(
parts,
).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
let path;
let query;
match head.uri.path_and_query() {
Some(pq) => {
path = pq.path().trim_end_matches('/');
query = pq.query().unwrap_or_default();
},
None => {
path = "";
query = "";
},
}
return Ok(handler.handle(HandlerArgs {
peer_addr: peer_addr,
subpath: path,
query: query,
url: url,
head: &head,
body: body,
}).await) as Result<_, std::io::Error>;
}
}
}))
.await {
Ok(_) => { },
Err(e) => superif!({
let Some(e) = e.downcast_ref::<hyper::Error>() else {
break 'bad;
};
let Some(e) = e.source() else {
break 'bad;
};
let Some(e) = e.downcast_ref::<std::io::Error>() else {
break 'bad;
};
match e.kind() {
std::io::ErrorKind::NotConnected => {
},
_ => {
break 'bad;
},
}
} 'bad {
return Err(loga::err(format!("Error serving connection: {:?}", e)));
}),
};
return Ok(()) as Result<(), loga::Error>;
}.await {
Ok(_) => (),
Err(e) => {
log.log_err(loga::DEBUG, e.context("Error serving connection"));
},
}
});
}
pub async fn root_handle_http<
OD: 'static + Send + Buf,
OE: 'static + Send + Sync + std::error::Error,
O: 'static + Send + http_body::Body<Data = OD, Error = OE>,
>(log: &Log, handler: Arc<dyn Handler<O>>, stream: TcpStream) -> Result<(), loga::Error> {
match async {
let peer_addr = stream.peer_addr().context("Error getting connection peer address")?;
root_handle_http_inner(log, false, peer_addr, stream, handler);
return Ok(()) as Result<_, loga::Error>;
}.await {
Ok(_) => (),
Err(e) => {
log.log_err(loga::DEBUG, e.context("Error setting up connection"));
},
}
return Ok(());
}
pub fn tls_acceptor(certs: Arc<dyn ResolvesServerCert>) -> TlsAcceptor {
return TlsAcceptor::from(Arc::new({
let mut server_config = ServerConfig::builder().with_no_client_auth().with_cert_resolver(certs);
server_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
server_config
}));
}
pub async fn root_handle_https<
OD: 'static + Send + Buf,
OE: 'static + Send + Sync + std::error::Error,
O: 'static + Send + http_body::Body<Data = OD, Error = OE>,
>(
log: &Log,
tls_acceptor: TlsAcceptor,
handler: Arc<dyn Handler<O>>,
stream: TcpStream,
) -> Result<(), loga::Error> {
match async {
let peer_addr = stream.peer_addr().context("Error getting connection peer address")?;
root_handle_http_inner(
log,
true,
peer_addr,
tls_acceptor.accept(stream).await.context("Error establishing TLS connection")?,
handler,
);
return Ok(()) as Result<_, loga::Error>;
}.await {
Ok(_) => (),
Err(e) => {
log.log_err(loga::DEBUG, e.context("Error setting up connection"));
},
}
return Ok(());
}