This commit is contained in:
2022-07-09 07:25:37 +00:00
committed by GitHub
parent 0f580c4441
commit c0d61e8a5b
10 changed files with 244 additions and 15 deletions

14
src/server/context.rs Normal file
View File

@@ -0,0 +1,14 @@
use super::cors::CorsContext;
use std::default::Default;
pub struct ServerContext {
pub cors: CorsContext,
}
impl Default for ServerContext {
fn default() -> Self {
Self {
cors: CorsContext::default(),
}
}
}

View File

@@ -1,10 +1,13 @@
use crate::gettext;
use crate::opthelper::get_helper;
use http::uri::InvalidUri;
use http::uri::Scheme;
use http::Uri;
use json::JsonValue;
use std::cmp::PartialEq;
use std::convert::From;
use std::convert::TryFrom;
use std::default::Default;
use std::fmt::Display;
use std::net::SocketAddr;
use std::str::FromStr;
@@ -165,6 +168,28 @@ impl TryFrom<String> for CorsEntry {
}
}
impl TryFrom<&JsonValue> for CorsEntry {
type Error = CorsError;
fn try_from(value: &JsonValue) -> Result<Self, Self::Error> {
match value.as_str() {
Some(s) => Self::try_from(s),
None => Err(CorsError::from(gettext("Object is not a string."))),
}
}
}
pub fn parse_cors_entries(v: &JsonValue) -> Result<Vec<CorsEntry>, CorsError> {
if v.is_array() {
let mut list = Vec::new();
for i in v.members() {
list.push(CorsEntry::try_from(i)?);
}
Ok(list)
} else {
Err(CorsError::from(gettext("Object is not an array.")))
}
}
#[derive(Clone, Debug, PartialEq)]
/// Current host
pub struct CorsHost {
@@ -322,6 +347,75 @@ impl From<&str> for CorsError {
}
}
#[derive(Clone, Debug, derive_more::Display, PartialEq, Eq, PartialOrd, Ord)]
pub enum CorsResult {
Allowed,
AllowedAll,
NotAllowed,
}
pub struct CorsContext {
allow_all: bool,
entries: Vec<CorsEntry>,
hosts: Vec<CorsHost>,
}
impl CorsContext {
pub fn matches<T: Clone>(&self, host: T) -> CorsResult
where
CorsEntry: PartialEq<T>,
CorsHost: PartialEq<T>,
{
if self.is_in_hosts(host.clone()) || self.is_in_entries(host.clone()) {
CorsResult::Allowed
} else if self.is_all_allowed() {
CorsResult::AllowedAll
} else {
CorsResult::NotAllowed
}
}
/// Returns true if every domain allowed
pub fn is_all_allowed(&self) -> bool {
self.allow_all
}
pub fn is_in_entries<T>(&self, host: T) -> bool
where
CorsEntry: PartialEq<T>,
{
for ent in self.entries.iter() {
if *ent == host {
return true;
}
}
false
}
pub fn is_in_hosts<T>(&self, host: T) -> bool
where
CorsHost: PartialEq<T>,
{
for h in self.hosts.iter() {
if *h == host {
return true;
}
}
false
}
}
impl Default for CorsContext {
fn default() -> Self {
let helper = get_helper();
Self {
allow_all: false,
entries: helper.cors_entries(),
hosts: Vec::new(),
}
}
}
#[test]
fn test_cors_entry() {
let ent = CorsEntry::try_from("https://.test.com").unwrap();

View File

@@ -1,3 +1,4 @@
pub mod context;
/// CORS Handle
pub mod cors;
/// Routes

View File

@@ -1,8 +1,10 @@
use super::context::ServerContext;
use super::traits::MatchRoute;
use super::traits::ResponseFor;
use super::version::VersionRoute;
use hyper::Body;
use hyper::Request;
use std::sync::Arc;
pub type RouteType = dyn MatchRoute<Body, Body> + Send + Sync;
pub type ResponseForType = dyn ResponseFor<Body, Body> + Send + Sync;
@@ -18,10 +20,14 @@ impl ServerRoutes {
Self { routes }
}
pub fn match_route(&self, req: &Request<Body>) -> Option<Box<ResponseForType>> {
pub fn match_route(
&self,
req: &Request<Body>,
ctx: &Arc<ServerContext>,
) -> Option<Box<ResponseForType>> {
for i in self.routes.iter() {
if i.match_route(req) {
return Some(i.get_route());
return Some(i.get_route(Arc::clone(ctx)));
}
}
None

View File

@@ -1,3 +1,4 @@
use super::context::ServerContext;
use super::route::ServerRoutes;
use hyper::server::conn::AddrIncoming;
use hyper::server::Server;
@@ -13,12 +14,13 @@ use std::task::Context;
use std::task::Poll;
pub struct PixivDownloaderSvc {
context: Arc<ServerContext>,
routes: Arc<ServerRoutes>,
}
impl PixivDownloaderSvc {
pub fn new(routes: Arc<ServerRoutes>) -> Self {
Self { routes }
pub fn new(routes: Arc<ServerRoutes>, context: Arc<ServerContext>) -> Self {
Self { routes, context }
}
}
@@ -33,7 +35,7 @@ impl Service<Request<Body>> for PixivDownloaderSvc {
fn call(&mut self, req: Request<Body>) -> Self::Future {
println!("{} {}", req.method(), req.uri());
match self.routes.match_route(&req) {
match self.routes.match_route(&req, &self.context) {
Some(route) => Box::pin(async move {
match route.response(req).await {
Ok(data) => Ok(data),
@@ -54,12 +56,14 @@ impl Service<Request<Body>> for PixivDownloaderSvc {
}
pub struct PixivDownloaderMakeSvc {
context: Arc<ServerContext>,
routes: Arc<ServerRoutes>,
}
impl PixivDownloaderMakeSvc {
pub fn new() -> Self {
Self {
context: Arc::new(ServerContext::default()),
routes: Arc::new(ServerRoutes::new()),
}
}
@@ -76,7 +80,8 @@ impl<T> Service<T> for PixivDownloaderMakeSvc {
fn call(&mut self, _: T) -> Self::Future {
let routes = Arc::clone(&self.routes);
let fut = async move { Ok(PixivDownloaderSvc::new(routes)) };
let context = Arc::clone(&self.context);
let fut = async move { Ok(PixivDownloaderSvc::new(routes, context)) };
Box::pin(fut)
}
}

View File

@@ -1,11 +1,13 @@
use super::context::ServerContext;
use crate::error::PixivDownloaderError;
use hyper::Body;
use hyper::Request;
use hyper::Response;
use json::JsonValue;
use std::sync::Arc;
pub trait MatchRoute<T, R> {
fn get_route(&self) -> Box<dyn ResponseFor<T, R> + Send + Sync>;
fn get_route(&self, ctx: Arc<ServerContext>) -> Box<dyn ResponseFor<T, R> + Send + Sync>;
fn match_route(&self, req: &Request<T>) -> bool;
}

View File

@@ -1,3 +1,4 @@
use super::context::ServerContext;
use super::route::ResponseForType;
use super::traits::MatchRoute;
use super::traits::ResponseJsonFor;
@@ -8,14 +9,15 @@ use hyper::Response;
use json::JsonValue;
use proc_macros::filter_http_methods;
use regex::Regex;
use std::sync::Arc;
pub struct VersionContext {
_unused: [u8; 0],
ctx: Arc<ServerContext>,
}
impl VersionContext {
pub fn new() -> Self {
Self { _unused: [] }
pub fn new(ctx: Arc<ServerContext>) -> Self {
Self { ctx }
}
}
@@ -25,8 +27,10 @@ impl ResponseJsonFor<Body> for VersionContext {
&self,
req: Request<Body>,
) -> Result<Response<JsonValue>, PixivDownloaderError> {
filter_http_methods!(req, json::object! {}, true, GET, OPTIONS, POST);
Ok(Response::new(json::object! {"version": [0, 0, 1, 0]}))
filter_http_methods!(req, json::object! {}, true, self.ctx, GET, OPTIONS, POST);
Ok(builder
.body(json::object! {"version": [0, 0, 1, 0]})
.unwrap())
}
}
@@ -43,8 +47,8 @@ impl VersionRoute {
}
impl MatchRoute<Body, Body> for VersionRoute {
fn get_route(&self) -> Box<ResponseForType> {
Box::new(VersionContext::new())
fn get_route(&self, ctx: Arc<ServerContext>) -> Box<ResponseForType> {
Box::new(VersionContext::new(ctx))
}
fn match_route(&self, req: &http::Request<Body>) -> bool {