rust_web_server/traffic/
mod.rs1mod classify;
19pub mod config;
20mod data;
21pub(crate) mod persist;
22pub mod record;
23pub mod summary;
24
25pub use classify::{classify, Class, Group, Kind};
26pub use record::{enable, enabled};
27
28pub(crate) fn observe(request: &crate::request::Request, response: &crate::response::Response, ip: std::net::IpAddr) {
31 if !enabled() {
32 return;
33 }
34 let header = |name: &str| request.header(name).map(|h| h.value.as_ref()).filter(|v: &&str| !v.is_empty());
35 let content_type = response
36 .content_range_list
37 .first()
38 .map(|c| c.content_type.as_str())
39 .or_else(|| response.headers.iter().find(|h| h.name.eq_ignore_ascii_case("content-type")).map(|h| h.value.as_ref()));
40 record::record(&record::Hit {
41 ip,
42 host: header("host").unwrap_or(""),
43 user_agent: header("user-agent"),
44 referer: header("referer"),
45 method: &request.method,
46 path: &request.request_uri,
47 status: response.status_code,
48 content_type,
49 now: record::now(),
50 });
51}
52
53pub fn start() {
57 if enabled() {
58 return;
59 }
60 enable();
61 let Some(path) = config::persist_path() else { return };
62 persist::load(&path);
63 let _ = std::thread::Builder::new().name("rws-traffic".to_string()).spawn(move || loop {
64 std::thread::sleep(std::time::Duration::from_secs(300));
65 if let Err(e) = persist::save(&path) {
66 eprintln!("traffic: can't save {}: {e}", path.display());
67 }
68 });
69}
70
71pub fn save() {
73 if let (true, Some(path)) = (enabled(), config::persist_path()) {
74 if let Err(e) = persist::save(&path) {
75 eprintln!("traffic: can't save {}: {e}", path.display());
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests;