#![forbid(unsafe_code)]
mod accept;
mod ascii_string;
mod body_async_reader;
mod body_reader;
mod content_type;
mod cookie;
mod event;
mod head;
mod headers;
mod http_conn;
mod http_error;
mod request;
mod request_body;
mod response;
mod response_body;
mod time;
mod token_set;
mod util;
pub use crate::accept::{
socket_addr_127_0_0_1, socket_addr_127_0_0_1_any_port, socket_addr_all_interfaces,
};
pub use crate::ascii_string::AsciiString;
pub use crate::body_async_reader::BodyAsyncReader;
pub use crate::body_reader::BodyReader;
pub use crate::content_type::ContentType;
pub use crate::cookie::{Cookie, SameSite};
pub use crate::event::{Event, EventSender};
pub use crate::headers::{Header, HeaderList};
pub use crate::http_conn::HttpConn;
pub use crate::request::Request;
pub use crate::request_body::RequestBody;
pub use crate::response::Response;
pub use crate::response_body::ResponseBody;
pub mod internal {
pub use crate::accept::*;
pub use crate::body_async_reader::*;
pub use crate::body_reader::*;
pub use crate::content_type::*;
pub use crate::cookie::*;
pub use crate::event::*;
pub use crate::head::*;
pub use crate::headers::*;
pub use crate::http_conn::*;
pub use crate::http_error::*;
pub use crate::request::*;
pub use crate::request_body::*;
pub use crate::response::*;
pub use crate::response_body::*;
pub use crate::time::*;
pub use crate::token_set::*;
pub use crate::util::*;
}
pub mod reexport {
pub use permit;
pub use safina_executor;
pub use safina_sync;
pub use safina_timer;
}
use crate::accept::accept_loop;
use crate::http_conn::handle_http_conn;
use crate::token_set::TokenSet;
use async_net::TcpListener;
use permit::Permit;
use std::net::SocketAddr;
use std::path::PathBuf;
#[allow(clippy::needless_pass_by_value)]
#[must_use]
pub fn print_log_response(req: &Request, result: Result<Response, Response>) -> Response {
let response = result.unwrap_or_else(|e| e);
if !response.is_get_body_and_reprocess() {
println!(
"{} {} {} => {} {}",
if response.code / 100 == 5 {
"ERROR"
} else {
"INFO"
},
req.method(),
req.url().path(),
response.code,
if let Some(len) = response.body.len() {
format!("len={}", len)
} else {
"streamed".to_string()
},
);
}
response
}
pub struct HttpServerBuilder {
opt_cache_dir: Option<PathBuf>,
listen_addr: SocketAddr,
max_conns: usize,
small_body_len: usize,
permit: Permit,
}
impl HttpServerBuilder {
#[allow(clippy::new_without_default)]
#[must_use]
pub fn new() -> Self {
Self {
opt_cache_dir: None,
listen_addr: socket_addr_127_0_0_1_any_port(),
max_conns: 100,
small_body_len: 64 * 1024,
permit: Permit::new(),
}
}
#[must_use]
pub fn listen_addr(mut self, addr: SocketAddr) -> Self {
self.listen_addr = addr;
self
}
#[must_use]
pub fn max_conns(mut self, n: usize) -> Self {
assert!(n > 0, "refusing to set max_conns to zero");
self.max_conns = n;
self
}
#[must_use]
pub fn receive_large_bodies(mut self, cache_dir: &std::path::Path) -> Self {
self.opt_cache_dir = Some(cache_dir.to_path_buf());
self
}
#[must_use]
pub fn small_body_len(mut self, n: usize) -> Self {
self.small_body_len = n;
self
}
#[must_use]
pub fn permit(mut self, p: Permit) -> Self {
self.permit = p;
self
}
pub async fn spawn<F>(
self,
request_handler: F,
) -> Result<(SocketAddr, reexport::safina_sync::Receiver<()>), std::io::Error>
where
F: FnOnce(Request) -> Response + 'static + Clone + Send + Sync,
{
let async_request_handler = |req: Request| async move {
let request_handler_clone = request_handler.clone();
safina_executor::schedule_blocking(move || request_handler_clone(req))
.await
.unwrap_or_else(|_| Response::text(500, "Server error"))
};
let conn_handler = move |permit, token, stream: async_net::TcpStream, addr| {
let http_conn = HttpConn::new(addr, stream);
safina_executor::spawn(handle_http_conn(
permit,
token,
http_conn,
self.opt_cache_dir,
self.small_body_len,
async_request_handler,
));
};
let listener = TcpListener::bind(self.listen_addr).await?;
let addr = listener.local_addr()?;
let token_set = TokenSet::new(self.max_conns);
let (sender, receiver) = safina_sync::oneshot();
safina_executor::spawn(async move {
accept_loop(self.permit, listener, token_set, conn_handler).await;
let _ignored = sender.send(());
});
Ok((addr, receiver))
}
pub async fn spawn_and_join<F>(self, request_handler: F) -> Result<(), std::io::Error>
where
F: FnOnce(Request) -> Response + 'static + Clone + Send + Sync,
{
let (_addr, mut stopped_receiver) = self.spawn(request_handler).await?;
let _ignored = stopped_receiver.async_recv().await;
Ok(())
}
}