use std::{net::SocketAddr, sync::Arc};
use http_body_util::Full;
use hyper::{Request, Response, StatusCode, body::Bytes, server::conn::http1, service::service_fn};
use hyper_util::rt::TokioIo;
use sub_host::sub_host;
use tokio::net::TcpListener;
use crate::{Result, Route, req_host};
fn response(status: StatusCode) -> Response<Full<Bytes>> {
let body = if status.is_redirection() {
Bytes::new()
} else {
Bytes::from(status.canonical_reason().unwrap_or_default())
};
let mut res = Response::new(Full::new(body));
*res.status_mut() = status;
res
}
async fn redirect(
req: Request<hyper::body::Incoming>,
route: Arc<Route>,
) -> Result<Response<Full<Bytes>>, hyper::Error> {
let host = req_host(&req);
let pq = req
.uri()
.path_and_query()
.map(|p| p.as_str())
.unwrap_or("/");
let host = if route.host_conf.contains_key(host) {
host.to_owned()
} else if let Some(h) = sub_host(host)
&& route.host_conf.contains_key(h.as_str())
{
h
} else {
return Ok(response(StatusCode::NOT_FOUND));
};
let new_uri = format!("https://{}{}", host, pq);
Ok(match hyper::header::HeaderValue::from_str(&new_uri) {
Ok(location) => {
let mut res = response(StatusCode::MOVED_PERMANENTLY);
res.headers_mut().insert("Location", location);
res
}
Err(_) => response(StatusCode::INTERNAL_SERVER_ERROR),
})
}
pub async fn srv(addr: SocketAddr, route: Arc<Route>) -> Result<()> {
let listener = TcpListener::bind(addr).await?;
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);
let route = route.clone();
tokio::task::spawn(async move {
if let Err(err) = http1::Builder::new()
.serve_connection(io, service_fn(move |req| redirect(req, route.clone())))
.await
{
eprintln!("h1: {:?}", err);
}
});
}
}