v0.34.0_fina;

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-18 12:00:59 +02:00
1 parent 1ca019d849
commit c207e8a03f
69 files changed
+1496 -13838

No files matched your search

+30 -261
View File
@@ -1,4 +1,5 @@
//! Embedded Markdown documents for the authenticated Reader.
//! Embedded current-version Markdown guides for the authenticated documentation Reader.
//! Keep this list in sync with docs/README.md. The embedded files must exist at build time.
macro_rules! reader_doc {
($path:literal, $category:literal) => {
@@ -11,264 +12,32 @@ macro_rules! reader_doc {
}
pub(super) static READER_DOCUMENTS: &[(&str, &str, &str)] = &[
(
"README.md",
"project",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md")),
),
(
"ROADMAP.md",
"project",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/ROADMAP.md")),
),
(
"CHANGELOG.md",
"project",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/CHANGELOG.md")),
),
(
"docs/ACME_PROVIDERS.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/ACME_PROVIDERS.md"
)),
),
(
"docs/ACME_RFC2136.md",
"historical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/ACME_RFC2136.md")),
),
(
"docs/API.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/API.md")),
),
(
"docs/API_ACCESS.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/API_ACCESS.md")),
),
(
"docs/ARCHITECTURE.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/ARCHITECTURE.md")),
),
(
"docs/CACHE.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/CACHE.md")),
),
(
"docs/CACHE_DIAGNOSTICS.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/CACHE_DIAGNOSTICS.md"
)),
),
(
"docs/CHANGELOG_TECHNICAL_ARCHIVE.md",
"historical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/CHANGELOG_TECHNICAL_ARCHIVE.md"
)),
),
(
"docs/CONTAINER.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/CONTAINER.md")),
),
(
"docs/CUSTOM_LOCATIONS.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/CUSTOM_LOCATIONS.md"
)),
),
(
"docs/DOCKER_DISCOVERY.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/DOCKER_DISCOVERY.md"
)),
),
(
"docs/ERROR_LOGGING.md",
"technical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/ERROR_LOGGING.md"
)),
),
(
"docs/FILE_LOGS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/FILE_LOGS.md")),
),
(
"docs/HTTP3.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/HTTP3.md")),
),
(
"docs/INSTALL.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/INSTALL.md")),
),
(
"docs/INTEGRATIONS_040.md",
"historical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/INTEGRATIONS_040.md"
)),
),
(
"docs/LIVE_LOGS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/LIVE_LOGS.md")),
),
(
"docs/MANAGEMENT.md",
"historical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/MANAGEMENT.md")),
),
reader_doc!("docs/MEMORY_STRESS.md", "technical"),
(
"docs/MIGRATION.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/MIGRATION.md")),
),
(
"docs/MOCK_AUTH.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/MOCK_AUTH.md")),
),
(
"docs/MOCK_SERVER.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/MOCK_SERVER.md")),
),
(
"docs/NOT_FOUND_HOSTS.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/NOT_FOUND_HOSTS.md"
)),
),
(
"docs/NPM_MIGRATION.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/NPM_MIGRATION.md"
)),
),
(
"docs/PHP_FPM.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/PHP_FPM.md")),
),
(
"docs/PHP_FPM_ISOLATION.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/PHP_FPM_ISOLATION.md"
)),
),
(
"docs/PROMETHEUS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/PROMETHEUS.md")),
),
(
"docs/README.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/README.md")),
),
(
"docs/REDIRECTS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/REDIRECTS.md")),
),
(
"docs/REFERENCES.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/REFERENCES.md")),
),
(
"docs/RENEWAL.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/RENEWAL.md")),
),
(
"docs/ROADMAP_HISTORY.md",
"historical",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/ROADMAP_HISTORY.md"
)),
),
(
"docs/SECRET_STORE_BACKUP.md",
"guides",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/SECRET_STORE_BACKUP.md"
)),
),
(
"docs/SECURITY.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/SECURITY.md")),
),
(
"docs/SECURITY_AUDIT.md",
"audits",
include_str!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/docs/SECURITY_AUDIT.md"
)),
),
(
"docs/SETTINGS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/SETTINGS.md")),
),
(
"docs/STREAMS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/STREAMS.md")),
),
(
"docs/TRANSFORM.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/TRANSFORM.md")),
),
(
"docs/TRANSPORT.md",
"technical",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/TRANSPORT.md")),
),
(
"docs/VHOSTS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/VHOSTS.md")),
),
(
"docs/VHOST_ACCESS.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/VHOST_ACCESS.md")),
),
(
"docs/WEB_SERVER.md",
"guides",
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/docs/WEB_SERVER.md")),
),
reader_doc!("README.md", "project"),
reader_doc!("CHANGELOG.md", "project"),
reader_doc!("ROADMAP.md", "project"),
reader_doc!("docs/README.md", "guides"),
reader_doc!("docs/INSTALL.md", "guides"),
reader_doc!("docs/CONTAINER.md", "guides"),
reader_doc!("docs/CONFIGURATION.md", "guides"),
reader_doc!("docs/VHOSTS.md", "guides"),
reader_doc!("docs/CUSTOM_LOCATIONS.md", "guides"),
reader_doc!("docs/ACCESS.md", "guides"),
reader_doc!("docs/WEB_SERVER.md", "guides"),
reader_doc!("docs/PHP_FPM.md", "guides"),
reader_doc!("docs/CACHE.md", "guides"),
reader_doc!("docs/RESPONSE_DELIVERY.md", "guides"),
reader_doc!("docs/TRANSPORT.md", "guides"),
reader_doc!("docs/STREAMS.md", "guides"),
reader_doc!("docs/ACME_PROVIDERS.md", "guides"),
reader_doc!("docs/DOCKER_DISCOVERY.md", "guides"),
reader_doc!("docs/INTEGRATIONS.md", "guides"),
reader_doc!("docs/DASHBOARD.md", "guides"),
reader_doc!("docs/MONITORING.md", "guides"),
reader_doc!("docs/LOGS.md", "guides"),
reader_doc!("docs/BACKUP.md", "guides"),
reader_doc!("docs/MIGRATION.md", "guides"),
reader_doc!("docs/SECURITY.md", "guides"),
reader_doc!("docs/API.md", "guides"),
reader_doc!("docs/API_ACCESS.md", "guides"),
reader_doc!("docs/TROUBLESHOOTING.md", "guides"),
];
+2 -2
View File
@@ -2370,10 +2370,10 @@ mod tests {
.any(|document| document["path"] == "docs/PHP_FPM.md"));
assert!(documents
.iter()
.any(|document| document["path"] == "docs/SECURITY_AUDIT.md"));
.any(|document| document["path"] == "docs/SECURITY.md"));
assert!(documents
.iter()
.any(|document| document["path"] == "docs/TEST_REPORT.md"));
.any(|document| document["path"] == "docs/TROUBLESHOOTING.md"));
store.close().await;
}
#[tokio::test]
+5 -2
View File
@@ -3571,8 +3571,11 @@
<label class="vh-wide"><span data-i18n="webServiceSendfile">Linux sendfile (zero-copy)</span>
<select id="web-service-sendfile">
<option value="off" data-i18n="notEnabled">Disabled</option>
<option value="auto" data-i18n="webServiceSendfileAuto">Auto (eligible plain HTTP/1.1)</option>
</select><small class="fine" data-i18n="webServiceSendfileHelp">Opt-in for static GET files at least 64 KiB on Linux HTTP/1.1 without TLS, cache, compression or transform. Other requests use safe streaming; switching this setting never clears the cache.</small></label>
<option value="auto" data-i18n="webServiceSendfileAuto">Auto (eligible plain HTTP/1.1)
</option>
</select><small class="fine" data-i18n="webServiceSendfileHelp">Opt-in for static GET files at
least 64 KiB on Linux HTTP/1.1 without TLS, cache, compression or transform. Other requests
use safe streaming; switching this setting never clears the cache.</small></label>
<label><span data-i18n="webServiceCompression">Dynamic compression</span><select
id="web-service-compression">
<option value="" data-i18n="inherit">Inherit</option>
+20 -11
View File
@@ -28,29 +28,38 @@ impl PeerFormatting {
local,
peer_ip,
peer_header: HeaderValue::from_str(&peer_text).expect("IP text is a valid header"),
peer_node: if peer_ip.is_ipv4() { peer_text } else { node(peer_ip) },
peer_node: if peer_ip.is_ipv4() {
peer_text
} else {
node(peer_ip)
},
by_node: node(canonical(local.ip())),
port_header: HeaderValue::from_str(&local.port().to_string())
.expect("TCP port is a valid header"),
untrusted_headers: std::sync::Mutex::new(None),
}
}
fn peer_header(&self) -> &HeaderValue { &self.peer_header }
fn peer_header(&self) -> &HeaderValue {
&self.peer_header
}
fn cached_untrusted(&self, authority: &HeaderValue, tls: bool) -> Option<HeaderMap> {
self.untrusted_headers.lock().unwrap_or_else(|e| e.into_inner())
self.untrusted_headers
.lock()
.unwrap_or_else(|e| e.into_inner())
.as_ref()
.filter(|entry| entry.tls == tls
&& entry.authority.as_bytes() == authority.as_bytes())
.filter(|entry| entry.tls == tls && entry.authority.as_bytes() == authority.as_bytes())
.map(|entry| entry.base.clone())
}
fn remember_untrusted(&self, authority: &HeaderValue, tls: bool, base: &HeaderMap) {
*self.untrusted_headers.lock().unwrap_or_else(|e| e.into_inner()) =
Some(UntrustedHeaders {
authority: authority.clone(),
tls,
base: base.clone(),
});
*self
.untrusted_headers
.lock()
.unwrap_or_else(|e| e.into_inner()) = Some(UntrustedHeaders {
authority: authority.clone(),
tls,
base: base.clone(),
});
}
}
+18 -7
View File
@@ -20,19 +20,30 @@ mod authority_tests {
("/assets/%61pp.js?rev=5", Some("/assets/app.js")),
("/assets//app.js", Some("/assets/app.js")),
] {
let mut request = Request::builder().method(Method::GET).uri(raw)
.header(HOST, "localhost").body(Body::empty()).unwrap();
request.extensions_mut().insert(LocalAddr("127.0.0.1:8080".parse().unwrap()));
let mut request = Request::builder()
.method(Method::GET)
.uri(raw)
.header(HOST, "localhost")
.body(Body::empty())
.unwrap();
request
.extensions_mut()
.insert(LocalAddr("127.0.0.1:8080".parse().unwrap()));
let context = response_context(&snapshot, &request);
let validated = context.validated.unwrap();
assert_eq!(validated.host, "localhost");
assert_eq!(validated.original_host, "localhost");
assert_eq!(validated.decoded_path.as_deref(), normalized);
}
let mut invalid = Request::builder().uri("/ok")
.header(HOST, "localhost").header(HOST, "another.example")
.body(Body::empty()).unwrap();
invalid.extensions_mut().insert(LocalAddr("127.0.0.1:8080".parse().unwrap()));
let mut invalid = Request::builder()
.uri("/ok")
.header(HOST, "localhost")
.header(HOST, "another.example")
.body(Body::empty())
.unwrap();
invalid
.extensions_mut()
.insert(LocalAddr("127.0.0.1:8080".parse().unwrap()));
assert!(response_context(&snapshot, &invalid).validated.is_none());
assert!(crate::acl::routing_path_view("/encoded%2fslash").is_err());
}
+33 -17
View File
@@ -3,8 +3,16 @@ include!("request_id.rs");
// Retain the exchange through the response, but do not allocate an additional
// body adapter for a request whose body has no frames or trailers.
fn wrap_observed_request(body: Body, exchange: &Arc<Exchange>, idle: Duration) -> Body {
if body.is_end_stream() { body }
else { Body::new(MeteredBody::new(body, exchange.clone(), Direction::Request, idle)) }
if body.is_end_stream() {
body
} else {
Body::new(MeteredBody::new(
body,
exchange.clone(),
Direction::Request,
idle,
))
}
}
fn log_prefix(input: &str, max_chars: usize) -> String {
@@ -51,13 +59,12 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
.unwrap_or_default()
.tls;
request.extensions_mut().insert(snapshot.clone());
let original_headers = if snapshot.envelope.config.compression.enabled()
&& request.method() != Method::HEAD
{
crate::compression::request_headers(request.headers())
} else {
HeaderMap::new()
};
let original_headers =
if snapshot.envelope.config.compression.enabled() && request.method() != Method::HEAD {
crate::compression::request_headers(request.headers())
} else {
HeaderMap::new()
};
let method = request.method().clone();
let is_head = method == Method::HEAD;
let ws_attempt = request
@@ -71,9 +78,15 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
&& request.method() == Method::CONNECT
&& websocket::h3_protocol(&request) == Some("websocket"));
match request.version() {
Version::HTTP_11 => { state.metrics.http1_requests.fetch_add(1, Ordering::Relaxed); }
Version::HTTP_2 => { state.metrics.http2_requests.fetch_add(1, Ordering::Relaxed); }
Version::HTTP_3 => { state.metrics.http3_requests.fetch_add(1, Ordering::Relaxed); }
Version::HTTP_11 => {
state.metrics.http1_requests.fetch_add(1, Ordering::Relaxed);
}
Version::HTTP_2 => {
state.metrics.http2_requests.fetch_add(1, Ordering::Relaxed);
}
Version::HTTP_3 => {
state.metrics.http3_requests.fetch_add(1, Ordering::Relaxed);
}
_ => {}
}
let id = state.metrics.requests.fetch_add(1, Ordering::Relaxed) + 1;
@@ -132,7 +145,9 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
frontend.metrics.active.fetch_add(1, Ordering::Relaxed);
}
let host = log_prefix(
request.headers().get(HOST)
request
.headers()
.get(HOST)
.and_then(|h| h.to_str().ok())
.unwrap_or_else(|| request.uri().authority().map(|v| v.as_str()).unwrap_or("")),
253,
@@ -315,7 +330,8 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
// global encoding (e.g. Accept-Encoding: identity), encode is a no-op:
// avoid even constructing/resolving the per-request settings.
if method != Method::HEAD
&& crate::compression::preferred_encoding(&original_headers, global_compression).is_some()
&& crate::compression::preferred_encoding(&original_headers, global_compression)
.is_some()
{
let mut compression = global_compression.clone();
if let Some(frontend) = frontend_context.as_ref() {
@@ -330,9 +346,9 @@ async fn observe(State(state): State<Arc<App>>, mut request: Request, next: Next
.resolve(global_compression, &compression);
}
}
response = crate::compression::encode(
response, &original_headers, &method, &compression,
).await;
response =
crate::compression::encode(response, &original_headers, &method, &compression)
.await;
}
if let Some(encoding) = response
.extensions()