Fix: is_reachable() akzeptiert 401/403, aber nicht dauerhafte 5xx-Fehler
Der HTTP-Zweig von is_reachable() nutzte curl --fail, was einen HTTP-Server, der auf das HEAD mit 401/403 (Auth erforderlich, aber erreichbar) antwortet, faelschlich als nicht erreichbar meldete - relevant fuer WebDAV-Cloud-Seiten, die Auth verlangen. Der tatsaechliche Statuscode wird jetzt ausgelesen: nur ein Serverfehler (5xx) gilt als nicht erreichbar, da ein dauerhaft 5xx-antwortender Server (z. B. ein kaputter Reverse-Proxy) sonst bei jedem watch-Durchlauf einen vollen, letztlich erfolglosen Umschaltversuch (inkl. MOUNT_TIMEOUT_SECS-Wartezeit) ausloesen wuerde. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+64
-5
@@ -9,15 +9,37 @@ use std::process::{Command, Stdio};
|
|||||||
/// (HTTP HEAD via `curl`) oder eine reine Host-/IP-Adresse (ICMP-Ping) handelt.
|
/// (HTTP HEAD via `curl`) oder eine reine Host-/IP-Adresse (ICMP-Ping) handelt.
|
||||||
///
|
///
|
||||||
/// Portiert aus dem alten `src/network/utils.rs` (v0.2.0).
|
/// Portiert aus dem alten `src/network/utils.rs` (v0.2.0).
|
||||||
|
///
|
||||||
|
/// Für HTTP wird bewusst NICHT `curl --fail` verwendet: ein WebDAV-Server, der auf das HEAD
|
||||||
|
/// mit 401/403 (Auth erforderlich) antwortet, ist trotzdem erreichbar - `--fail` würde das als
|
||||||
|
/// Fehler werten. Der tatsächliche Statuscode wird stattdessen ausgelesen und geprüft: nur ein
|
||||||
|
/// Serverfehler (5xx) gilt als NICHT erreichbar, da ein dauerhaft 5xx-antwortender Server
|
||||||
|
/// (z. B. ein kaputter Reverse-Proxy) sonst bei jedem `watch`-Durchlauf einen vollen,
|
||||||
|
/// letztlich erfolglosen Umschaltversuch (inkl. `MOUNT_TIMEOUT_SECS`-Wartezeit) auslösen würde.
|
||||||
pub fn is_reachable(addr: &str) -> bool {
|
pub fn is_reachable(addr: &str) -> bool {
|
||||||
if addr.starts_with("http://") || addr.starts_with("https://") {
|
if addr.starts_with("http://") || addr.starts_with("https://") {
|
||||||
Command::new("curl")
|
Command::new("curl")
|
||||||
.args(["--head", "--silent", "--fail", "--max-time", "5", addr])
|
.args([
|
||||||
.stdout(Stdio::null())
|
"--head",
|
||||||
|
"--silent",
|
||||||
|
"--max-time",
|
||||||
|
"5",
|
||||||
|
"--output",
|
||||||
|
"/dev/null",
|
||||||
|
"--write-out",
|
||||||
|
"%{http_code}",
|
||||||
|
addr,
|
||||||
|
])
|
||||||
.stderr(Stdio::null())
|
.stderr(Stdio::null())
|
||||||
.status()
|
.output()
|
||||||
.map(|s| s.success())
|
.ok()
|
||||||
.unwrap_or(false)
|
.and_then(|o| {
|
||||||
|
String::from_utf8_lossy(&o.stdout)
|
||||||
|
.trim()
|
||||||
|
.parse::<u16>()
|
||||||
|
.ok()
|
||||||
|
})
|
||||||
|
.is_some_and(|code| (100..500).contains(&code))
|
||||||
} else {
|
} else {
|
||||||
Command::new("ping")
|
Command::new("ping")
|
||||||
.args(["-c", "1", "-W", "2", addr])
|
.args(["-c", "1", "-W", "2", addr])
|
||||||
@@ -28,3 +50,40 @@ pub fn is_reachable(addr: &str) -> bool {
|
|||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::net::TcpListener;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn http_401_endpoint_is_reported_as_reachable() {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").expect("bind");
|
||||||
|
let port = listener.local_addr().expect("port").port();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
if let Ok((mut stream, _)) = listener.accept() {
|
||||||
|
let response = "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n\r\n";
|
||||||
|
let _ = stream.write_all(response.as_bytes());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(is_reachable(&format!("http://127.0.0.1:{port}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn http_500_endpoint_is_reported_as_unreachable() {
|
||||||
|
let listener = TcpListener::bind("127.0.0.1:0").expect("bind");
|
||||||
|
let port = listener.local_addr().expect("port").port();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
if let Ok((mut stream, _)) = listener.accept() {
|
||||||
|
let response = "HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n";
|
||||||
|
let _ = stream.write_all(response.as_bytes());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(!is_reachable(&format!("http://127.0.0.1:{port}")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user