This commit is contained in:
Mateusz Gruszczyński committed 2026-10-15 09:37:47 +02:00
1 parent bbfa8cbc3f
commit 2ccc598b12
49 files changed
+4771 -639

No files matched your search

+61 -33
View File
@@ -26,8 +26,7 @@ impl Php {
"Web Service PHP request body limit must be 0..8589934592 bytes"
);
ensure!(
self.max_response_header_bytes >= 4096
&& self.max_response_header_bytes <= 1024 * 1024,
self.max_response_header_bytes >= 4096 && self.max_response_header_bytes <= 1024 * 1024,
"Web Service PHP response header limit must be 4096..1048576 bytes"
);
ensure!(
@@ -37,7 +36,13 @@ impl Php {
self.custom_params = normalize_fastcgi_params(std::mem::take(&mut self.custom_params))?;
self.php_values = normalize_php_values(std::mem::take(&mut self.php_values))?;
self.php_admin_values = normalize_php_values(std::mem::take(&mut self.php_admin_values))?;
ensure!(!self.php_values.keys().any(|key| self.php_admin_values.contains_key(key)), "PHP_VALUE and PHP_ADMIN_VALUE must not define the same directive");
ensure!(
!self
.php_values
.keys()
.any(|key| self.php_admin_values.contains_key(key)),
"PHP_VALUE and PHP_ADMIN_VALUE must not define the same directive"
);
let php_custom_bytes = self
.custom_params
.iter()
@@ -50,7 +55,10 @@ impl Php {
"Web Service PHP custom FastCGI/PHP_VALUE data must be at most 128 KiB"
);
if self.enabled {
ensure!(!self.extensions.is_empty(), "PHP-FPM needs executable extensions");
ensure!(
!self.extensions.is_empty(),
"PHP-FPM needs executable extensions"
);
FastCgiEndpoint::parse(&self.endpoint)?;
}
Ok(())
@@ -59,10 +67,17 @@ impl Php {
impl PhpFpmProfile {
pub fn validate(&mut self) -> Result<()> {
ensure!(crate::config::valid_id(&self.id), "invalid PHP-FPM profile id");
ensure!(
crate::config::valid_id(&self.id),
"invalid PHP-FPM profile id"
);
self.name = self.name.trim().to_owned();
ensure!(!self.name.is_empty() && self.name.len() <= MAX_SERVICE_NAME_BYTES
&& !self.name.chars().any(char::is_control), "PHP-FPM profile name is invalid");
ensure!(
!self.name.is_empty()
&& self.name.len() <= MAX_SERVICE_NAME_BYTES
&& !self.name.chars().any(char::is_control),
"PHP-FPM profile name is invalid"
);
self.php.validate()
}
}
@@ -98,7 +113,10 @@ impl Service {
&& item != "..",
"Web Service index entries must be plain file names"
);
ensure!(seen.insert(item.clone()), "duplicate Web Service index file");
ensure!(
seen.insert(item.clone()),
"duplicate Web Service index file"
);
}
ensure!(
self.mime_types.len() <= MAX_MIME_TYPES,
@@ -106,7 +124,10 @@ impl Service {
);
let mut normalized_mime_types = BTreeMap::new();
for (extension, value) in std::mem::take(&mut self.mime_types) {
let extension = extension.trim().trim_start_matches('.').to_ascii_lowercase();
let extension = extension
.trim()
.trim_start_matches('.')
.to_ascii_lowercase();
ensure!(
!extension.is_empty()
&& extension.len() <= MAX_MIME_EXTENSION_BYTES
@@ -160,14 +181,11 @@ impl Service {
.matcher
.strip_prefix("ext:")
.is_some_and(valid_extension)
|| rule
.matcher
.strip_prefix("mime:")
.is_some_and(|value| {
!value.is_empty()
&& value.len() <= MAX_MIME_VALUE_BYTES
&& !value.chars().any(char::is_control)
});
|| rule.matcher.strip_prefix("mime:").is_some_and(|value| {
!value.is_empty()
&& value.len() <= MAX_MIME_VALUE_BYTES
&& !value.chars().any(char::is_control)
});
ensure!(
valid_matcher,
"Web Service cache rule matcher must be ext:<extension> or mime:<prefix>"
@@ -192,8 +210,8 @@ impl Service {
!name.is_empty() && name.len() <= MAX_HEADER_NAME_BYTES,
"Web Service response header name must be 1..128 bytes"
);
let header_name = HeaderName::from_str(&name)
.context("invalid Web Service response header name")?;
let header_name =
HeaderName::from_str(&name).context("invalid Web Service response header name")?;
ensure!(
!protected_service_header(&header_name),
"Web Service response header cannot override framing/transport headers"
@@ -203,8 +221,7 @@ impl Service {
value.len() <= MAX_HEADER_VALUE_BYTES,
"Web Service response header value must be at most 8192 bytes"
);
HeaderValue::from_str(&value)
.context("invalid Web Service response header value")?;
HeaderValue::from_str(&value).context("invalid Web Service response header value")?;
ensure!(
normalized_headers.insert(name, value).is_none(),
"duplicate Web Service response header"
@@ -277,7 +294,10 @@ impl Service {
self.php.validate()?;
if let Some(id) = &self.php_profile_id {
ensure!(crate::config::valid_id(id), "invalid PHP-FPM profile reference");
ensure!(
crate::config::valid_id(id),
"invalid PHP-FPM profile reference"
);
}
htaccess::validate(&mut self.htaccess)?;
@@ -320,7 +340,10 @@ impl Service {
);
#[cfg(unix)]
let root_handle = Arc::new(open_root_directory(&root).with_context(|| {
format!("Web Service {} document root could not be opened safely", self.id)
format!(
"Web Service {} document root could not be opened safely",
self.id
)
})?);
let htaccess = htaccess::compile(&root, &self.htaccess)?;
if let Some(compiled) = &htaccess {
@@ -337,10 +360,7 @@ impl Service {
.response_headers
.iter()
.map(|(name, value)| {
Ok::<_, anyhow::Error>((
HeaderName::from_str(name)?,
HeaderValue::from_str(value)?,
))
Ok::<_, anyhow::Error>((HeaderName::from_str(name)?, HeaderValue::from_str(value)?))
})
.collect::<std::result::Result<Vec<_>, anyhow::Error>>()?;
let cache_rules = self
@@ -367,12 +387,20 @@ impl Service {
Ok(CompiledRewriteRule {
pattern: Regex::new(&rule.pattern)?,
replacement: rule.replacement.clone(),
status: rule.status.and_then(|value| StatusCode::from_u16(value).ok()),
status: rule
.status
.and_then(|value| StatusCode::from_u16(value).ok()),
})
})
.collect::<Result<Vec<_>>>()?;
let mut script_extensions: HashSet<String> = self.php.extensions.iter().cloned().collect();
script_extensions.extend(["php", "phtml", "phar", "php3", "php4", "php5", "php7", "php8"].into_iter().map(str::to_owned));
script_extensions.extend(
[
"php", "phtml", "phar", "php3", "php4", "php5", "php7", "php8",
]
.into_iter()
.map(str::to_owned),
);
let php = if self.php.enabled {
Some(CompiledPhp {
extensions: self.php.extensions.iter().cloned().collect(),
@@ -381,6 +409,7 @@ impl Service {
connect_timeout: Duration::from_millis(self.php.connect_timeout_ms),
request_timeout: Duration::from_millis(self.php.request_timeout_ms),
permits: Arc::new(Semaphore::new(self.php.max_concurrent_requests)),
max_concurrent: self.php.max_concurrent_requests,
pass_authorization: self.php.pass_authorization,
max_request_body_bytes: self.php.max_request_body_bytes,
max_response_header_bytes: self.php.max_response_header_bytes,
@@ -403,10 +432,9 @@ impl Service {
mime_types: self
.mime_types
.iter()
.map(|(extension, value)| {
Ok((extension.clone(), HeaderValue::from_str(value)?))
})
.collect::<std::result::Result<BTreeMap<_, _>, http::header::InvalidHeaderValue>>()?,
.map(|(extension, value)| Ok((extension.clone(), HeaderValue::from_str(value)?)))
.collect::<std::result::Result<BTreeMap<_, _>, http::header::InvalidHeaderValue>>(
)?,
default_mime_type: HeaderValue::from_str(&self.default_mime_type)?,
cache_control: self
.cache_control