use axum::http::header::{HeaderMap, AUTHORIZATION};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BillingMode {
ApiKey,
Subscription,
Unknown,
}
impl BillingMode {
pub fn as_str(&self) -> &'static str {
match self {
BillingMode::ApiKey => "api_key",
BillingMode::Subscription => "subscription",
BillingMode::Unknown => "unknown",
}
}
}
pub fn detect_billing(headers: &HeaderMap) -> BillingMode {
if headers.contains_key("x-api-key") {
BillingMode::ApiKey
} else if is_oauth_bearer(headers) {
BillingMode::Subscription
} else {
BillingMode::Unknown
}
}
fn is_oauth_bearer(headers: &HeaderMap) -> bool {
headers
.get(AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.map(|v| {
let t = v.trim_start();
t.len() > "bearer ".len() && t[.."bearer".len()].eq_ignore_ascii_case("bearer")
})
.unwrap_or(false)
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::{HeaderName, HeaderValue};
fn hdr(pairs: &[(&str, &str)]) -> HeaderMap {
let mut h = HeaderMap::new();
for (k, v) in pairs {
h.insert(
HeaderName::from_bytes(k.as_bytes()).unwrap(),
HeaderValue::from_str(v).unwrap(),
);
}
h
}
#[test]
fn x_api_key_is_api_key() {
let h = hdr(&[("x-api-key", "sk-ant-xyz")]);
assert_eq!(detect_billing(&h), BillingMode::ApiKey);
assert_eq!(detect_billing(&h).as_str(), "api_key");
}
#[test]
fn oauth_bearer_is_subscription() {
let h = hdr(&[("authorization", "Bearer oauth-token-value")]);
assert_eq!(detect_billing(&h), BillingMode::Subscription);
assert_eq!(detect_billing(&h).as_str(), "subscription");
}
#[test]
fn bearer_scheme_is_case_insensitive() {
let h = hdr(&[("authorization", "bearer lower-scheme")]);
assert_eq!(detect_billing(&h), BillingMode::Subscription);
}
#[test]
fn no_recognised_shape_is_unknown() {
assert_eq!(detect_billing(&HeaderMap::new()), BillingMode::Unknown);
let h = hdr(&[("authorization", "Bearer")]);
assert_eq!(detect_billing(&h), BillingMode::Unknown);
}
#[test]
fn x_api_key_wins_over_bearer() {
let h = hdr(&[("x-api-key", "sk-ant-xyz"), ("authorization", "Bearer t")]);
assert_eq!(detect_billing(&h), BillingMode::ApiKey);
}
}