mod classify;
pub mod config;
mod data;
pub mod device;
pub mod geo;
#[doc(hidden)]
pub mod persist;
pub mod record;
pub mod script;
#[cfg(feature = "model-postgres")]
pub mod store;
pub mod summary;
pub use classify::{classify, Class, Group, Kind};
pub use record::{enable, enabled};
pub use script::TrafficScriptLayer;
#[doc(hidden)]
pub 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,
headers: &request.headers,
now: record::now(),
});
}
pub fn start() {
if enabled() {
return;
}
enable();
let _ = std::thread::Builder::new().name("rws-traffic-geo".to_string()).spawn(|| {
geo::table();
});
start_store();
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());
}
});
}
fn start_store() {
let Some(url) = config::database_url() else { return };
#[cfg(feature = "model-postgres")]
match store::start(&url) {
Ok(()) => println!("traffic: writing page views to PostgreSQL."),
Err(e) => eprintln!("traffic: can't use the traffic database: {e}; counting without it"),
}
#[cfg(not(feature = "model-postgres"))]
{
let _ = url;
eprintln!("traffic: [traffic] database_url_env is set, but this rws is built without the model-postgres feature; counting without it");
}
}
pub fn save() {
#[cfg(feature = "model-postgres")]
store::stop();
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;