use crate::{
error::Error,
handler::HttpHandler,
header::HttpHeader,
request::HttpRequest,
response::{HttpResponse, ResponseBody},
status_code::StatusCode,
};
use embassy_net::{Stack, tcp::TcpSocket};
use embassy_time::{Duration, Timer, with_timeout};
use embedded_io_async::Write as EmbeddedWrite;
use heapless::Vec;
const SERVER_BUFFER_SIZE: usize = 4096;
const MAX_REQUEST_SIZE: usize = 4096;
const DEFAULT_MAX_RESPONSE_SIZE: usize = 4096;
#[derive(Debug, Clone, Copy)]
pub struct ServerTimeouts {
pub accept_timeout: u64,
pub read_timeout: u64,
pub handler_timeout: u64,
}
impl Default for ServerTimeouts {
fn default() -> Self {
Self {
accept_timeout: 10,
read_timeout: 30,
handler_timeout: 60,
}
}
}
impl ServerTimeouts {
#[must_use]
pub fn new(accept_timeout: u64, read_timeout: u64, handler_timeout: u64) -> Self {
Self {
accept_timeout,
read_timeout,
handler_timeout,
}
}
}
pub struct HttpServer<
const RX_SIZE: usize,
const TX_SIZE: usize,
const REQ_SIZE: usize,
const MAX_RESPONSE_SIZE: usize,
> {
port: u16,
timeouts: ServerTimeouts,
}
impl<
const RX_SIZE: usize,
const TX_SIZE: usize,
const REQ_SIZE: usize,
const MAX_RESPONSE_SIZE: usize,
> HttpServer<RX_SIZE, TX_SIZE, REQ_SIZE, MAX_RESPONSE_SIZE>
{
#[must_use]
pub fn new(port: u16) -> Self {
Self {
port,
timeouts: ServerTimeouts::default(),
}
}
#[must_use]
pub fn with_timeouts(port: u16, timeouts: ServerTimeouts) -> Self {
Self { port, timeouts }
}
pub async fn serve<H>(&mut self, stack: Stack<'_>, handler: H) -> !
where
H: HttpHandler,
{
info!("HTTP server started on port {}", self.port);
let mut rx_buffer = [0; RX_SIZE];
let mut tx_buffer = [0; TX_SIZE];
let mut buf = [0; REQ_SIZE];
loop {
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
socket.set_timeout(Some(Duration::from_secs(self.timeouts.accept_timeout)));
if let Err(e) = socket.accept(self.port).await {
warn!("Accept error: {:?}", e);
Timer::after(Duration::from_millis(100)).await;
continue;
}
let n = match with_timeout(
Duration::from_secs(self.timeouts.read_timeout),
socket.read(&mut buf),
)
.await
{
Ok(Ok(0)) => {
continue;
}
Ok(Ok(n)) => n,
Ok(Err(e)) => {
warn!("Read error: {:?}", e);
continue;
}
Err(_) => {
warn!("Socket read timeout");
continue;
}
};
match self.handle_connection(&buf[..n], &handler).await {
Ok(response_bytes) => {
if let Err(e) = socket.write_all(&response_bytes).await {
warn!("Failed to write response: {:?}", e);
}
if let Err(e) = socket.flush().await {
warn!("Failed to flush response: {:?}", e);
}
}
Err(e) => {
error!("Error handling request: {:?}", e);
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";
let _ = socket.write_all(error_response).await;
let _ = socket.flush().await;
}
}
socket.close();
}
}
async fn handle_connection<H>(
&mut self,
buffer: &[u8],
handler: &H,
) -> Result<Vec<u8, MAX_RESPONSE_SIZE>, Error>
where
H: HttpHandler,
{
let request = HttpRequest::try_from(buffer)?;
let response = match with_timeout(
Duration::from_secs(self.timeouts.handler_timeout),
handler.handle_request(&request),
)
.await
{
Ok(Ok(response)) => response,
Ok(Err(e)) => {
warn!("Handler error: {:?}", e);
let mut headers = Vec::new();
let _ = headers.push(HttpHeader::new("Content-Type", "text/plain"));
let error_response = HttpResponse {
status_code: StatusCode::InternalServerError,
headers,
body: ResponseBody::Text("Internal Server Error"),
};
return Ok(error_response.build_bytes::<MAX_RESPONSE_SIZE>());
}
Err(_) => {
warn!("Request handling timed out");
let mut headers = Vec::new();
let _ = headers.push(HttpHeader::new("Content-Type", "text/plain"));
let timeout_response = HttpResponse {
status_code: StatusCode::BadRequest,
headers,
body: ResponseBody::Text("Request Timeout"),
};
return Ok(timeout_response.build_bytes::<MAX_RESPONSE_SIZE>());
}
};
Ok(response.build_bytes::<MAX_RESPONSE_SIZE>())
}
}
pub type DefaultHttpServer =
HttpServer<SERVER_BUFFER_SIZE, SERVER_BUFFER_SIZE, MAX_REQUEST_SIZE, DEFAULT_MAX_RESPONSE_SIZE>;
pub type SmallHttpServer = HttpServer<1024, 1024, 1024, 1024>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_http_server_creation() {
let server: DefaultHttpServer = HttpServer::new(8080);
assert_eq!(server.port, 8080);
assert_eq!(server.timeouts.accept_timeout, 10);
assert_eq!(server.timeouts.read_timeout, 30);
assert_eq!(server.timeouts.handler_timeout, 60);
let server: SmallHttpServer = HttpServer::new(3000);
assert_eq!(server.port, 3000);
}
#[test]
fn test_server_timeouts() {
let timeouts = ServerTimeouts::default();
assert_eq!(timeouts.accept_timeout, 10);
assert_eq!(timeouts.read_timeout, 30);
assert_eq!(timeouts.handler_timeout, 60);
let custom_timeouts = ServerTimeouts::new(5, 15, 45);
assert_eq!(custom_timeouts.accept_timeout, 5);
assert_eq!(custom_timeouts.read_timeout, 15);
assert_eq!(custom_timeouts.handler_timeout, 45);
let server = HttpServer::<1024, 1024, 1024, 1024>::with_timeouts(8080, custom_timeouts);
assert_eq!(server.port, 8080);
assert_eq!(server.timeouts.accept_timeout, 5);
assert_eq!(server.timeouts.read_timeout, 15);
assert_eq!(server.timeouts.handler_timeout, 45);
}
}