use alloc::string::String;
use alloc::vec;
use embassy_net::Stack;
use embassy_time::Duration;
use picoserve::io::Write;
use picoserve::response::Content;
use picoserve::routing::PathRouter;
use picoserve::{Router, Timeouts};
#[derive(Debug)]
pub struct ServeConfig {
pub port: u16,
pub http_buffer: usize,
pub tcp_rx_buffer: usize,
pub tcp_tx_buffer: usize,
pub server: picoserve::Config,
}
impl ServeConfig {
pub const DEFAULT: Self = ServeConfig {
port: 80,
http_buffer: 2 * 1024,
tcp_rx_buffer: 16 * 1024,
tcp_tx_buffer: 16 * 1024,
server: picoserve::Config::new(TIMEOUTS).keep_connection_alive(),
};
}
pub const TIMEOUTS: Timeouts = Timeouts {
start_read_request: Duration::from_secs(5),
persistent_start_read_request: Duration::from_secs(1),
read_request: Duration::from_secs(3),
write: Duration::from_secs(10),
};
pub async fn serve<P: PathRouter>(
id: usize,
stack: Stack<'_>,
app: &Router<P>,
config: &ServeConfig,
) -> ! {
stack.wait_config_up().await;
let mut http_buffer = vec![0u8; config.http_buffer];
let mut rx_buffer = vec![0u8; config.tcp_rx_buffer];
let mut tx_buffer = vec![0u8; config.tcp_tx_buffer];
match picoserve::Server::new(app, &config.server, &mut http_buffer)
.listen_and_serve(id, stack, config.port, &mut rx_buffer, &mut tx_buffer)
.await {}
}
#[derive(Debug)]
pub struct TextBody {
pub body: String,
pub content_type: &'static str,
}
impl Content for TextBody {
fn content_type(&self) -> &'static str {
self.content_type
}
fn content_length(&self) -> usize {
self.body.len()
}
async fn write_content<W: Write>(self, mut writer: W) -> Result<(), W::Error> {
writer.write_all(self.body.as_bytes()).await
}
}
#[derive(Debug, Clone, Copy)]
pub struct StaticFile<'a> {
pub body: &'a [u8],
pub content_type: &'static str,
}
impl Content for StaticFile<'_> {
fn content_type(&self) -> &'static str {
self.content_type
}
fn content_length(&self) -> usize {
self.body.len()
}
async fn write_content<W: Write>(self, mut writer: W) -> Result<(), W::Error> {
writer.write_all(self.body).await
}
}
#[cfg(feature = "site")]
pub use site_files::SiteFiles;
#[cfg(feature = "site")]
mod site_files {
use picoserve::ResponseSent;
use picoserve::io::Read;
use picoserve::request::{Path, Request};
use picoserve::response::{IntoResponse, ResponseWriter, StatusCode};
use picoserve::routing::PathRouterService;
use super::StaticFile;
use crate::site::Site;
#[derive(Debug, Clone, Copy)]
pub struct SiteFiles<'s>(pub &'s Site);
impl<State, PathParameters> PathRouterService<State, PathParameters> for SiteFiles<'_> {
async fn call_path_router_service<R: Read, W: ResponseWriter<Error = R::Error>>(
&self,
_state: &State,
_path_parameters: PathParameters,
path: Path<'_>,
request: Request<'_, R>,
response_writer: W,
) -> Result<ResponseSent, W::Error> {
if request.parts.method() != "GET" {
return (StatusCode::new(405), "")
.write_to(request.body_connection.finalize().await?, response_writer)
.await;
}
match self.0.get(path.encoded()) {
Some(asset) => {
(
StatusCode::OK,
StaticFile {
body: &asset.body,
content_type: asset.content_type,
},
)
.write_to(request.body_connection.finalize().await?, response_writer)
.await
}
None => {
(StatusCode::new(404), "not found")
.write_to(request.body_connection.finalize().await?, response_writer)
.await
}
}
}
}
}