#[macro_use]
extern crate log;
use futures::future::Ready;
use tokio::sync::RwLock;
use saphir::prelude::*;
struct MagicController {
label: String,
}
impl MagicController {
pub fn new<S: Into<String>>(label: S) -> Self {
Self { label: label.into() }
}
}
impl Controller for MagicController {
const BASE_PATH: &'static str = "/magic";
fn handlers(&self) -> Vec<ControllerEndpoint<Self>>
where
Self: Sized,
{
EndpointsBuilder::new()
.add(Method::GET, "/{delay}", MagicController::magic_delay)
.add(Method::GET, "/", magic_handler)
.add(Method::POST, "/", MagicController::read_body)
.build()
}
}
impl MagicController {
async fn magic_delay(&self, req: Request<Body>) -> (u16, String) {
if let Some(delay) = req.captures().get("delay").and_then(|t| t.parse::<u64>().ok()) {
tokio::time::delay_for(tokio::time::Duration::from_secs(delay)).await;
(200, format!("Delayed of {} secs: {}", delay, self.label))
} else {
(400, "Invalid timeout".to_owned())
}
}
async fn read_body(&self, req: Request<Body>) -> (u16, Vec<u8>) {
let req = req.async_map(|b| async { hyper::body::to_bytes(b).await }).await.transpose().unwrap();
(200, req.body().to_vec())
}
}
fn magic_handler(controller: &MagicController, _: Request<Body>) -> Ready<(u16, String)> {
futures::future::ready((200, controller.label.clone()))
}
struct StatsData {
entered: RwLock<u32>,
exited: RwLock<u32>,
}
impl StatsData {
fn new() -> Self {
Self {
entered: RwLock::new(0),
exited: RwLock::new(0),
}
}
async fn stats_middleware(
&self,
ctx: HttpContext<Body>,
chain: &dyn MiddlewareChain,
) -> Result<Response<Body>, SaphirError> {
{
let mut entered = self.entered.write().await;
let exited = self.exited.read().await;
*entered += 1;
info!(
"entered stats middleware! Current data: entered={} ; exited={}",
*entered, *exited
);
}
let res = chain.next(ctx).await?;
{
let mut exited = self.exited.write().await;
let entered = self.entered.read().await;
*exited += 1;
info!(
"exited stats middleware! Current data: entered={} ; exited={}",
*entered, *exited
);
}
Ok(res)
}
}
async fn log_middleware(
prefix: &String,
ctx: HttpContext<Body>,
chain: &dyn MiddlewareChain,
) -> Result<Response<Body>, SaphirError> {
info!("{} | new request on path: {}", prefix, ctx.request.uri().path());
let res = chain.next(ctx).await?;
info!("{} | new response with status: {}", prefix, res.status());
Ok(res)
}
async fn test_handler(mut req: Request<Body>) -> (u16, Option<String>) {
(200, req.captures_mut().remove("variable"))
}
async fn hello_world(_: Request<Body>) -> (u16, &'static str) {
(200, "Hello, World!")
}
#[tokio::main]
async fn main() -> Result<(), SaphirError> {
env_logger::init();
let server = Server::builder()
.configure_listener(|l| l.interface("127.0.0.1:3000"))
.configure_router(|r| {
r.route("/", Method::GET, hello_world)
.route("/{variable}/print", Method::GET, test_handler)
.controller(MagicController::new("Just Like Magic!"))
})
.configure_middlewares(|m| {
m.apply(log_middleware, "LOG".to_string(), vec!["/"], None)
.apply(StatsData::stats_middleware, StatsData::new(), vec!["/"], None)
})
.build();
server.run().await
}