feat: new matcher system instead of manually 'match'ing in servcie
This commit is contained in:
parent
f305cb5a85
commit
638b0376d8
15 changed files with 646 additions and 225 deletions
129
src/services/matcher.rs
Normal file
129
src/services/matcher.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
use std::{net::IpAddr, pin::Pin, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use base64::{Engine, prelude::BASE64_STANDARD};
|
||||
use http::request::Parts;
|
||||
use http_body_util::BodyExt;
|
||||
use hyper::{
|
||||
Method, Request, StatusCode,
|
||||
body::{self, Incoming},
|
||||
service::Service,
|
||||
};
|
||||
use log::{debug, error, warn};
|
||||
use tokio::{net::TcpStream, sync::Mutex};
|
||||
|
||||
use crate::{
|
||||
config::{Client, Config},
|
||||
db::{BoxyDatabase, Endpoint},
|
||||
server::{GeneralResponse, TcpIntercept, custom_resp, default_response, json_to_vec},
|
||||
};
|
||||
|
||||
// The routes itself
|
||||
#[async_trait]
|
||||
pub trait Route<T>
|
||||
where
|
||||
T: Matcher,
|
||||
{
|
||||
fn matcher(&self, m: &T, req: &Request<Incoming>) -> bool;
|
||||
async fn call(&self, m: &T, parts: Parts) -> Result<GeneralResponse, hyper::Error>;
|
||||
}
|
||||
|
||||
// Matcher, essentially just a router that contains routes and some other features
|
||||
#[async_trait]
|
||||
pub trait Matcher: Clone + Send + Sync + 'static {
|
||||
// Essentially a kind of "middleware", a universal matcher. If it doesn't match, it won't
|
||||
// route.
|
||||
async fn unimatch(
|
||||
&mut self,
|
||||
req: &Request<Incoming>,
|
||||
) -> (bool, Option<Result<GeneralResponse, hyper::Error>>);
|
||||
|
||||
// Return list of routes associated with self matcher
|
||||
fn retrieve(&self) -> Vec<Arc<dyn Route<Self> + Sync + Send>>;
|
||||
|
||||
// Wrap self into matcher service
|
||||
fn service(self) -> MatcherService<Self> {
|
||||
MatcherService::new(self)
|
||||
}
|
||||
|
||||
// Do something with TCP stream
|
||||
fn stream(&mut self, stream: &TcpStream) {}
|
||||
|
||||
// Body parser - made universal for api server cause lazy
|
||||
async fn body(&mut self, body: Incoming) -> Option<Result<GeneralResponse, hyper::Error>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper service, wraps matcher into a service
|
||||
#[derive(Clone)]
|
||||
pub struct MatcherService<T>
|
||||
where
|
||||
T: Matcher,
|
||||
{
|
||||
inner: T,
|
||||
}
|
||||
|
||||
impl<T> MatcherService<T>
|
||||
where
|
||||
T: Matcher,
|
||||
{
|
||||
pub fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Service<Request<Incoming>> for MatcherService<T>
|
||||
where
|
||||
T: Matcher,
|
||||
{
|
||||
type Response = GeneralResponse;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
fn call(&self, req: Request<Incoming>) -> Self::Future {
|
||||
let mut matcher = self.inner.clone();
|
||||
|
||||
Box::pin(async move {
|
||||
let unimatched = matcher.unimatch(&req).await;
|
||||
|
||||
if !unimatched.0 {
|
||||
match unimatched.1 {
|
||||
Some(x) => {
|
||||
return x;
|
||||
}
|
||||
None => {
|
||||
return Ok(custom_resp(
|
||||
StatusCode::NOT_FOUND,
|
||||
"Could not match route".to_string(),
|
||||
)
|
||||
.await);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for r in matcher.retrieve() {
|
||||
if r.matcher(&matcher, &req) {
|
||||
let (parts, body) = req.into_parts();
|
||||
|
||||
if let Some(resp) = matcher.body(body).await {
|
||||
return resp;
|
||||
}
|
||||
|
||||
return r.call(&matcher, parts).await;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(default_response().await)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TcpIntercept for MatcherService<T>
|
||||
where
|
||||
T: Matcher,
|
||||
{
|
||||
fn stream(&mut self, stream: &TcpStream) {
|
||||
self.inner.stream(stream);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue