mirror of
https://github.com/DefectingCat/candy
synced 2025-07-15 16:51:34 +00:00
refactor: remove useless macro
This commit is contained in:
@ -7,6 +7,7 @@ use axum::{
|
|||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::Host;
|
use axum_extra::extract::Host;
|
||||||
|
use dashmap::mapref::one::Ref;
|
||||||
use http::{
|
use http::{
|
||||||
HeaderName, HeaderValue, StatusCode, Uri,
|
HeaderName, HeaderValue, StatusCode, Uri,
|
||||||
header::{CONTENT_TYPE, ETAG, IF_NONE_MATCH},
|
header::{CONTENT_TYPE, ETAG, IF_NONE_MATCH},
|
||||||
@ -17,6 +18,7 @@ use tokio::fs::File;
|
|||||||
use tokio_util::io::ReaderStream;
|
use tokio_util::io::ReaderStream;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
config::SettingRoute,
|
||||||
http::serve::{calculate_etag, resolve_parent_path},
|
http::serve::{calculate_etag, resolve_parent_path},
|
||||||
utils::parse_port_from_host,
|
utils::parse_port_from_host,
|
||||||
};
|
};
|
||||||
@ -26,83 +28,102 @@ use super::{
|
|||||||
error::{RouteError, RouteResult},
|
error::{RouteError, RouteResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! custom_not_found {
|
/// 处理自定义错误页面(如404、500等)的请求
|
||||||
($host_config:expr, $request:expr, $is_error_page:expr) => {{
|
///
|
||||||
let page = if $is_error_page {
|
/// 该函数根据配置信息加载自定义错误页面文件,并根据HTTP缓存机制
|
||||||
$host_config
|
/// 决定是返回完整内容还是304 Not Modified状态码。
|
||||||
.error_page
|
///
|
||||||
.as_ref()
|
/// # 参数
|
||||||
.ok_or(RouteError::RouteNotFound())?
|
/// - `host_config`: 主机路由配置,包含错误页面路径和根目录信息
|
||||||
} else {
|
/// - `request`: 原始HTTP请求
|
||||||
$host_config
|
/// - `is_error_page`: 是否为错误页面(true: 错误页,false: 404页)
|
||||||
.not_found_page
|
///
|
||||||
.as_ref()
|
/// # 返回
|
||||||
.ok_or(RouteError::RouteNotFound())?
|
/// - `Ok(Response)`: 成功时返回HTTP响应
|
||||||
};
|
/// - `Err(RouteError)`: 失败时返回路由错误
|
||||||
let root = $host_config
|
pub async fn handle_custom_page(
|
||||||
.root
|
host_config: Ref<'_, String, SettingRoute>,
|
||||||
|
request: Request<Body>,
|
||||||
|
is_error_page: bool,
|
||||||
|
) -> RouteResult<Response<Body>> {
|
||||||
|
// 根据请求类型选择相应的页面配置
|
||||||
|
let page = if is_error_page {
|
||||||
|
host_config
|
||||||
|
.error_page
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.ok_or(RouteError::InternalError())?;
|
.ok_or(RouteError::RouteNotFound())?
|
||||||
let path = format!("{}/{}", root, page.page);
|
} else {
|
||||||
tracing::debug!("custom not found path: {:?}", path);
|
host_config
|
||||||
|
.not_found_page
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(RouteError::RouteNotFound())?
|
||||||
|
};
|
||||||
|
|
||||||
let file = File::open(path.clone())
|
// 获取站点根目录配置
|
||||||
.await
|
let root = host_config
|
||||||
.with_context(|| "open file failed")?;
|
.root
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(RouteError::InternalError())?;
|
||||||
|
|
||||||
let etag = calculate_etag(&file, path.as_str()).await?;
|
// 构建完整文件路径
|
||||||
let mut response = Response::builder();
|
let path = format!("{}/{}", root, page.page);
|
||||||
let mut not_modified = false;
|
tracing::debug!("custom not found path: {:?}", path);
|
||||||
|
|
||||||
// if let Some(if_none_match) = $request.headers().get(IF_NONE_MATCH)
|
// 打开文件并计算ETag用于缓存验证
|
||||||
// && if_none_match
|
let file = File::open(path.clone())
|
||||||
// .to_str()
|
.await
|
||||||
// .with_context(|| "parse if-none-match failed")?
|
.with_context(|| "open file failed")?;
|
||||||
// == etag
|
|
||||||
// {
|
let etag = calculate_etag(&file, path.as_str()).await?;
|
||||||
// response = response.status(StatusCode::NOT_MODIFIED);
|
let mut response = Response::builder();
|
||||||
// not_modified = true;
|
let mut not_modified = false;
|
||||||
// };
|
|
||||||
if let Some(if_none_match) = $request.headers().get(IF_NONE_MATCH) {
|
// 检查客户端缓存验证头(If-None-Match)
|
||||||
if let Ok(if_none_match_str) = if_none_match.to_str() {
|
if let Some(if_none_match) = request.headers().get(IF_NONE_MATCH) {
|
||||||
if if_none_match_str == etag {
|
if let Ok(if_none_match_str) = if_none_match.to_str() {
|
||||||
response = response.status(StatusCode::NOT_MODIFIED);
|
if if_none_match_str == etag {
|
||||||
not_modified = true;
|
// 资源未修改,返回304状态码
|
||||||
}
|
response = response.status(StatusCode::NOT_MODIFIED);
|
||||||
|
not_modified = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let stream = if not_modified {
|
// 准备响应主体
|
||||||
let empty = File::open(PathBuf::from("/dev/null"))
|
let stream = if not_modified {
|
||||||
.await
|
// 304响应返回空内容
|
||||||
.with_context(|| "open /dev/null failed")?;
|
let empty = File::open(PathBuf::from("/dev/null"))
|
||||||
ReaderStream::new(empty)
|
.await
|
||||||
} else {
|
.with_context(|| "open /dev/null failed")?;
|
||||||
ReaderStream::new(file)
|
ReaderStream::new(empty)
|
||||||
};
|
} else {
|
||||||
let body = Body::from_stream(stream);
|
// 正常响应返回文件内容
|
||||||
|
ReaderStream::new(file)
|
||||||
|
};
|
||||||
|
let body = Body::from_stream(stream);
|
||||||
|
|
||||||
let mime = from_path(path).first_or_octet_stream();
|
// 设置响应头:内容类型和ETag
|
||||||
response
|
let mime = from_path(path).first_or_octet_stream();
|
||||||
.headers_mut()
|
response
|
||||||
.with_context(|| "insert header failed")?
|
.headers_mut()
|
||||||
.insert(
|
.with_context(|| "insert header failed")?
|
||||||
CONTENT_TYPE,
|
.insert(
|
||||||
HeaderValue::from_str(mime.as_ref()).with_context(|| "insert header failed")?,
|
CONTENT_TYPE,
|
||||||
);
|
HeaderValue::from_str(mime.as_ref()).with_context(|| "insert header failed")?,
|
||||||
response
|
);
|
||||||
.headers_mut()
|
response
|
||||||
.with_context(|| "insert header failed")?
|
.headers_mut()
|
||||||
.insert(
|
.with_context(|| "insert header failed")?
|
||||||
ETAG,
|
.insert(
|
||||||
HeaderValue::from_str(&etag).with_context(|| "insert header failed")?,
|
ETAG,
|
||||||
);
|
HeaderValue::from_str(&etag).with_context(|| "insert header failed")?,
|
||||||
|
);
|
||||||
|
|
||||||
let response = response
|
// 构建最终响应
|
||||||
.body(body)
|
let response = response
|
||||||
.with_context(|| "Failed to build HTTP response with body")?;
|
.body(body)
|
||||||
Ok(response)
|
.with_context(|| "Failed to build HTTP response with body")?;
|
||||||
}};
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles the reverse proxy logic for incoming requests.
|
/// Handles the reverse proxy logic for incoming requests.
|
||||||
@ -146,7 +167,7 @@ pub async fn serve(
|
|||||||
.ok_or(RouteError::RouteNotFound())?;
|
.ok_or(RouteError::RouteNotFound())?;
|
||||||
tracing::debug!("proxy pass: {:?}", proxy_config);
|
tracing::debug!("proxy pass: {:?}", proxy_config);
|
||||||
let Some(ref proxy_pass) = proxy_config.proxy_pass else {
|
let Some(ref proxy_pass) = proxy_config.proxy_pass else {
|
||||||
return custom_not_found!(proxy_config, req, true);
|
return handle_custom_page(proxy_config, req, true).await;
|
||||||
};
|
};
|
||||||
let uri = format!("{proxy_pass}{path_query}");
|
let uri = format!("{proxy_pass}{path_query}");
|
||||||
tracing::debug!("reverse proxy uri: {:?}", &uri);
|
tracing::debug!("reverse proxy uri: {:?}", &uri);
|
||||||
|
@ -26,46 +26,54 @@ use crate::{
|
|||||||
|
|
||||||
use super::error::RouteResult;
|
use super::error::RouteResult;
|
||||||
|
|
||||||
/// Macro to handle custom "not found" responses for a route.
|
/// 处理自定义页面请求(如404错误页或自定义错误页面)
|
||||||
///
|
///
|
||||||
/// When a requested file is not found, this macro:
|
/// 此函数根据请求类型(错误页或404页)加载相应的自定义页面,
|
||||||
/// 1. Checks if the `host_route` has a configured `not_found_page`.
|
/// 构建完整文件路径并尝试流式传输文件内容作为HTTP响应。
|
||||||
/// 2. Attempts to serve the custom "not found" file (e.g., `404.html`).
|
|
||||||
/// 3. Falls back to `RouteNotFound` or `InternalError` if the file is missing or unreadable.
|
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # 参数
|
||||||
/// - `$host_route`: The route configuration containing `not_found_page` and `root` paths.
|
/// - `host_route`: 主机路由配置引用,包含页面位置和根目录信息
|
||||||
macro_rules! custom_not_found {
|
/// - `request`: 原始HTTP请求
|
||||||
($host_route:expr, $request:expr, $is_error_page:expr) => {
|
/// - `is_error_page`: 是否为错误页面(true: 错误页,false: 404页)
|
||||||
async {
|
///
|
||||||
let page = if $is_error_page {
|
/// # 返回
|
||||||
$host_route
|
/// - `RouteResult<Response>`: 成功时返回HTTP响应,失败时返回路由错误
|
||||||
.error_page
|
async fn custom_page(
|
||||||
.as_ref()
|
host_route: Ref<'_, String, SettingRoute>,
|
||||||
.ok_or(RouteError::RouteNotFound())?
|
request: Request<Body>,
|
||||||
} else {
|
is_error_page: bool,
|
||||||
$host_route
|
) -> RouteResult<Response<Body>> {
|
||||||
.not_found_page
|
let page = if is_error_page {
|
||||||
.as_ref()
|
host_route
|
||||||
.ok_or(RouteError::RouteNotFound())?
|
.error_page
|
||||||
};
|
.as_ref()
|
||||||
let root = $host_route
|
.ok_or(RouteError::RouteNotFound())?
|
||||||
.root
|
} else {
|
||||||
.as_ref()
|
host_route
|
||||||
.ok_or(RouteError::InternalError())?;
|
.not_found_page
|
||||||
let path = format!("{}/{}", root, page.page);
|
.as_ref()
|
||||||
let status = StatusCode::from_str(page.status.to_string().as_ref())
|
.ok_or(RouteError::RouteNotFound())?
|
||||||
.map_err(|_| RouteError::BadRequest())?;
|
|
||||||
tracing::debug!("custom not found path: {:?}", path);
|
|
||||||
match stream_file(path.into(), $request, Some(status)).await {
|
|
||||||
Ok(res) => RouteResult::Ok(res),
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to stream file: {:?}", e);
|
|
||||||
RouteResult::Err(RouteError::InternalError())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let root = host_route
|
||||||
|
.root
|
||||||
|
.as_ref()
|
||||||
|
.ok_or(RouteError::InternalError())?;
|
||||||
|
|
||||||
|
let path = format!("{}/{}", root, page.page);
|
||||||
|
|
||||||
|
let status = StatusCode::from_str(page.status.to_string().as_ref())
|
||||||
|
.map_err(|_| RouteError::BadRequest())?;
|
||||||
|
|
||||||
|
tracing::debug!("custom not found path: {:?}", path);
|
||||||
|
|
||||||
|
match stream_file(path.into(), request, Some(status)).await {
|
||||||
|
Ok(res) => RouteResult::Ok(res),
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to stream file: {:?}", e);
|
||||||
|
RouteResult::Err(RouteError::InternalError())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serve static files.
|
/// Serve static files.
|
||||||
@ -122,7 +130,7 @@ pub async fn serve(
|
|||||||
// check static file root configuration
|
// check static file root configuration
|
||||||
// if root is None, then return InternalError
|
// if root is None, then return InternalError
|
||||||
let Some(ref root) = host_route.root else {
|
let Some(ref root) = host_route.root else {
|
||||||
return custom_not_found!(host_route, request, true).await;
|
return custom_page(host_route, request, true).await;
|
||||||
};
|
};
|
||||||
// try find index file first
|
// try find index file first
|
||||||
// build index filename as vec
|
// build index filename as vec
|
||||||
@ -164,7 +172,7 @@ pub async fn serve(
|
|||||||
return Ok((headers, list_html).into_response());
|
return Ok((headers, list_html).into_response());
|
||||||
} else {
|
} else {
|
||||||
debug!("No valid file found in path candidates");
|
debug!("No valid file found in path candidates");
|
||||||
return custom_not_found!(host_route, request, false).await;
|
return custom_page(host_route, request, false).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user