mod classify;
pub mod config;
mod data;
pub(crate) mod persist;
pub mod record;
pub mod summary;
pub use classify::{classify, Class, Group, Kind};
pub use record::{enable, enabled};
pub(crate) fn observe(request: &crate::request::Request, response: &crate::response::Response, ip: std::net::IpAddr) {
if !enabled() {
return;
}
let header = |name: &str| request.header(name).map(|h| h.value.as_ref()).filter(|v: &&str| !v.is_empty());
let content_type = response
.content_range_list
.first()
.map(|c| c.content_type.as_str())
.or_else(|| response.headers.iter().find(|h| h.name.eq_ignore_ascii_case("content-type")).map(|h| h.value.as_ref()));
record::record(&record::Hit {
ip,
host: header("host").unwrap_or(""),
user_agent: header("user-agent"),
referer: header("referer"),
method: &request.method,
path: &request.request_uri,
status: response.status_code,
content_type,
now: record::now(),
});
}
pub fn start() {
if enabled() {
return;
}
enable();
let Some(path) = config::persist_path() else { return };
persist::load(&path);
let _ = std::thread::Builder::new().name("rws-traffic".to_string()).spawn(move || loop {
std::thread::sleep(std::time::Duration::from_secs(300));
if let Err(e) = persist::save(&path) {
eprintln!("traffic: can't save {}: {e}", path.display());
}
});
}
pub fn save() {
if let (true, Some(path)) = (enabled(), config::persist_path()) {
if let Err(e) = persist::save(&path) {
eprintln!("traffic: can't save {}: {e}", path.display());
}
}
}
#[cfg(test)]
mod tests;