#![no_std]
extern crate no_std_compat as std;
use std::prelude::v1::*;
use std::collections::BTreeMap;
pub use product_os_http::{
Request, Response,
request::Parts as RequestParts,
response::Parts as ResponseParts,
StatusCode,
header::{ HeaderMap, HeaderName, HeaderValue }
};
pub use product_os_http_body::{ BodyBytes, Bytes };
#[cfg(feature = "middleware")]
pub use product_os_http_body::*;
pub use axum::{
routing::*,
middleware::*,
handler::Handler,
response::{ IntoResponse, Redirect },
body::{ HttpBody, Body },
Router,
Json,
Form,
extract::{
*,
ws::{ WebSocketUpgrade, WebSocket, Message }
},
BoxError,
http::Uri
};
pub use crate::extractors::RequestMethod;
use axum::RequestExt;
#[cfg(feature = "debug")]
pub use axum_macros::debug_handler;
pub use tower::{
Layer, Service, ServiceBuilder, ServiceExt, MakeService,
make::AsService, make::MakeConnection, make::IntoService,
make::Shared, make::future::SharedFuture,
util::service_fn, util::ServiceFn
};
pub use product_os_request::Method;
use regex::Regex;
#[cfg(feature = "middleware")]
mod middleware;
mod extractors;
mod default_headers;
mod service_wrapper;
mod dual_protocol;
pub use crate::default_headers::DefaultHeadersLayer;
pub use crate::dual_protocol::UpgradeHttpLayer;
#[cfg(feature = "middleware")]
pub use crate::middleware::*;
#[cfg(feature = "cors")]
use tower_http::cors::CorsLayer;
use crate::service_wrapper::{WrapperLayer, WrapperService};
#[derive(Debug)]
pub enum ProductOSRouterError {
Headers(String),
Query(String),
Body(String),
Authentication(String),
Authorization(String),
Process(String),
Unavailable(String)
}
impl ProductOSRouterError {
pub fn error_response(error: ProductOSRouterError, status_code: &StatusCode) -> Response<Body> {
let error_message = match error {
ProductOSRouterError::Headers(m) => { m }
ProductOSRouterError::Query(m) => { m }
ProductOSRouterError::Body(m) => { m }
ProductOSRouterError::Authentication(m) => { m }
ProductOSRouterError::Authorization(m) => { m }
ProductOSRouterError::Process(m) => { m }
ProductOSRouterError::Unavailable(m) => { m }
}.replace("\"", "'");
let status_code_u16 = status_code.as_u16();
Response::builder()
.status(status_code_u16)
.header("content-type", "application/json")
.body(
Body::from(
format!("{{\
\"error\": \"{error_message}\"\
}}")))
.unwrap()
}
pub fn handle_error(error: &ProductOSRouterError) -> Response<Body> {
match error {
ProductOSRouterError::Headers(msg) => Response::builder()
.status(StatusCode::BAD_REQUEST.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Query(msg) => Response::builder()
.status(StatusCode::BAD_REQUEST.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Body(msg) => Response::builder()
.status(StatusCode::BAD_REQUEST.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Authentication(msg) => Response::builder()
.status(StatusCode::UNAUTHORIZED.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Authorization(msg) => Response::builder()
.status(StatusCode::FORBIDDEN.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Process(msg) => Response::builder()
.status(StatusCode::GATEWAY_TIMEOUT.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap(),
ProductOSRouterError::Unavailable(msg) => Response::builder()
.status(StatusCode::SERVICE_UNAVAILABLE.as_u16())
.body(Body::from(format!("{{\
\"error\": \"{msg}\"\
}}")))
.unwrap()
}
}
}
pub struct ProductOSRouter<S = ()> {
router: Router<S>,
state: S
}
impl ProductOSRouter<()> {
pub fn new() -> Self {
ProductOSRouter::new_with_state(())
}
pub fn param_to_field(path: &str) -> String {
let param_matcher = Regex::new("([:*])([a-zA-Z][-_a-zA-Z0-9]*)").unwrap();
let symbol_matcher = Regex::new("[*?&]").unwrap();
let mut path_new = path.to_owned();
for (value, [prefix, variable_name]) in param_matcher.captures_iter(path).map(|c| c.extract()) {
let mut value_sanitized = value.to_owned();
for (prefix, []) in symbol_matcher.captures_iter(value).map(|c| c.extract()) {
let mut symbol = String::from("[");
symbol.push_str(prefix);
symbol.push_str("]");
value_sanitized = value_sanitized.replace(prefix, symbol.as_str());
}
let exact_matcher = Regex::new(value_sanitized.as_str()).unwrap();
let mut variable = String::from("{");
variable.push_str(variable_name);
variable.push_str("}");
path_new = exact_matcher.replace(path_new.as_str(), variable).to_string()
}
path_new
}
pub fn add_service_no_state<Z>(&mut self, path: &str, service: Z)
where
Z: Service<Request<Body>, Response = Response<Body>, Error = BoxError> + Clone + Send + Sync + 'static,
Z::Future: Send + 'static
{
let wrapper = WrapperService::new(service);
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route_service(path.as_str(), wrapper);
}
pub fn add_middleware_no_state<L>(&mut self, middleware: L)
where
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<BoxError> + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
{
let wrapper = WrapperLayer::new(middleware);
self.router = self.router.clone().layer(wrapper)
}
pub fn add_default_header_no_state(&mut self, header_name: &str, header_value: &str) {
let mut default_headers = HeaderMap::new();
default_headers.insert(HeaderName::from_bytes(header_name.as_bytes()).unwrap(), HeaderValue::from_str(header_value).unwrap());
self.add_middleware_no_state(DefaultHeadersLayer::new(default_headers));
}
pub fn not_implemented() -> Response<Body> {
Response::builder()
.status(StatusCode::NOT_FOUND.as_u16())
.header("content-type", "application/json")
.body(Body::from("{}"))
.unwrap()
}
pub fn add_route_no_state(&mut self, path: &str, service_handler: MethodRouter)
{
let service= ServiceBuilder::new().service(service_handler);
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
pub fn set_fallback_no_state(&mut self, service_handler: MethodRouter)
{
let service= ServiceBuilder::new().service(service_handler);
self.router = self.router.clone().fallback(service);
}
pub fn add_get_no_state<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(Method::GET, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn add_post_no_state<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(Method::POST, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn add_handler_no_state<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(method, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn set_fallback_handler_no_state<H, T>(&mut self, handler: H)
where
H: Handler<T, ()>,
T: 'static
{
self.router = self.router.clone().fallback(handler).with_state(self.state.clone());
}
#[cfg(feature = "cors")]
pub fn add_cors_handler_no_state<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let method_router = ProductOSRouter::add_cors_method_router(method, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
#[cfg(feature = "ws")]
pub fn add_ws_handler_no_state<H, T>(&mut self, path: &str, ws_handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let service: MethodRouter = ProductOSRouter::convert_handler_to_method_router(Method::GET, ws_handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
#[cfg(feature = "sse")]
pub fn add_sse_handler_no_state<H, T>(&mut self, path: &str, sse_handler: H)
where
H: Handler<T, ()>,
T: 'static
{
let service: MethodRouter = ProductOSRouter::convert_handler_to_method_router(Method::GET, sse_handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
pub fn add_handlers_no_state<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, ()>,
T: 'static
{
let mut method_router: MethodRouter = MethodRouter::new();
for (method, handler) in handlers {
method_router = ProductOSRouter::add_handler_to_method_router(method_router, method, handler);
}
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
#[cfg(feature = "cors")]
pub fn add_cors_handlers_no_state<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, ()>,
T: 'static
{
let mut service: MethodRouter = MethodRouter::new();
for (method, handler) in handlers {
service = ProductOSRouter::add_cors_handler_to_method_router(service, method, handler);
}
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
fn add_handler_to_method_router_no_state<H, T>(method_router: MethodRouter, method: Method, handler: H) -> MethodRouter
where
H: Handler<T, ()>,
T: 'static
{
match method {
Method::GET => method_router.get(handler),
Method::POST => method_router.post(handler),
Method::PUT => method_router.put(handler),
Method::PATCH => method_router.patch(handler),
Method::DELETE => method_router.delete(handler),
Method::TRACE => method_router.trace(handler),
Method::HEAD => method_router.head(handler),
Method::CONNECT => method_router.get(handler),
Method::OPTIONS => method_router.get(handler),
Method::ANY => method_router.get(handler),
}
}
fn convert_handler_to_method_router_no_state<H, T>(method: Method, handler: H) -> MethodRouter
where
H: Handler<T, ()>,
T: 'static,
{
match method {
Method::GET => get(handler),
Method::POST => post(handler),
Method::PUT => put(handler),
Method::PATCH => patch(handler),
Method::DELETE => delete(handler),
Method::TRACE => trace(handler),
Method::HEAD => head(handler),
Method::CONNECT => get(handler),
Method::OPTIONS => get(handler),
Method::ANY => any(handler)
}
}
#[cfg(feature = "cors")]
fn add_cors_handler_to_method_router_no_state<H, T>(method_router: MethodRouter, method: Method, handler: H) -> MethodRouter
where
H: Handler<T, ()>,
T: 'static
{
ProductOSRouter::add_handler_to_method_router_no_state(method_router, method, handler).layer(CorsLayer::new()
.allow_origin(tower_http::cors::any())
.allow_methods(tower_http::cors::any()))
}
#[cfg(feature = "cors")]
fn add_cors_method_router_no_state<H, T>(method: Method, handler: H) -> MethodRouter
where
H: Handler<T, ()>,
T: 'static
{
ProductOSRouter::convert_handler_to_method_router_no_state(method, handler).layer(CorsLayer::new()
.allow_origin(tower_http::cors::any())
.allow_methods(tower_http::cors::any()))
}
}
impl<S> ProductOSRouter<S>
where
S: Clone + Send + Sync + 'static
{
pub fn get_router(&self) -> Router {
self.router.clone().with_state(self.state.clone())
}
pub fn get_router_mut(&mut self) -> &mut Router<S> {
&mut self.router
}
pub fn new_with_state(state: S) -> Self
where
S: Clone + Send + 'static
{
Self {
router: Router::new().with_state(state.clone()),
state
}
}
pub fn add_service<Z>(&mut self, path: &str, service: Z)
where
Z: Service<Request<Body>, Response = Response<Body>, Error = BoxError> + Clone + Send + Sync + 'static,
Z::Response: IntoResponse,
Z::Future: Send + 'static
{
let wrapper = WrapperService::new(service);
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route_service(path.as_str(), wrapper);
}
pub fn add_middleware<L>(&mut self, middleware: L)
where
L: Layer<Route> + Clone + Send + Sync + 'static,
L::Service: Service<Request<Body>> + Clone + Send + Sync + 'static,
<L::Service as Service<Request<Body>>>::Future: Send + 'static,
<L::Service as Service<Request<Body>>>::Error: Into<BoxError> + 'static,
<L::Service as Service<Request<Body>>>::Response: IntoResponse + 'static,
{
let wrapper = WrapperLayer::new(middleware);
self.router = self.router.clone().layer(wrapper)
}
pub fn add_default_header(&mut self, header_name: String, header_value: String) {
let mut default_headers = HeaderMap::new();
default_headers.insert(HeaderName::from_bytes(header_name.as_bytes()).unwrap(), HeaderValue::from_str(header_value.as_str()).unwrap());
self.add_middleware(DefaultHeadersLayer::new(default_headers));
}
pub fn add_route(&mut self, path: &str, service_handler: MethodRouter<S>)
where
S: Clone + Send + 'static,
{
let service= ServiceBuilder::new().service(service_handler);
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
pub fn set_fallback(&mut self, service_handler: MethodRouter<S>)
where
S: Clone + Send + 'static
{
let service= ServiceBuilder::new().service(service_handler);
self.router = self.router.clone().fallback(service);
}
pub fn add_get<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, S>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(Method::GET, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn add_post<H, T>(&mut self, path: &str, handler: H)
where
H: Handler<T, S>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(Method::POST, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn add_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, S>,
T: 'static
{
let method_router = ProductOSRouter::convert_handler_to_method_router(method, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
pub fn add_state(&mut self, state: S) {
self.router = self.router.clone().with_state(state);
}
pub fn set_fallback_handler<H, T>(&mut self, handler: H)
where
H: Handler<T, S>,
T: 'static
{
self.router = self.router.clone().fallback(handler).with_state(self.state.clone());
}
#[cfg(feature = "cors")]
pub fn add_cors_handler<H, T>(&mut self, path: &str, method: Method, handler: H)
where
H: Handler<T, S>,
T: 'static
{
let method_router = ProductOSRouter::add_cors_method_router(method, handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
#[cfg(feature = "ws")]
pub fn add_ws_handler<H, T>(&mut self, path: &str, ws_handler: H)
where
H: Handler<T, S>,
T: 'static
{
let service: MethodRouter<S> = ProductOSRouter::convert_handler_to_method_router(Method::GET, ws_handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
#[cfg(feature = "sse")]
pub fn add_sse_handler<H, T>(&mut self, path: &str, sse_handler: H)
where
H: Handler<T, S>,
T: 'static
{
let service: MethodRouter<S> = ProductOSRouter::convert_handler_to_method_router(Method::GET, sse_handler, self.state.clone());
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
pub fn add_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, S>,
T: 'static
{
let mut method_router: MethodRouter<S> = MethodRouter::new();
for (method, handler) in handlers {
method_router = ProductOSRouter::add_handler_to_method_router(method_router, method, handler);
}
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), method_router);
}
#[cfg(feature = "cors")]
pub fn add_cors_handlers<H, T>(&mut self, path: &str, handlers: BTreeMap<Method, H>)
where
H: Handler<T, S>,
T: 'static
{
let mut service: MethodRouter<S> = MethodRouter::new();
for (method, handler) in handlers {
service = ProductOSRouter::add_cors_handler_to_method_router(service, method, handler);
}
let path = ProductOSRouter::param_to_field(path);
self.router = self.router.clone().route(path.as_str(), service);
}
fn add_handler_to_method_router<H, T>(method_router: MethodRouter<S>, method: Method, handler: H) -> MethodRouter<S>
where
H: Handler<T, S>,
T: 'static
{
match method {
Method::GET => method_router.get(handler),
Method::POST => method_router.post(handler),
Method::PUT => method_router.put(handler),
Method::PATCH => method_router.patch(handler),
Method::DELETE => method_router.delete(handler),
Method::TRACE => method_router.trace(handler),
Method::HEAD => method_router.head(handler),
Method::CONNECT => method_router.get(handler),
Method::OPTIONS => method_router.get(handler),
Method::ANY => method_router.get(handler),
}
}
fn convert_handler_to_method_router<H, T>(method: Method, handler: H, state: S) -> MethodRouter<S>
where
H: Handler<T, S>,
T: 'static,
{
match method {
Method::GET => get(handler).with_state(state),
Method::POST => post(handler).with_state(state),
Method::PUT => put(handler).with_state(state),
Method::PATCH => patch(handler).with_state(state),
Method::DELETE => delete(handler).with_state(state),
Method::TRACE => trace(handler).with_state(state),
Method::HEAD => head(handler).with_state(state),
Method::CONNECT => get(handler).with_state(state),
Method::OPTIONS => get(handler).with_state(state),
Method::ANY => any(handler).with_state(state)
}
}
#[cfg(feature = "cors")]
fn add_cors_handler_to_method_router<H, T>(method_router: MethodRouter<S>, method: Method, handler: H) -> MethodRouter<S>
where
H: Handler<T, S>,
T: 'static
{
ProductOSRouter::add_handler_to_method_router(method_router, method, handler).layer(CorsLayer::new()
.allow_origin(tower_http::cors::any())
.allow_methods(tower_http::cors::any()))
}
#[cfg(feature = "cors")]
fn add_cors_method_router<H, T>(method: Method, handler: H, state: S) -> MethodRouter<S>
where
H: Handler<T, S>,
T: 'static
{
ProductOSRouter::convert_handler_to_method_router(method, handler, state).layer(CorsLayer::new()
.allow_origin(tower_http::cors::any())
.allow_methods(tower_http::cors::any()))
}
}