use reqwest::Client;
use serde_json::json;
use sha2::{Digest, Sha256};
use super::{PollingProbe, ProbeError, ProbeFuture, ProbeResult};
#[derive(Debug, Clone)]
pub struct HttpProbeConfig {
pub url: String,
pub method: String,
pub headers: Vec<(String, String)>,
pub expected_status: u16,
}
pub struct HttpProbe {
config: HttpProbeConfig,
client: Client,
}
impl HttpProbe {
pub fn new(config: HttpProbeConfig) -> Self {
Self {
config,
client: Client::new(),
}
}
}
impl PollingProbe for HttpProbe {
fn name(&self) -> &str {
"http"
}
fn poll(&self) -> ProbeFuture<'_> {
Box::pin(async {
let method = self
.config
.method
.parse()
.map_err(|e| ProbeError::Failed(format!("invalid HTTP method: {e}")))?;
let mut request = self.client.request(method, &self.config.url);
for (key, value) in &self.config.headers {
request = request.header(key.as_str(), value.as_str());
}
let response = request
.send()
.await
.map_err(|e| ProbeError::Failed(format!("HTTP request failed: {e}")))?;
let status = response.status().as_u16();
if status != self.config.expected_status {
return Err(ProbeError::Failed(format!(
"unexpected status: {status}, expected: {}",
self.config.expected_status
)));
}
let body = response
.bytes()
.await
.map_err(|e| ProbeError::Failed(format!("failed to read body: {e}")))?;
let content_hash = hex::encode(Sha256::digest(&body));
let data = match serde_json::from_slice(&body) {
Ok(v) => v,
Err(_) => {
let body_str = String::from_utf8_lossy(&body);
json!({ "body": body_str })
}
};
Ok(Some(ProbeResult::with_hash(data, content_hash)))
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn http_probe_name() {
let probe = HttpProbe::new(HttpProbeConfig {
url: "http://localhost".to_string(),
method: "GET".to_string(),
headers: vec![],
expected_status: 200,
});
assert_eq!(probe.name(), "http");
}
#[test]
fn http_probe_config_clone() {
let config = HttpProbeConfig {
url: "http://localhost".to_string(),
method: "POST".to_string(),
headers: vec![("X-Key".to_string(), "val".to_string())],
expected_status: 201,
};
let cloned = config.clone();
assert_eq!(cloned.url, config.url);
assert_eq!(cloned.method, config.method);
assert_eq!(cloned.headers.len(), 1);
assert_eq!(cloned.expected_status, 201);
}
}