use crate::{
error::Error,
handler::HttpHandler,
header::{HttpHeader, headers::CONTENT_LENGTH, mime_types},
protocol::{self, DOUBLE_CRLF_LEN},
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;
}
socket.set_timeout(None);
let mut total_read = 0;
let read_ok = match with_timeout(
Duration::from_secs(self.timeouts.read_timeout),
Self::read_request(&mut socket, &mut buf, &mut total_read),
)
.await
{
Ok(Ok(())) => true,
Ok(Err(e)) => {
warn!("Read error: {:?}", e);
false
}
Err(_) => {
warn!("Socket read timeout");
false
}
};
if !read_ok || total_read == 0 {
socket.close();
continue;
}
match self.handle_connection(&buf[..total_read], &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);
if let Ok(error_bytes) = Self::text_error_response(
StatusCode::InternalServerError,
"Internal Server Error",
) {
let _ = socket.write_all(&error_bytes).await;
let _ = socket.flush().await;
}
}
}
socket.close();
}
}
async fn read_request(
socket: &mut TcpSocket<'_>,
buf: &mut [u8],
total_read: &mut usize,
) -> Result<(), embassy_net::tcp::Error> {
let mut header_end = None;
while *total_read < buf.len() {
let n = socket.read(&mut buf[*total_read..]).await?;
if n == 0 {
break;
}
*total_read += n;
if header_end.is_none() {
header_end = protocol::find_double_crlf(&buf[..*total_read]);
}
if let Some(hdr_end) = header_end {
let body_start = hdr_end + DOUBLE_CRLF_LEN;
if let Some(cl) = Self::parse_content_length(&buf[..hdr_end]) {
if *total_read >= body_start + cl {
break;
}
} else {
break;
}
}
}
Ok(())
}
fn parse_content_length(header_bytes: &[u8]) -> Option<usize> {
let headers_str = core::str::from_utf8(header_bytes).ok()?;
protocol::find_header_value(headers_str, CONTENT_LENGTH)?
.parse()
.ok()
}
fn text_error_response(
status: StatusCode,
body: &str,
) -> Result<Vec<u8, MAX_RESPONSE_SIZE>, Error> {
let mut headers = Vec::new();
let _ = headers.push(HttpHeader::content_type(mime_types::TEXT));
let resp = HttpResponse {
status_code: status,
headers,
body: ResponseBody::Text(body),
};
resp.build_bytes::<MAX_RESPONSE_SIZE>()
}
async fn handle_connection<H>(
&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);
return Self::text_error_response(
StatusCode::InternalServerError,
"Internal Server Error",
);
}
Err(_) => {
warn!("Request handling timed out");
return Self::text_error_response(StatusCode::RequestTimeout, "Request Timeout");
}
};
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);
}
}