direct_http 0.5.5

An simple and low-level rust REST API library.
Documentation
use serde::Serialize;
use smol::prelude::*;
use smol::io;

use std::net::{ TcpListener, TcpStream, SocketAddr };
use serde::Serializer;

mod request;
mod response;
mod router;
mod rate_limiter;

pub use response::Response;
pub use router::{ ResponseHandler, Router };
pub use request::{ Request, Method };
pub use rate_limiter::MutexLimiter as Limiter;

#[derive(Copy, Clone)]
pub enum Status {
	Ok = 200,
	Created = 201,
	Accepted = 202,
	NoContent = 204,
	BadRequest = 400,
	Unauthorized = 401,
	Forbidden = 403,
	NotFound = 404,
	MethodNotAllowed = 405,
	TooManyRequests = 429,
	InternalServerError = 500,
}

impl Serialize for Status {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
		serializer.serialize_i32(*self as i32)
	}
}

pub struct Server {
	listener: smol::Async<TcpListener>,
}

impl Server {
	pub fn new(addr: &str) -> Self {
		let addr_parsed: std::net::SocketAddr = addr.parse().expect("Failed to parse address");
		let listener = smol::Async::<TcpListener>::bind(addr_parsed).expect("Failed to create api server");

		Server {
			listener,
		}
	}

	pub fn listen(self, router: Router, limiter: Limiter) {
		limiter.start();
		let router: &'static Router = Box::leak(Box::new(router));
		smol::spawn(async move { self.client_acceptor(router, limiter).await }).detach();
	}

	async fn client_acceptor(self, router: &'static Router, limiter: Limiter) {
		while let Ok((stream, addr)) = self.listener.accept().await {
			let limiter_clone = limiter.clone();
			smol
				::spawn(async move {
					let _ = handler(stream, addr, router, limiter_clone).await;
				})
				.detach();
		}
	}
}

const BUFFER_SIZE: usize = 2048;
const TIMEOUT: u64 = 60;

async fn handler(mut stream: smol::Async<TcpStream>, addr: SocketAddr, router: &'static Router, limiter: Limiter) -> io::Result<()> {
	let mut buffer = [0; BUFFER_SIZE];

	let ip = addr.ip().to_string();

	loop {
		let mut keep_alive = false;

		if stream.read_timeout().await {
			break;
		}

		let bytes_read = stream.read(&mut buffer).await?;

		if bytes_read == 0 {
			break;
		}

		let response = {
			if !limiter.server_allow() || !limiter.client_allow(ip.clone()) {
				Response::status(Status::TooManyRequests).message("Muitas requisições foram feitas. Tente novamente em alguns segundos.").finish()
			} else {
				match Request::new(&buffer[..bytes_read]) {
					Some(mut request) => {
						keep_alive = request.headers.get("connection").is_some_and(|value| value == "keep-alive");
						router::handle(&mut request, &router).finish()
					}
					None => {
						continue;
					}
				}
			}
		};

		if stream.write_timeout().await {
			break;
		}

		stream.write(response.as_bytes()).await?;

		stream.flush().await?;

		if !keep_alive {
			break;
		}
	}

	stream.close().await
}

#[async_trait::async_trait]
trait Timeout {
	async fn write_timeout(&self) -> bool;
	async fn read_timeout(&self) -> bool;
}

#[async_trait::async_trait]
impl Timeout for smol::Async<TcpStream> {
	async fn write_timeout(&self) -> bool {
		smol::future::race(async { Ok(self.writable()) }, async { Err(timer(TIMEOUT).await) }).await.is_err()
	}

	async fn read_timeout(&self) -> bool {
		smol::future::race(async { Ok(self.readable()) }, async { Err(timer(TIMEOUT).await) }).await.is_err()
	}
}

async fn timer(seconds: u64) {
	smol::Timer::after(std::time::Duration::from_secs(seconds)).await;
}