chore: add reject_filters

This commit is contained in:
Jesús Pérez Lorenzo 2021-09-01 19:03:39 +01:00
parent c5bfc9df7f
commit ef85f32cb6
5 changed files with 235 additions and 0 deletions

10
reject_filters/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
/target
target
Cargo.lock
.cache
.temp
.env
*.log
.DS_Store
logs
tmp

14
reject_filters/Cargo.toml Normal file
View File

@ -0,0 +1,14 @@
[package]
name = "reject_filters"
version = "0.1.0"
authors = ["JesusPerez <jpl@jesusperez.pro>"]
edition = "2018"
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_derive = "1.0.125"
warp = { version = "0.3", features = ["default","websocket","tls","compression"] }
app_env = { version = "0.1.0", path = "../../defs/app_env" }

5
reject_filters/README.md Normal file
View File

@ -0,0 +1,5 @@
# App reject filters library
Filters as configure routes and webservices for [warp](https://github.com/seanmonstar/warp)
As default fallback for **warp routes**

5
reject_filters/TODO.md Normal file
View File

@ -0,0 +1,5 @@
# App reject filters library
- [ ] Conditional **boxed** with cargo build for production
[Warp and compile times](https://rust-classes.com/chapter_6_3.html)

201
reject_filters/src/lib.rs Normal file
View File

@ -0,0 +1,201 @@
use serde_derive::{Serialize};
//use std::convert::Infallible;
use std::error::Error;
use warp::{
http::{StatusCode},
// http::{method::Method, HeaderMap, HeaderValue},
// http::{HeaderMap, HeaderValue},
// filters::header::headers_cloned,
// filters::method::method,
// filters::BoxedFilter,
Filter,
Rejection, Reply,
};
use app_env::AppStore;
/// An API error serializable to JSON.
#[derive(Serialize)]
struct ErrorMessage {
code: u16,
message: String,
}
/*
#[must_use]
#[allow(clippy::missing_errors_doc)]
pub fn static_filter(
static_path: &String
) -> BoxedFilter<(impl warp::Reply,)> {
warp::fs::dir(&static_path).with(warp::compression::gzip())
.boxed()
}
*/
/*
#[allow(clippy::missing_errors_doc)]
pub fn rejection(
err: Rejection,
db: AppStore,
) -> impl Filter<Extract = impl Reply, Error = Rejection> + Clone {
headers_cloned()
.and(method())
.and(handle_rejection)
.and(with_db(db))
.and_then(handle_rejection_filter)
}
*/
// This function receives a `Rejection` and tries to return a custom
// value, otherwise simply passes the rejection along.
#[allow(clippy::missing_errors_doc)]
pub async fn handle_rejection_filter(
reply: impl warp::Reply,
// full_path: warp::path::FullPath,
// header: HeaderMap<HeaderValue>,
db: AppStore,
) -> Result<impl Reply, Rejection> {
// dbg!(&db);
// dbg!(&header);
// dbg!(&full_path);
let response = reply.into_response();
let status_code = &response.status();
//if let Some(message) = response.headers().get("x-message") {
let message = match &response.headers().get("x-message") {
Some(msg) => format!("{:?}",&msg),
//warp::http::HeaderValue::from("NOT_FOUND") => println!("No encontrado"),
None => String::from("Unknown"),
};
let result =format!("StatusCode: {} -> Message: {}", &status_code, &message);
let app = db.app_data.read();
let fallback = |_: &str| {
Ok(warp::http::Response::builder()
.body(result.to_string())
.into_response())
// Ok(warp::reply::with_header(
// warp::http::Response::new(result),
// "Access-Control-Allow-Origin",
// "http://localhost:3333"))
};
let tera = app.tera.to_owned();
let mut context = app.ctx.to_owned();
context.insert("status_code", &status_code.to_string());
let template = match status_code.as_str() {
"404" => "error404.html",
_ => "error.html",
};
match tera.render(&template, &context) {
Ok(page) =>
Ok(warp::http::Response::builder()
.body(page.to_string())
.into_response()),
// Ok(warp::reply::with_header(
// warp::http::Response::new(page),
// "Access-Control-Allow-Origin",
// "http://localhost:3333")),
Err(_) => fallback("Page not found"),
}
}
#[allow(clippy::missing_errors_doc)]
pub async fn handle_rejection(
err: Rejection,
// header: HeaderMap<HeaderValue>,
db: AppStore,
// full_path: warp::path::FullPath,
) -> Result<impl warp::Reply, Rejection> {
// ) -> Result<impl Reply, Infallible> {
// dbg!(&header);
// dbg!(&err);
// dbg!(&full_path);
let code;
let message;
if err.is_not_found() {
code = StatusCode::NOT_FOUND;
message = "NOT_FOUND";
} else if let Some(e) = err.find::<warp::filters::body::BodyDeserializeError>() {
// This error happens if the body could not be deserialized correctly
// We can use the cause to analyze the error and customize the error message
message = match e.source() {
Some(cause) => {
if cause.to_string().contains("denom") {
"FIELD_ERROR: denom"
} else {
"BAD_REQUEST"
}
}
None => "BAD_REQUEST",
};
code = StatusCode::BAD_REQUEST;
} else if let Some(_) = err.find::<warp::reject::MethodNotAllowed>() {
// We can handle a specific error, here METHOD_NOT_ALLOWED,
// and render it however we want
code = StatusCode::METHOD_NOT_ALLOWED;
message = "METHOD_NOT_ALLOWED";
} else {
// We should have expected this... Just log and say its a 500
eprintln!("unhandled rejection: {:?}", err);
code = StatusCode::INTERNAL_SERVER_ERROR;
message = "UNHANDLED_REJECTION";
}
// let json = warp::reply::json(&ErrorMessage {
// code: code.as_u16(),
// message: message.into(),
// });
let result =format!("StatusCode: {} -> Message: {}", &code, &message);
let app = db.app_data.read();
// let fallback = |e: &str| {
// warp::reply::with_header(
// warp::http::Response::new(result),
// "Access-Control-Allow-Origin",
// "http://localhost:3333")
// };
let tera = app.tera.to_owned();
let mut context = app.ctx.to_owned();
context.insert("status_code", &code.to_string());
let template = match code.as_str() {
"404" => "error404.html",
_ => "error.html",
};
let reply = match tera.render(&template, &context) {
Ok(page) =>
warp::http::Response::builder()
.status(code)
.header("X-Message", message)
.body(page)
.into_response(),
/*
Ok(warp::reply::with_header(
warp::http::Response::new(page),
"Access-Control-Allow-Origin",
"http://localhost:3333")),
*/
Err(_) =>
warp::http::Response::builder()
.status(code)
.header("X-Message", message)
.body(result)
.into_response(),
};
Ok(warp::reply::with_status(reply,code))
/*
let res = warp::http::Response::builder()
.status(code)
.header("X-Message", message)
.body(message.to_owned())
.into_response();
Ok(warp::reply::with_status(res, code))
*/
}
#[must_use]
pub fn with_app_db(db: AppStore) -> impl Filter<Extract = (AppStore,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || db.clone())
}