v0.3
This commit is contained in:
1 parent
28e39f7ce4
commit
b57beb83c7
34 files changed
+1037
-314
No files matched your search
@@ -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);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user