use std::collections::HashMap;
use std::future;
use std::sync::Arc;
use http_body_util::Full;
use hyper::body::{Bytes, Incoming};
use hyper::server::conn::http1::Builder as ConnectionBuilder;
use hyper::{Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use tower::service_fn;
use tower::Service as _;
type Body = Full<Bytes>;
async fn index(_req: Request<Incoming>) -> hyper::Result<Response<Body>> {
Ok(Response::new(Body::from("Hello, world!")))
}
async fn blog(_req: Request<Incoming>) -> hyper::Result<Response<Body>> {
Ok(Response::new(Body::from("...")))
}
async fn not_found(_req: Request<Incoming>) -> hyper::Result<Response<Body>> {
Ok(Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::default())
.unwrap())
}
type Service = tower::util::BoxCloneSyncService<Request<Incoming>, Response<Body>, hyper::Error>;
type Router = HashMap<Method, matchit::Router<Service>>;
async fn route(router: Arc<Router>, req: Request<Incoming>) -> hyper::Result<Response<Body>> {
let Some(router) = router.get(req.method()) else {
return Ok(Response::builder()
.status(StatusCode::METHOD_NOT_ALLOWED)
.body(Body::default())
.unwrap());
};
let Ok(found) = router.at(req.uri().path()) else {
return not_found(req).await;
};
let mut service = found.value.clone();
future::poll_fn(|cx| service.poll_ready(cx)).await?;
service.call(req).await
}
#[tokio::main]
async fn main() {
let mut router = Router::new();
router
.entry(Method::GET)
.or_default()
.insert("/", Service::new(service_fn(index)))
.unwrap();
router
.entry(Method::GET)
.or_default()
.insert("/blog", Service::new(service_fn(blog)))
.unwrap();
let listener = TcpListener::bind(("127.0.0.1", 3000)).await.unwrap();
let router = Arc::new(router);
loop {
let router = router.clone();
let (tcp, _) = listener.accept().await.unwrap();
tokio::task::spawn(async move {
if let Err(err) = ConnectionBuilder::new()
.serve_connection(
TokioIo::new(tcp),
hyper::service::service_fn(|request| route(router.clone(), request)),
)
.await
{
println!("Error serving connection: {err:?}");
}
});
}
}