This commit is contained in:
Mateusz Gruszczyński committed 2026-10-19 14:05:13 +02:00
1 parent 28e39f7ce4
commit b57beb83c7
34 files changed
+1037 -314

No files matched your search

+52 -25
View File
@@ -1,15 +1,14 @@
use super::{
config::CompiledCacheMatcher,
filesystem::{DirectoryEntry, Opened},
htaccess,
CompiledService,
htaccess, CompiledService,
};
use axum::{body::Body, response::Response};
use http::{
header::{
ACCEPT_RANGES, CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING,
CONTENT_LENGTH, CONTENT_RANGE, CONTENT_TYPE, ETAG, IF_MODIFIED_SINCE, IF_NONE_MATCH,
IF_RANGE, LAST_MODIFIED, LOCATION, RANGE, VARY,
ACCEPT_RANGES, CACHE_CONTROL, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LENGTH,
CONTENT_RANGE, CONTENT_TYPE, ETAG, IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE,
LAST_MODIFIED, LOCATION, RANGE, VARY,
},
HeaderMap, HeaderValue, Method, StatusCode, Uri,
};
@@ -21,13 +20,37 @@ use std::{
use tokio::io::{AsyncReadExt, AsyncSeekExt};
use tokio_util::io::ReaderStream;
pub(super) fn status_response(status: StatusCode, message: &'static str) -> Response {
let mut response = Response::new(Body::from(message));
pub(super) fn status_response(status: StatusCode, detail: &'static str) -> Response {
// Keep internal diagnostics in server logs, not in public HTTP bodies.
if status.is_server_error() {
tracing::warn!(
status = status.as_u16(),
detail = detail.trim_end(),
"Web Server error"
);
}
let message = format!(
"{} {}\n",
status.as_u16(),
status.canonical_reason().unwrap_or("Error")
);
let mut response = Response::new(Body::from(message.clone()));
*response.status_mut() = status;
response.headers_mut().insert(
CONTENT_TYPE,
HeaderValue::from_static("text/plain; charset=utf-8"),
);
response.headers_mut().insert(
CONTENT_LENGTH,
HeaderValue::from_str(&message.len().to_string()).unwrap(),
);
response
.headers_mut()
.insert(CACHE_CONTROL, HeaderValue::from_static("no-store"));
response.headers_mut().insert(
"x-content-type-options",
HeaderValue::from_static("nosniff"),
);
response
}
@@ -60,15 +83,20 @@ pub(super) fn builtin_error_response(status: StatusCode, method: &Method) -> Res
pub(super) fn io_response(error: &io::Error) -> Response {
#[cfg(unix)]
if matches!(error.raw_os_error(), Some(libc::ELOOP)) {
return status_response(StatusCode::FORBIDDEN, "RustProxy: Web Server path is not allowed\n");
return status_response(
StatusCode::FORBIDDEN,
"RustProxy: Web Server path is not allowed\n",
);
}
match error.kind() {
io::ErrorKind::NotFound | io::ErrorKind::NotADirectory => {
status_response(StatusCode::NOT_FOUND, "RustProxy: Web Server resource not found\n")
}
io::ErrorKind::PermissionDenied | io::ErrorKind::InvalidInput => {
status_response(StatusCode::FORBIDDEN, "RustProxy: Web Server path is not allowed\n")
}
io::ErrorKind::NotFound | io::ErrorKind::NotADirectory => status_response(
StatusCode::NOT_FOUND,
"RustProxy: Web Server resource not found\n",
),
io::ErrorKind::PermissionDenied | io::ErrorKind::InvalidInput => status_response(
StatusCode::FORBIDDEN,
"RustProxy: Web Server path is not allowed\n",
),
_ => status_response(
StatusCode::INTERNAL_SERVER_ERROR,
"RustProxy: Web Server filesystem error\n",
@@ -172,10 +200,7 @@ fn cache_control_for(
.and_then(|value| value.to_str())
.unwrap_or("")
.to_ascii_lowercase();
let mime = content_type
.to_str()
.unwrap_or("")
.to_ascii_lowercase();
let mime = content_type.to_str().unwrap_or("").to_ascii_lowercase();
for rule in &service.cache_rules {
let matches = match &rule.matcher {
CompiledCacheMatcher::Extension(value) => extension == *value,
@@ -216,8 +241,14 @@ fn add_download_header(response: &mut Response, service: &CompiledService, path:
if !download_extension(service, path) {
return;
}
let filename = path.file_name().and_then(|value| value.to_str()).unwrap_or("download");
let value = format!("attachment; filename*=UTF-8''{}", percent_encode_segment(filename));
let filename = path
.file_name()
.and_then(|value| value.to_str())
.unwrap_or("download");
let value = format!(
"attachment; filename*=UTF-8''{}",
percent_encode_segment(filename)
);
if let Ok(value) = HeaderValue::from_str(&value) {
response.headers_mut().insert(CONTENT_DISPOSITION, value);
}
@@ -372,11 +403,7 @@ fn if_range_allows(headers: &HeaderMap, etag: Option<&str>, modified: Option<Sys
.is_some_and(|date| modified <= date)
}
fn add_validators(
response: &mut Response,
etag: Option<&str>,
modified: Option<SystemTime>,
) {
fn add_validators(response: &mut Response, etag: Option<&str>, modified: Option<SystemTime>) {
if let Some(etag) = etag.and_then(|value| HeaderValue::from_str(value).ok()) {
response.headers_mut().insert(ETAG, etag);
}