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