mirror of
https://github.com/lifegpc/pixiv_downloader.git
synced 2026-06-06 05:49:01 +08:00
server now support HttpBody trait
This commit is contained in:
@@ -261,6 +261,7 @@ struct FilterHttpMethods {
|
||||
pub cors_methods: Option<Vec<Ident>>,
|
||||
pub expose_headers: Option<Vec<String>>,
|
||||
pub cors_allow_headers: Option<Vec<String>>,
|
||||
pub typ_def: Option<syn::Type>,
|
||||
}
|
||||
|
||||
impl Parse for FilterHttpMethods {
|
||||
@@ -285,6 +286,7 @@ impl Parse for FilterHttpMethods {
|
||||
let mut cors_methods = None;
|
||||
let mut expose_headers = None;
|
||||
let mut cors_allow_headers = None;
|
||||
let mut typ_def = None;
|
||||
loop {
|
||||
if input.cursor().eof() {
|
||||
break;
|
||||
@@ -330,6 +332,9 @@ impl Parse for FilterHttpMethods {
|
||||
let m: HTTPHeader = content.parse()?;
|
||||
cors_allow_headers.as_mut().unwrap().push(m.header);
|
||||
}
|
||||
} else if method.to_string() == "typ_def" {
|
||||
token::Eq::parse(input)?;
|
||||
typ_def.replace(input.parse()?);
|
||||
} else {
|
||||
methods.push(method);
|
||||
}
|
||||
@@ -343,6 +348,7 @@ impl Parse for FilterHttpMethods {
|
||||
cors_methods,
|
||||
expose_headers,
|
||||
cors_allow_headers,
|
||||
typ_def,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -361,7 +367,12 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
|
||||
cors_methods,
|
||||
expose_headers,
|
||||
cors_allow_headers,
|
||||
typ_def,
|
||||
} = parse_macro_input!(item as FilterHttpMethods);
|
||||
let typ_def = match typ_def {
|
||||
Some(t) => Some(quote!(::<#t>)),
|
||||
None => None,
|
||||
};
|
||||
let mut header_value = Vec::new();
|
||||
let mut streams = Vec::new();
|
||||
let mut enable_options = false;
|
||||
@@ -429,7 +440,7 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
|
||||
.header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, #cors_methods_header);
|
||||
#expose_headers
|
||||
#cors_allow_headers
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body(#typ)?);
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body #typ_def(#typ)?);
|
||||
}
|
||||
crate::server::cors::CorsResult::AllowedAll => {
|
||||
let builder = builder
|
||||
@@ -437,15 +448,15 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
|
||||
.header(hyper::header::ACCESS_CONTROL_ALLOW_METHODS, #cors_methods_header);
|
||||
#expose_headers
|
||||
#cors_allow_headers
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body(#typ)?);
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body #typ_def(#typ)?);
|
||||
}
|
||||
_ => {
|
||||
return Ok(builder.status(400).header("Allow", #allow_header).body(#typ)?);
|
||||
return Ok(builder.status(400).header("Allow", #allow_header).body #typ_def(#typ)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body(#typ)?);
|
||||
return Ok(builder.status(200).header("Allow", #allow_header).body #typ_def(#typ)?);
|
||||
}
|
||||
}
|
||||
}));
|
||||
@@ -509,7 +520,7 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
|
||||
#cors_allow_headers
|
||||
}
|
||||
_ => {
|
||||
return Ok(builder.status(403).body(#typ)?);
|
||||
return Ok(builder.status(403).body #typ_def(#typ)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -523,7 +534,7 @@ pub fn filter_http_methods(item: TokenStream) -> TokenStream {
|
||||
match #req.method() {
|
||||
#(#streams)*
|
||||
_ => {
|
||||
return Ok(hyper::Response::builder().status(405).header("Allow", #allow_header).body(#typ)?)
|
||||
return Ok(hyper::Response::builder().status(405).header("Allow", #allow_header).body #typ_def(#typ)?)
|
||||
}
|
||||
}
|
||||
#post_stream
|
||||
@@ -889,3 +900,47 @@ pub fn call_parent_data_source_fun(item: TokenStream) -> TokenStream {
|
||||
);
|
||||
stream.into()
|
||||
}
|
||||
|
||||
struct HttpError {
|
||||
pub code: Option<LitInt>,
|
||||
pub expr: Expr,
|
||||
}
|
||||
|
||||
impl Parse for HttpError {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut code = None;
|
||||
match input.parse::<LitInt>() {
|
||||
Ok(c) => {
|
||||
code.replace(c);
|
||||
match input.parse::<token::Comma>() {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
panic!("Expr not found");
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
let expr = input.parse()?;
|
||||
Ok(Self { code, expr })
|
||||
}
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn http_error(item: TokenStream) -> TokenStream {
|
||||
let HttpError { code, expr } = parse_macro_input!(item as HttpError);
|
||||
let code = match code {
|
||||
Some(code) => code,
|
||||
None => LitInt::new("400", Span::call_site()),
|
||||
};
|
||||
let stream = quote!(
|
||||
match (#expr) {
|
||||
Ok(re) => re,
|
||||
Err(e) => {
|
||||
builder = builder.status(#code);
|
||||
return Ok(builder.body::<Pin<Box<HttpBodyType>>>(Box::pin(Body::from(format!("{}", e))))?);
|
||||
}
|
||||
}
|
||||
);
|
||||
stream.into()
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ impl AuthPubkeyRoute {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Body> for AuthPubkeyRoute {
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for AuthPubkeyRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
|
||||
@@ -43,7 +43,7 @@ impl AuthStatusRoute {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Body> for AuthStatusRoute {
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for AuthStatusRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
|
||||
@@ -216,7 +216,7 @@ impl AuthTokenRoute {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Body> for AuthTokenRoute {
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for AuthTokenRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
|
||||
@@ -507,7 +507,7 @@ impl AuthUserRoute {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Body> for AuthUserRoute {
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for AuthUserRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
|
||||
@@ -136,4 +136,17 @@ impl ServerContext {
|
||||
.await?
|
||||
.ok_or(gettext("No corresponding user was found."))?)
|
||||
}
|
||||
|
||||
pub async fn verify(
|
||||
&self,
|
||||
req: &Request<Body>,
|
||||
params: &RequestParams,
|
||||
) -> Result<Option<User>, PixivDownloaderError> {
|
||||
let root_user = self.db.get_user(0).await?;
|
||||
if root_user.is_some() {
|
||||
Ok(Some(self.verify_token(req, params).await?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ pub mod cors;
|
||||
pub mod params;
|
||||
/// Predefined includes
|
||||
pub mod preclude;
|
||||
/// Routes about proxy
|
||||
pub mod proxy;
|
||||
/// Base result type for JSON response
|
||||
pub mod result;
|
||||
/// Routes
|
||||
|
||||
@@ -4,11 +4,15 @@ pub use super::result::JSONResult;
|
||||
pub use super::route::ResponseForType;
|
||||
pub use super::traits::{GetRequestParams, MatchRoute, ResponseFor, ResponseJsonFor};
|
||||
pub use crate::error::PixivDownloaderError;
|
||||
pub use hyper::body::HttpBody;
|
||||
pub use hyper::Body;
|
||||
pub use hyper::Method;
|
||||
pub use hyper::Request;
|
||||
pub use hyper::Response;
|
||||
pub use json::JsonValue;
|
||||
pub use proc_macros::filter_http_methods;
|
||||
pub use proc_macros::{filter_http_methods, http_error};
|
||||
pub use regex::Regex;
|
||||
pub use std::pin::Pin;
|
||||
pub use std::sync::Arc;
|
||||
|
||||
pub type HttpBodyType = dyn HttpBody<Data = hyper::body::Bytes, Error = hyper::Error> + Send;
|
||||
|
||||
3
src/server/proxy/mod.rs
Normal file
3
src/server/proxy/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod pixiv;
|
||||
|
||||
pub use pixiv::ProxyPixivRoute;
|
||||
66
src/server/proxy/pixiv.rs
Normal file
66
src/server/proxy/pixiv.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
use super::super::preclude::*;
|
||||
use http::Uri;
|
||||
|
||||
pub struct ProxyPixivContext {
|
||||
ctx: Arc<ServerContext>,
|
||||
}
|
||||
|
||||
impl ProxyPixivContext {
|
||||
pub fn new(ctx: Arc<ServerContext>) -> Self {
|
||||
Self { ctx }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ResponseFor<Body, Pin<Box<HttpBodyType>>> for ProxyPixivContext {
|
||||
async fn response(
|
||||
&self,
|
||||
mut req: Request<Body>,
|
||||
) -> Result<Response<Pin<Box<HttpBodyType>>>, PixivDownloaderError> {
|
||||
filter_http_methods!(
|
||||
req,
|
||||
Box::pin(Body::empty()),
|
||||
true,
|
||||
self.ctx,
|
||||
allow_headers = [X_SIGN, X_TOKEN_ID],
|
||||
typ_def=Pin<Box<HttpBodyType>>,
|
||||
GET,
|
||||
OPTIONS
|
||||
);
|
||||
let params = req.get_params().await?;
|
||||
let _ = http_error!(401, self.ctx.verify(&req, ¶ms).await);
|
||||
let url = http_error!(params.get("url").ok_or("Url is required."));
|
||||
let uri = http_error!(Uri::try_from(url));
|
||||
let host = uri.host().ok_or("Host is needed.")?;
|
||||
if !host.ends_with(".pximg.net") {
|
||||
http_error!(403, Err("Host is not allowed."));
|
||||
}
|
||||
return Ok(builder.body::<Pin<Box<HttpBodyType>>>(Box::pin(Body::empty()))?);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProxyPixivRoute {
|
||||
regex: Regex,
|
||||
}
|
||||
|
||||
impl ProxyPixivRoute {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
regex: Regex::new(r"^(/+api)?/+proxy/+pixiv(/.*)?$").unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for ProxyPixivRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
req: &Request<Body>,
|
||||
) -> Option<Box<ResponseForType>> {
|
||||
if self.regex.is_match(req.uri().path()) {
|
||||
Some(Box::new(ProxyPixivContext::new(Arc::clone(ctx))))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
use super::auth::*;
|
||||
use super::context::ServerContext;
|
||||
use super::preclude::HttpBodyType;
|
||||
use super::proxy::*;
|
||||
use super::traits::MatchRoute;
|
||||
use super::traits::ResponseFor;
|
||||
use super::version::VersionRoute;
|
||||
use hyper::Body;
|
||||
use hyper::Request;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub type RouteType = dyn MatchRoute<Body, Body> + Send + Sync;
|
||||
pub type ResponseForType = dyn ResponseFor<Body, Body> + Send + Sync;
|
||||
pub type RouteType = dyn MatchRoute<Body, Pin<Box<HttpBodyType>>> + Send + Sync;
|
||||
pub type ResponseForType = dyn ResponseFor<Body, Pin<Box<HttpBodyType>>> + Send + Sync;
|
||||
|
||||
pub struct ServerRoutes {
|
||||
routes: Vec<Box<RouteType>>,
|
||||
@@ -22,6 +25,7 @@ impl ServerRoutes {
|
||||
routes.push(Box::new(AuthUserRoute::new()));
|
||||
routes.push(Box::new(AuthPubkeyRoute::new()));
|
||||
routes.push(Box::new(AuthTokenRoute::new()));
|
||||
routes.push(Box::new(ProxyPixivRoute::new()));
|
||||
Self { routes }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::context::ServerContext;
|
||||
use super::preclude::*;
|
||||
use super::route::ServerRoutes;
|
||||
use hyper::server::conn::AddrIncoming;
|
||||
use hyper::server::Server;
|
||||
use hyper::service::Service;
|
||||
use hyper::Body;
|
||||
use hyper::Request;
|
||||
use hyper::Response;
|
||||
use std::future::Future;
|
||||
@@ -25,7 +25,7 @@ impl PixivDownloaderSvc {
|
||||
}
|
||||
|
||||
impl Service<Request<Body>> for PixivDownloaderSvc {
|
||||
type Response = Response<Body>;
|
||||
type Response = Response<Pin<Box<HttpBodyType>>>;
|
||||
type Error = hyper::Error;
|
||||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
|
||||
|
||||
@@ -43,7 +43,9 @@ impl Service<Request<Body>> for PixivDownloaderSvc {
|
||||
println!("{}", e);
|
||||
Ok(Response::builder()
|
||||
.status(500)
|
||||
.body(Body::from("Internal server error"))
|
||||
.body::<Pin<Box<HttpBodyType>>>(Box::pin(Body::from(
|
||||
"Internal server error",
|
||||
)))
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
@@ -51,7 +53,7 @@ impl Service<Request<Body>> for PixivDownloaderSvc {
|
||||
None => Box::pin(async {
|
||||
Ok(Response::builder()
|
||||
.status(404)
|
||||
.body(Body::from("404 Not Found"))
|
||||
.body::<Pin<Box<HttpBodyType>>>(Box::pin(Body::from("404 Not Found")))
|
||||
.unwrap())
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use super::context::ServerContext;
|
||||
use super::params::RequestParams;
|
||||
use super::preclude::HttpBodyType;
|
||||
use crate::error::PixivDownloaderError;
|
||||
use hyper::Body;
|
||||
use hyper::Request;
|
||||
use hyper::Response;
|
||||
use json::JsonValue;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait MatchRoute<T, R> {
|
||||
@@ -36,18 +38,24 @@ pub trait GetRequestParams {
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T, U> ResponseFor<T, Body> for U
|
||||
impl<T, U> ResponseFor<T, Pin<Box<HttpBodyType>>> for U
|
||||
where
|
||||
U: ResponseJsonFor<T> + Sync + Send,
|
||||
T: Sync + Send + 'static,
|
||||
{
|
||||
async fn response(&self, req: Request<T>) -> Result<Response<Body>, PixivDownloaderError> {
|
||||
async fn response(
|
||||
&self,
|
||||
req: Request<T>,
|
||||
) -> Result<Response<Pin<Box<HttpBodyType>>>, PixivDownloaderError> {
|
||||
let re = self.response_json(req).await?;
|
||||
let (mut parts, body) = re.into_parts();
|
||||
parts.headers.insert(
|
||||
hyper::header::CONTENT_TYPE,
|
||||
"application/json; charset=utf-8".parse()?,
|
||||
);
|
||||
Ok(Response::from_parts(parts, Body::from(body.to_string())))
|
||||
Ok(Response::from_parts(
|
||||
parts,
|
||||
Box::pin(Body::from(body.to_string())),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ mod version;
|
||||
|
||||
use super::context::ServerContext;
|
||||
use super::cors::CorsContext;
|
||||
use super::preclude::HttpBodyType;
|
||||
use super::route::ServerRoutes;
|
||||
use crate::db::{open_and_init_database, PixivDownloaderDbConfig};
|
||||
use crate::error::PixivDownloaderError;
|
||||
@@ -14,6 +15,7 @@ use std::collections::BTreeMap;
|
||||
use std::fs::{create_dir, remove_file};
|
||||
#[cfg(test)]
|
||||
use std::path::Path;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct UnitTestContext {
|
||||
@@ -44,7 +46,7 @@ impl UnitTestContext {
|
||||
pub async fn request(
|
||||
&self,
|
||||
req: Request<Body>,
|
||||
) -> Result<Option<Response<Body>>, PixivDownloaderError> {
|
||||
) -> Result<Option<Response<Pin<Box<HttpBodyType>>>>, PixivDownloaderError> {
|
||||
Ok(match self.routes.match_route(&req, &self.ctx) {
|
||||
Some(r) => Some(r.response(req).await?),
|
||||
None => None,
|
||||
|
||||
@@ -44,7 +44,7 @@ impl VersionRoute {
|
||||
}
|
||||
}
|
||||
|
||||
impl MatchRoute<Body, Body> for VersionRoute {
|
||||
impl MatchRoute<Body, Pin<Box<HttpBodyType>>> for VersionRoute {
|
||||
fn match_route(
|
||||
&self,
|
||||
ctx: &Arc<ServerContext>,
|
||||
|
||||
Reference in New Issue
Block a user