use std::sync::atomic::{AtomicU64, Ordering};
pub static SUCCESSFUL_RESPONSES_TOTAL: AtomicU64 = AtomicU64::new(0);
pub static OK_RESPONSES_TOTAL: AtomicU64 = AtomicU64::new(0);
pub static HTTP_ACCEPTS_TOTAL: AtomicU64 = AtomicU64::new(0);
#[inline]
pub fn record_response(status_code: u16) {
if (200..=399).contains(&status_code) {
SUCCESSFUL_RESPONSES_TOTAL.fetch_add(1, Ordering::Relaxed);
}
if status_code == 200 {
OK_RESPONSES_TOTAL.fetch_add(1, Ordering::Relaxed);
}
}
#[inline]
pub fn record_accept() {
HTTP_ACCEPTS_TOTAL.fetch_add(1, Ordering::Relaxed);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct CounterSnapshot {
pub successful: u64,
pub ok: u64,
pub accepts: u64,
}
pub fn snapshot() -> CounterSnapshot {
CounterSnapshot {
successful: SUCCESSFUL_RESPONSES_TOTAL.load(Ordering::Relaxed),
ok: OK_RESPONSES_TOTAL.load(Ordering::Relaxed),
accepts: HTTP_ACCEPTS_TOTAL.load(Ordering::Relaxed),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn reset_counters() {
SUCCESSFUL_RESPONSES_TOTAL.store(0, Ordering::Relaxed);
OK_RESPONSES_TOTAL.store(0, Ordering::Relaxed);
HTTP_ACCEPTS_TOTAL.store(0, Ordering::Relaxed);
}
#[test]
fn record_response_classifies_2xx_as_successful() {
let _g = TEST_LOCK.lock().unwrap();
reset_counters();
record_response(200);
record_response(204);
record_response(301);
let s = snapshot();
assert_eq!(s.successful, 3, "200, 204, 301 are all successful");
assert_eq!(s.ok, 1, "only one 200");
}
#[test]
fn record_response_excludes_4xx_5xx_from_successful() {
let _g = TEST_LOCK.lock().unwrap();
reset_counters();
record_response(404);
record_response(429);
record_response(500);
record_response(503);
let s = snapshot();
assert_eq!(s.successful, 0);
assert_eq!(s.ok, 0);
}
#[test]
fn record_accept_increments() {
let _g = TEST_LOCK.lock().unwrap();
reset_counters();
record_accept();
record_accept();
record_accept();
let s = snapshot();
assert_eq!(s.accepts, 3);
}
#[test]
fn snapshot_returns_current_values() {
let _g = TEST_LOCK.lock().unwrap();
reset_counters();
record_response(200);
record_response(200);
record_accept();
let s = snapshot();
assert_eq!(s.successful, 2);
assert_eq!(s.ok, 2);
assert_eq!(s.accepts, 1);
}
#[test]
fn ok_counter_only_for_status_200() {
let _g = TEST_LOCK.lock().unwrap();
reset_counters();
record_response(200);
record_response(201);
record_response(204);
let s = snapshot();
assert_eq!(s.ok, 1, "only 200 increments ok counter");
assert_eq!(s.successful, 3, "all three are successful");
}
}