1use crate::http::{Request, Response};
2use crate::routing::Router;
3use bytes::Bytes;
4use http_body_util::Full;
5use hyper::server::conn::http1;
6use hyper::service::service_fn;
7use hyper_util::rt::TokioIo;
8use std::convert::Infallible;
9use std::net::SocketAddr;
10use std::sync::Arc;
11use tokio::net::TcpListener;
12
13pub struct Server {
15 router: Arc<Router>,
16 host: String,
17 port: u16,
18}
19
20impl Server {
21 pub fn new(router: Router) -> Self {
22 Self {
23 router: Arc::new(router),
24 host: "127.0.0.1".to_string(),
25 port: 8000,
26 }
27 }
28
29 pub fn host(&mut self, host: &str) -> &mut Self {
30 self.host = host.to_string();
31 self
32 }
33
34 pub fn port(&mut self, port: u16) -> &mut Self {
35 self.port = port;
36 self
37 }
38
39 fn get_addr(&self) -> SocketAddr {
40 SocketAddr::new(self.host.parse().unwrap(), self.port)
41 }
42
43 pub async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
45 let addr: SocketAddr = self.get_addr();
47 let listener = TcpListener::bind(addr).await?;
48
49 println!("Kit server running on http://{}", addr);
50
51 loop {
52 let (stream, _) = listener.accept().await?;
53 let io = TokioIo::new(stream);
54 let router = self.router.clone();
55
56 tokio::spawn(async move {
57 let service = service_fn(move |req: hyper::Request<hyper::body::Incoming>| {
58 let router = router.clone();
59 async move { Ok::<_, Infallible>(handle_request(router, req).await) }
60 });
61
62 if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
63 eprintln!("Error serving connection: {:?}", err);
64 }
65 });
66 }
67 }
68}
69
70async fn handle_request(
71 router: Arc<Router>,
72 req: hyper::Request<hyper::body::Incoming>,
73) -> hyper::Response<Full<Bytes>> {
74 let method = req.method().clone();
75 let path = req.uri().path().to_string();
76
77 match router.match_route(&method, &path) {
78 Some((handler, params)) => {
79 let request = Request::new(req).with_params(params);
80 let response = handler(request).await;
81 response.into_hyper()
82 }
83 None => {
84 Response::text("404 Not Found")
85 .status(404)
86 .into_hyper()
87 }
88 }
89}