zero4rs 2.0.0

zero4rs is a powerful, pragmatic, and extremely fast web framework for Rust
Documentation
use crate::prelude2::*;

use std::time::Duration;

use actix_web::dev;
use actix_web::http::header::LOCATION;

use actix_extensible_rate_limit::backend::memory::InMemoryBackend;
use actix_extensible_rate_limit::backend::SimpleInput;
use actix_extensible_rate_limit::backend::SimpleInputFunctionBuilder;
use actix_extensible_rate_limit::backend::SimpleOutput;
use actix_extensible_rate_limit::RateLimiter;

// Return an opaque 400 while preserving the error's root cause for logging.
pub fn e400<T>(e: T) -> actix_web::Error
where
    T: std::fmt::Debug + std::fmt::Display + 'static,
{
    log::error!("e400: error={:?}", e);
    actix_web::error::ErrorBadRequest(e)
}

// Return an opaque 500 while preserving the error's root cause for logging.
pub fn e500<T>(e: T) -> actix_web::Error
where
    T: std::fmt::Debug + std::fmt::Display + 'static,
{
    log::error!("e500: error={:?}", e);
    actix_web::error::ErrorInternalServerError(e)
}

pub fn see_other(location: &str) -> HttpResponse {
    HttpResponse::SeeOther()
        .insert_header((LOCATION, location))
        .finish()
}

pub fn found(location: &str) -> HttpResponse {
    HttpResponse::Found()
        .insert_header((LOCATION, location))
        .finish()
}

pub fn send_redirect(location: &str) -> HttpResponse {
    HttpResponse::Found()
        .insert_header((LOCATION, location))
        .finish()
}

pub fn send_forward(location: &str) -> HttpResponse {
    HttpResponse::SeeOther()
        .insert_header((LOCATION, location))
        .finish()
}

pub fn normalize_path_config() -> actix_web::middleware::NormalizePath {
    actix_web::middleware::NormalizePath::new(actix_web::middleware::TrailingSlash::Trim)
}

#[rustfmt::skip]
pub fn cors() -> actix_cors::Cors {

    let h1 = actix_web::http::header::HeaderName::from_lowercase(b"csrf-token").unwrap();
    let h3 = actix_web::http::header::HeaderName::from_lowercase(b"x-csrf-token").unwrap();
    let h4 = actix_web::http::header::HeaderName::from_lowercase(b"cookie").unwrap();
    let h5 = actix_web::http::header::HeaderName::from_lowercase(b"x-requested-with").unwrap();
    let h6 = actix_web::http::header::HeaderName::from_lowercase(b"etag").unwrap();

    actix_cors::Cors // ..
        // ::permissive()
        ::default()
        // .allowed_origin("https://www.rust-lang.org")
        .allow_any_origin()
        // .allowed_origin_fn(|origin, _req_head| origin.as_bytes().ends_with(b".rust-lang.org"))
        .allowed_methods(vec!["GET", "POST", "DELETE", "PUT"])
        .allowed_headers(vec![
            actix_web::http::header::CONTENT_TYPE,
            actix_web::http::header::AUTHORIZATION,
            actix_web::http::header::ACCEPT,
            actix_web::http::header::COOKIE,
            h1.clone(),
            h3.clone(),
        ])
        // Access-Control-Expose-Headers: x-csrf-token, csrf-token
        .expose_headers(vec![h1.clone(), h3.clone(), h4.clone(), h5.clone(), h6.clone()])
        .supports_credentials() // Access-Control-Allow-Credentials: true
        .max_age(3600)
}

#[rustfmt::skip]
pub fn default_headers() -> actix_web::middleware::DefaultHeaders {

    let mut default_headers = actix_web::middleware::DefaultHeaders::new().add(("X-Version", "1.0"));

    // armor::armor(&mut secure_headers);
    default_headers = default_headers.add(("X-DNS-Prefetch-Control", "on"));
    default_headers = default_headers.add(("X-Content-Type-Options", "nosniff"));
    default_headers = default_headers.add(("X-Frame-Options", "sameorigin"));
    default_headers = default_headers.add(("Strict-Transport-Security", "max-age=5184000"));
    default_headers = default_headers.add(("X-XSS-Protection", "1; mode=block"));
    // default_headers = default_headers.add(("Connection", "keep-alive"));
    // default_headers = default_headers.add(("Keep-Alive", "timeout=60, max=100"));
    // default_headers = default_headers.remove("X-Powered-By");

    default_headers
}

#[rustfmt::skip]
pub fn access_limiter() -> RateLimiter<
    InMemoryBackend,
    SimpleOutput,
    impl Fn(&dev::ServiceRequest) -> std::future::Ready<std::result::Result<SimpleInput, actix_web::Error>>,
> {
    RateLimiter::builder(
        InMemoryBackend::builder().build(),
        SimpleInputFunctionBuilder::new(Duration::from_secs(1), 100)
            .real_ip_key()
            .build(),
    )
    .add_headers()
    .build()
}

// QueryString 参数验证
pub fn query_error_handler() -> actix_web::web::QueryConfig {
    actix_web::web::QueryConfig::default().error_handler(|err, _| {
        let r = R::failed(4137, err.to_string());
        let r = actix_web::HttpResponse::Ok().json(r);
        actix_web::error::InternalError::from_response(err, r).into()
    })
}

// Customize your error in the handler
pub fn path_variable_error_handler() -> actix_web::web::PathConfig {
    actix_web::web::PathConfig::default().error_handler(|err, _| {
        let r = R::failed(4134, err.to_string());
        let r = actix_web::HttpResponse::Ok().json(r);
        actix_web::error::InternalError::from_response(err, r).into()
    })
}

pub fn post_json_error_handler() -> actix_web::web::JsonConfig {
    actix_web::web::JsonConfig::default()
        .limit(2_097_152 * 100) // 200m
        .error_handler(|err, _| {
            let r = R::failed(4133, err.to_string());
            let r = actix_web::HttpResponse::Ok().json(r);
            actix_web::error::InternalError::from_response(err, r).into()
        })
}

pub fn post_form_error_handler() -> actix_web::web::FormConfig {
    actix_web::web::FormConfig::default().error_handler(|err, _| {
        let r = R::failed(4132, err.to_string());
        let r = actix_web::HttpResponse::Ok().json(r);
        actix_web::error::InternalError::from_response(err, r).into()
    })
}

pub fn multipart_form_error_handler() -> actix_multipart::form::MultipartFormConfig {
    actix_multipart::form::MultipartFormConfig::default().error_handler(|err, _| {
        let r = R::failed(4131, err.to_string());
        let r = actix_web::HttpResponse::Ok().json(r);
        actix_web::error::InternalError::from_response(err, r).into()
    })
}

pub fn get_header_value(request: &HttpRequest, key: &str) -> String {
    match request.headers().get(key) {
        Some(val) => val.to_str().unwrap_or_default().to_string(),
        _ => "".to_string(),
    }
}