use std::time::Duration;
pub(super) struct DeadLink {
pub loc: String,
pub origin: String,
pub target: String,
pub reason: String,
}
pub(super) fn client() -> reqwest::Result<reqwest::Client> {
reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.user_agent("inkhaven-research (dead-source check)")
.redirect(reqwest::redirect::Policy::limited(5))
.build()
}
pub(super) async fn check_web(client: &reqwest::Client, url: &str) -> Option<String> {
match client.head(url).send().await {
Ok(resp) => {
if resp.status() == reqwest::StatusCode::METHOD_NOT_ALLOWED {
match client.get(url).send().await {
Ok(r2) => dead_status(r2.status()),
Err(e) => classify_err(&e),
}
} else {
dead_status(resp.status())
}
}
Err(e) => classify_err(&e),
}
}
fn dead_status(s: reqwest::StatusCode) -> Option<String> {
if s == reqwest::StatusCode::NOT_FOUND || s == reqwest::StatusCode::GONE {
Some(format!("HTTP {}", s.as_u16()))
} else {
None
}
}
fn classify_err(e: &reqwest::Error) -> Option<String> {
if e.is_timeout() {
Some("timeout".into())
} else if e.is_connect() {
Some("unreachable".into())
} else {
Some("request failed".into())
}
}
#[cfg(test)]
mod tests {
use super::*;
use reqwest::StatusCode;
#[test]
fn only_404_and_410_count_as_dead() {
assert_eq!(dead_status(StatusCode::NOT_FOUND), Some("HTTP 404".into()));
assert_eq!(dead_status(StatusCode::GONE), Some("HTTP 410".into()));
assert_eq!(dead_status(StatusCode::OK), None);
assert_eq!(dead_status(StatusCode::FORBIDDEN), None);
assert_eq!(dead_status(StatusCode::TOO_MANY_REQUESTS), None);
assert_eq!(dead_status(StatusCode::INTERNAL_SERVER_ERROR), None);
assert_eq!(dead_status(StatusCode::MOVED_PERMANENTLY), None);
}
}