kit_rs/
server.rs

1use crate::http::{HttpResponse, Request};
2use crate::inertia::InertiaContext;
3use crate::routing::Router;
4use bytes::Bytes;
5use http_body_util::Full;
6use hyper::server::conn::http1;
7use hyper::service::service_fn;
8use hyper_util::rt::TokioIo;
9use std::convert::Infallible;
10use std::net::SocketAddr;
11use std::sync::Arc;
12use tokio::net::TcpListener;
13
14pub 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>> {
44        // Default to localhost:8000
45        let addr: SocketAddr = self.get_addr();
46        let listener = TcpListener::bind(addr).await?;
47
48        println!("Kit server running on http://{}", addr);
49
50        loop {
51            let (stream, _) = listener.accept().await?;
52            let io = TokioIo::new(stream);
53            let router = self.router.clone();
54
55            tokio::spawn(async move {
56                let service = service_fn(move |req: hyper::Request<hyper::body::Incoming>| {
57                    let router = router.clone();
58                    async move { Ok::<_, Infallible>(handle_request(router, req).await) }
59                });
60
61                if let Err(err) = http1::Builder::new().serve_connection(io, service).await {
62                    eprintln!("Error serving connection: {:?}", err);
63                }
64            });
65        }
66    }
67}
68
69async fn handle_request(
70    router: Arc<Router>,
71    req: hyper::Request<hyper::body::Incoming>,
72) -> hyper::Response<Full<Bytes>> {
73    let method = req.method().clone();
74    let path = req.uri().path().to_string();
75
76    // Set up Inertia context from request headers
77    let is_inertia = req
78        .headers()
79        .get("X-Inertia")
80        .and_then(|v| v.to_str().ok())
81        .map(|v| v == "true")
82        .unwrap_or(false);
83
84    let inertia_version = req
85        .headers()
86        .get("X-Inertia-Version")
87        .and_then(|v| v.to_str().ok())
88        .map(|v| v.to_string());
89
90    InertiaContext::set(InertiaContext {
91        path: path.clone(),
92        is_inertia,
93        version: inertia_version,
94    });
95
96    let response = match router.match_route(&method, &path) {
97        Some((handler, params)) => {
98            let request = Request::new(req).with_params(params);
99            let response = handler(request).await;
100            // Unwrap the Result - both Ok and Err contain HttpResponse
101            let http_response = response.unwrap_or_else(|e| e);
102            http_response.into_hyper()
103        }
104        None => {
105            HttpResponse::text("404 Not Found")
106                .status(404)
107                .into_hyper()
108        }
109    };
110
111    // Clear context after request
112    InertiaContext::clear();
113
114    response
115}