Skip to main content

nanofish_client/
server.rs

1use crate::{
2    error::Error,
3    handler::HttpHandler,
4    header::HttpHeader,
5    request::HttpRequest,
6    response::{HttpResponse, ResponseBody},
7    status_code::StatusCode,
8};
9use embassy_net::{Stack, tcp::TcpSocket};
10use embassy_net_08 as embassy_net;
11use embassy_time_05 as embassy_time;
12use embedded_io_async_07 as embedded_io_async;
13
14use embassy_time::{Duration, Timer, with_timeout};
15use embedded_io_async::Write as EmbeddedWrite;
16use heapless::Vec;
17
18const SERVER_BUFFER_SIZE: usize = 4096;
19const MAX_REQUEST_SIZE: usize = 4096;
20const DEFAULT_MAX_RESPONSE_SIZE: usize = 4096;
21
22/// HTTP server timeout configuration
23#[derive(Debug, Clone, Copy)]
24pub struct ServerTimeouts {
25    /// Socket accept timeout in seconds
26    pub accept_timeout: u64,
27    /// Socket read timeout in seconds  
28    pub read_timeout: u64,
29    /// Request handler timeout in seconds
30    pub handler_timeout: u64,
31}
32
33impl Default for ServerTimeouts {
34    fn default() -> Self {
35        Self {
36            accept_timeout: 10,
37            read_timeout: 30,
38            handler_timeout: 60,
39        }
40    }
41}
42
43impl ServerTimeouts {
44    /// Create new server timeouts with custom values
45    #[must_use]
46    pub fn new(accept_timeout: u64, read_timeout: u64, handler_timeout: u64) -> Self {
47        Self {
48            accept_timeout,
49            read_timeout,
50            handler_timeout,
51        }
52    }
53}
54
55/// Simple HTTP server implementation
56///
57/// **Note**: This server only supports HTTP connections, not HTTPS/TLS.
58/// For secure connections, consider using a reverse proxy or load balancer
59/// that handles TLS termination.
60pub struct HttpServer<
61    const RX_SIZE: usize,
62    const TX_SIZE: usize,
63    const REQ_SIZE: usize,
64    const MAX_RESPONSE_SIZE: usize,
65> {
66    port: u16,
67    timeouts: ServerTimeouts,
68}
69
70impl<
71    const RX_SIZE: usize,
72    const TX_SIZE: usize,
73    const REQ_SIZE: usize,
74    const MAX_RESPONSE_SIZE: usize,
75> HttpServer<RX_SIZE, TX_SIZE, REQ_SIZE, MAX_RESPONSE_SIZE>
76{
77    /// Create a new HTTP server with default timeouts
78    #[must_use]
79    pub fn new(port: u16) -> Self {
80        Self {
81            port,
82            timeouts: ServerTimeouts::default(),
83        }
84    }
85
86    /// Create a new HTTP server with custom timeouts
87    #[must_use]
88    pub fn with_timeouts(port: u16, timeouts: ServerTimeouts) -> Self {
89        Self { port, timeouts }
90    }
91
92    /// Start the HTTP server and handle incoming connections
93    ///
94    /// **Important**: This server only accepts plain HTTP connections.
95    /// HTTPS/TLS is not supported by the server (only by the client).
96    pub async fn serve<H>(&mut self, stack: Stack<'_>, mut handler: H) -> !
97    where
98        H: HttpHandler,
99    {
100        defmt::info!("HTTP server started on port {}", self.port);
101
102        let mut rx_buffer = [0; RX_SIZE];
103        let mut tx_buffer = [0; TX_SIZE];
104        let mut buf = [0; REQ_SIZE];
105
106        loop {
107            let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
108            socket.set_timeout(Some(Duration::from_secs(self.timeouts.accept_timeout)));
109
110            if let Err(e) = socket.accept(self.port).await {
111                defmt::warn!("Accept error: {:?}", e);
112                Timer::after(Duration::from_millis(100)).await;
113                continue;
114            }
115
116            let n = match with_timeout(
117                Duration::from_secs(self.timeouts.read_timeout),
118                socket.read(&mut buf),
119            )
120            .await
121            {
122                Ok(Ok(0)) => {
123                    // Connection closed
124                    continue;
125                }
126                Ok(Ok(n)) => n,
127                Ok(Err(e)) => {
128                    defmt::warn!("Read error: {:?}", e);
129                    continue;
130                }
131                Err(_) => {
132                    defmt::warn!("Socket read timeout");
133                    continue;
134                }
135            };
136
137            // Parse the request
138            match self.handle_connection(&buf[..n], &mut handler).await {
139                Ok(response_bytes) => {
140                    if let Err(e) = socket.write_all(&response_bytes).await {
141                        defmt::warn!("Failed to write response: {:?}", e);
142                    }
143                }
144                Err(e) => {
145                    defmt::error!("Error handling request: {:?}", e);
146                    // Send a 500 error response
147                    let error_response = b"HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\nContent-Length: 21\r\n\r\nInternal Server Error";
148                    let _ = socket.write_all(error_response).await;
149                }
150            }
151
152            socket.close();
153        }
154    }
155
156    async fn handle_connection<H>(
157        &mut self,
158        buffer: &[u8],
159        handler: &mut H,
160    ) -> Result<Vec<u8, MAX_RESPONSE_SIZE>, Error>
161    where
162        H: HttpHandler,
163    {
164        // Parse the request
165        let request = HttpRequest::try_from(buffer)?;
166
167        // Handle the request
168        let response = match with_timeout(
169            Duration::from_secs(self.timeouts.handler_timeout),
170            handler.handle_request(&request),
171        )
172        .await
173        {
174            Ok(Ok(response)) => response,
175            Ok(Err(e)) => {
176                defmt::warn!("Handler error: {:?}", e);
177                let mut headers = Vec::new();
178                let _ = headers.push(HttpHeader::new("Content-Type", "text/plain"));
179                let error_response = HttpResponse {
180                    status_code: StatusCode::InternalServerError,
181                    headers,
182                    body: ResponseBody::Text("Internal Server Error"),
183                };
184                return Ok(error_response.build_bytes::<MAX_RESPONSE_SIZE>());
185            }
186            Err(_) => {
187                defmt::warn!("Request handling timed out");
188                let mut headers = Vec::new();
189                let _ = headers.push(HttpHeader::new("Content-Type", "text/plain"));
190                let timeout_response = HttpResponse {
191                    status_code: StatusCode::BadRequest,
192                    headers,
193                    body: ResponseBody::Text("Request Timeout"),
194                };
195                return Ok(timeout_response.build_bytes::<MAX_RESPONSE_SIZE>());
196            }
197        };
198
199        Ok(response.build_bytes::<MAX_RESPONSE_SIZE>())
200    }
201}
202
203/// Type alias for `HttpServer` with default buffer sizes (4KB each)
204pub type DefaultHttpServer =
205    HttpServer<SERVER_BUFFER_SIZE, SERVER_BUFFER_SIZE, MAX_REQUEST_SIZE, DEFAULT_MAX_RESPONSE_SIZE>;
206
207/// Type alias for `HttpServer` with small buffer sizes for memory-constrained environments (1KB each)
208pub type SmallHttpServer = HttpServer<1024, 1024, 1024, 1024>;
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    fn test_http_server_creation() {
216        let server: DefaultHttpServer = HttpServer::new(8080);
217        assert_eq!(server.port, 8080);
218        assert_eq!(server.timeouts.accept_timeout, 10);
219        assert_eq!(server.timeouts.read_timeout, 30);
220        assert_eq!(server.timeouts.handler_timeout, 60);
221
222        let server: SmallHttpServer = HttpServer::new(3000);
223        assert_eq!(server.port, 3000);
224    }
225
226    #[test]
227    fn test_server_timeouts() {
228        // Test default timeouts
229        let timeouts = ServerTimeouts::default();
230        assert_eq!(timeouts.accept_timeout, 10);
231        assert_eq!(timeouts.read_timeout, 30);
232        assert_eq!(timeouts.handler_timeout, 60);
233
234        // Test custom timeouts
235        let custom_timeouts = ServerTimeouts::new(5, 15, 45);
236        assert_eq!(custom_timeouts.accept_timeout, 5);
237        assert_eq!(custom_timeouts.read_timeout, 15);
238        assert_eq!(custom_timeouts.handler_timeout, 45);
239
240        // Test server with custom timeouts
241        let server = HttpServer::<1024, 1024, 1024, 1024>::with_timeouts(8080, custom_timeouts);
242        assert_eq!(server.port, 8080);
243        assert_eq!(server.timeouts.accept_timeout, 5);
244        assert_eq!(server.timeouts.read_timeout, 15);
245        assert_eq!(server.timeouts.handler_timeout, 45);
246    }
247}