use std::{convert::Infallible, net::SocketAddr, sync::Arc};
use http::{HeaderMap, Method, Request, Response, StatusCode};
use hyper::{server::conn::Http, service::service_fn, Body};
use tokio::{net::TcpListener, sync::Mutex};
use crate::{handler::Handler, router::Router, Error, ServerError, TransientState};
#[derive(Clone)]
pub struct App<S: Clone + Send, T: TransientState + 'static + Clone + Send> {
router: Router<S, T>,
global_state: Option<Arc<Mutex<S>>>,
}
impl<S: 'static + Clone + Send, T: TransientState + 'static + Clone + Send> App<S, T> {
pub fn new() -> Self {
Self {
router: Router::new(),
global_state: None,
}
}
pub fn with_state(state: S) -> Self {
Self {
router: Router::new(),
global_state: Some(Arc::new(Mutex::new(state))),
}
}
pub async fn state(&self) -> Option<Arc<Mutex<S>>> {
self.global_state.clone()
}
pub fn get(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::GET, path.to_string(), ch);
}
pub fn post(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::POST, path.to_string(), ch);
}
pub fn delete(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::DELETE, path.to_string(), ch);
}
pub fn put(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::PUT, path.to_string(), ch);
}
pub fn options(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::OPTIONS, path.to_string(), ch);
}
pub fn patch(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::PATCH, path.to_string(), ch);
}
pub fn head(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::HEAD, path.to_string(), ch);
}
pub fn connect(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::CONNECT, path.to_string(), ch);
}
pub fn trace(&mut self, path: &str, ch: Handler<S, T>) {
self.router.add(Method::TRACE, path.to_string(), ch);
}
pub async fn dispatch(&self, req: Request<Body>) -> Result<Response<Body>, Infallible> {
match self.router.dispatch(req, self.clone()).await {
Ok(resp) => Ok(resp),
Err(e) => match e.clone() {
Error::StatusCode(sc, msg) => Ok(Response::builder()
.status(sc)
.body(Body::from(msg))
.unwrap()),
Error::InternalServerError(e) => Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from(e.to_string()))
.unwrap()),
},
}
}
pub async fn serve(self, addr: &str) -> Result<(), ServerError> {
let socketaddr: SocketAddr = addr.parse()?;
let tcp_listener = TcpListener::bind(socketaddr).await?;
loop {
let s = self.clone();
let sfn = service_fn(move |req: Request<Body>| {
let s = s.clone();
async move { s.clone().dispatch(req).await }
});
let (tcp_stream, _) = tcp_listener.accept().await?;
tokio::task::spawn(async move {
if let Err(http_err) = Http::new()
.http1_keep_alive(true)
.serve_connection(tcp_stream, sfn)
.await
{
eprintln!("Error while serving HTTP connection: {}", http_err);
}
});
}
}
pub async fn serve_tls(
self,
addr: &str,
config: tokio_rustls::rustls::ServerConfig,
) -> Result<(), ServerError> {
let socketaddr: SocketAddr = addr.parse()?;
let config = tokio_rustls::TlsAcceptor::from(Arc::new(config));
let tcp_listener = TcpListener::bind(socketaddr).await?;
loop {
let s = self.clone();
let sfn = service_fn(move |req: Request<Body>| {
let s = s.clone();
async move { s.clone().dispatch(req).await }
});
let (tcp_stream, _) = tcp_listener.accept().await?;
let config = config.clone();
tokio::task::spawn(async move {
match config.accept(tcp_stream).await {
Ok(tcp_stream) => {
if let Err(http_err) = Http::new()
.http1_keep_alive(true)
.serve_connection(tcp_stream, sfn)
.await
{
eprintln!("Error while serving HTTP connection: {}", http_err);
}
}
Err(e) => {
eprintln!("Error while serving TLS: {}", e)
}
}
});
}
}
}
#[derive(Clone)]
pub struct TestApp<S: Clone + Send + 'static, T: TransientState + 'static + Clone + Send> {
app: App<S, T>,
headers: Option<HeaderMap>,
}
impl<S: Clone + Send + 'static, T: TransientState + 'static + Clone + Send> TestApp<S, T> {
pub fn new(app: App<S, T>) -> Self {
Self { app, headers: None }
}
pub fn with_headers(&self, headers: http::HeaderMap) -> Self {
Self {
app: self.app.clone(),
headers: Some(headers),
}
}
pub async fn dispatch(&self, req: Request<Body>) -> Response<Body> {
self.app.dispatch(req).await.unwrap()
}
fn populate_headers(&self, mut req: http::request::Builder) -> http::request::Builder {
if let Some(include_headers) = self.headers.clone() {
for (header, value) in include_headers.clone() {
if let Some(header) = header {
req = req.header(header, value.clone());
}
}
}
req
}
pub async fn get(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(req.uri(path).body(Body::default()).unwrap())
.await
.unwrap()
}
pub async fn post(&self, path: &str, body: Body) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(req.method(Method::POST).uri(path).body(body).unwrap())
.await
.unwrap()
}
pub async fn delete(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(
req.method(Method::DELETE)
.uri(path)
.body(Body::default())
.unwrap(),
)
.await
.unwrap()
}
pub async fn put(&self, path: &str, body: Body) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(req.method(Method::PUT).uri(path).body(body).unwrap())
.await
.unwrap()
}
pub async fn options(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(
req.method(Method::OPTIONS)
.uri(path)
.body(Body::default())
.unwrap(),
)
.await
.unwrap()
}
pub async fn patch(&self, path: &str, body: Body) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(req.method(Method::PATCH).uri(path).body(body).unwrap())
.await
.unwrap()
}
pub async fn head(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(
req.method(Method::HEAD)
.uri(path)
.body(Body::default())
.unwrap(),
)
.await
.unwrap()
}
pub async fn trace(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(
req.method(Method::TRACE)
.uri(path)
.body(Body::default())
.unwrap(),
)
.await
.unwrap()
}
pub async fn connect(&self, path: &str) -> Response<Body> {
let req = self.populate_headers(Request::builder());
self.app
.dispatch(
req.method(Method::CONNECT)
.uri(path)
.body(Body::default())
.unwrap(),
)
.await
.unwrap()
}
}