use axum::extract::State;
use axum::http::{HeaderMap, HeaderValue, StatusCode, header};
use axum::response::{IntoResponse, Response};
use crate::app_state::AppState;
use super::SharedState;
const READY_BODY: &str = "all-smi is ready.\n";
const NOT_READY_BODY: &str = "all-smi is not ready: no collection cycle has completed yet.\n";
pub fn is_ready(state: &AppState) -> bool {
!state.loading
}
pub async fn ready_handler(State(state): State<SharedState>) -> Response {
let ready = is_ready(&*state.read().await);
readiness_response(ready)
}
fn readiness_response(ready: bool) -> Response {
let mut headers = HeaderMap::new();
headers.insert(
header::CONTENT_TYPE,
HeaderValue::from_static("text/plain; charset=utf-8"),
);
headers.insert(header::CACHE_CONTROL, HeaderValue::from_static("no-store"));
if ready {
(StatusCode::OK, headers, READY_BODY).into_response()
} else {
headers.insert(header::RETRY_AFTER, HeaderValue::from_static("1"));
(StatusCode::SERVICE_UNAVAILABLE, headers, NOT_READY_BODY).into_response()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn loading_state_is_not_ready() {
let state = AppState::default();
assert!(state.loading, "precondition: a fresh AppState is loading");
assert!(!is_ready(&state));
}
#[test]
fn cleared_loading_flag_is_ready() {
let state = AppState {
loading: false,
..Default::default()
};
assert!(is_ready(&state));
}
#[test]
fn not_ready_is_503_with_retry_after_and_no_store() {
let response = readiness_response(false);
assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
let headers = response.headers();
assert_eq!(headers.get(header::RETRY_AFTER).unwrap(), "1");
assert_eq!(headers.get(header::CACHE_CONTROL).unwrap(), "no-store");
assert_eq!(
headers.get(header::CONTENT_TYPE).unwrap(),
"text/plain; charset=utf-8"
);
}
#[test]
fn ready_is_200_without_retry_after() {
let response = readiness_response(true);
assert_eq!(response.status(), StatusCode::OK);
assert!(
response.headers().get(header::RETRY_AFTER).is_none(),
"Retry-After is meaningless on a 200 and would confuse a proxy"
);
assert_eq!(
response.headers().get(header::CACHE_CONTROL).unwrap(),
"no-store"
);
}
}