fix: per-check client with connect_timeout to guarantee OS-level TCP timeout
This commit is contained in:
parent
7905a8003b
commit
b8b0a9d5e2
|
|
@ -70,10 +70,20 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op
|
|||
let method = monitor.method.as_deref().unwrap_or("GET").to_uppercase();
|
||||
let timeout = std::time::Duration::from_millis(monitor.timeout_ms.unwrap_or(30000));
|
||||
|
||||
// Build a per-check client with connect_timeout = monitor timeout.
|
||||
// This ensures the OS-level TCP connect is bounded, since tokio future
|
||||
// cancellation alone cannot interrupt a kernel-level SYN wait.
|
||||
let check_client = reqwest::Client::builder()
|
||||
.user_agent("PingQL-Monitor/0.1")
|
||||
.connect_timeout(timeout)
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
.unwrap_or_else(|_| client.clone());
|
||||
|
||||
let req_method = reqwest::Method::from_bytes(method.as_bytes())
|
||||
.unwrap_or(reqwest::Method::GET);
|
||||
|
||||
let mut req = client.request(req_method, &monitor.url).timeout(timeout);
|
||||
let mut req = check_client.request(req_method, &monitor.url);
|
||||
|
||||
if let Some(headers) = &monitor.request_headers {
|
||||
for (k, v) in headers {
|
||||
|
|
@ -93,8 +103,8 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op
|
|||
let is_https = monitor.url.starts_with("https://");
|
||||
let url_for_cert = monitor.url.clone();
|
||||
|
||||
let timed = tokio::time::timeout(timeout, async {
|
||||
let resp = req.send().await?;
|
||||
let result: Result<_, String> = async {
|
||||
let resp = req.send().await.map_err(|e| e.to_string())?;
|
||||
let status = resp.status();
|
||||
let headers: HashMap<String, String> = resp.headers().iter()
|
||||
.filter_map(|(k, v)| Some((k.to_string(), v.to_str().ok()?.to_string())))
|
||||
|
|
@ -106,23 +116,16 @@ async fn run_check(client: &reqwest::Client, monitor: &Monitor, scheduled_at: Op
|
|||
if content_len > MAX_BODY_BYTES {
|
||||
format!("[body truncated: Content-Length {} exceeds 10MB limit]", content_len)
|
||||
} else {
|
||||
let bytes = resp.bytes().await?;
|
||||
let bytes = resp.bytes().await.map_err(|e| e.to_string())?;
|
||||
let truncated = &bytes[..bytes.len().min(MAX_BODY_BYTES)];
|
||||
String::from_utf8_lossy(truncated).into_owned()
|
||||
}
|
||||
};
|
||||
Ok::<_, reqwest::Error>((status, headers, body))
|
||||
}).await;
|
||||
Ok((status, headers, body))
|
||||
}.await;
|
||||
|
||||
let latency_ms = start.elapsed().as_millis() as u64;
|
||||
|
||||
// Flatten timeout + reqwest errors into a single result
|
||||
let result = match timed {
|
||||
Err(_) => Err(format!("timed out after {}ms", timeout.as_millis())),
|
||||
Ok(Err(e)) => Err(e.to_string()),
|
||||
Ok(Ok(v)) => Ok(v),
|
||||
};
|
||||
|
||||
match result {
|
||||
Err(e) => PingResult {
|
||||
monitor_id: monitor.id.clone(),
|
||||
|
|
|
|||
Loading…
Reference in New Issue