use super::{AxumSnafu, Error, InvalidHeaderNameSnafu, InvalidHeaderValueSnafu};
use axum::body::{Body, Bytes};
use axum::http::{HeaderMap, HeaderValue, header, header::HeaderName};
use axum_extra::extract::cookie::{Cookie, CookieJar};
use cookie::CookieBuilder;
use http_body_util::BodyExt;
use nanoid::nanoid;
use snafu::ResultExt;
use std::time::Duration;
type Result<T> = std::result::Result<T, Error>;
pub fn insert_headers<K, V>(
headers: &mut HeaderMap<HeaderValue>,
values: impl IntoIterator<Item = (K, V)>,
) -> Result<()>
where
K: AsRef<str>,
V: AsRef<str>,
{
for (name, value) in values {
let name = name.as_ref();
let value = value.as_ref();
if name.is_empty() || value.is_empty() {
continue;
}
headers.insert(
HeaderName::try_from(name).context(InvalidHeaderNameSnafu)?,
HeaderValue::try_from(value).context(InvalidHeaderValueSnafu)?,
);
}
Ok(())
}
pub fn set_header_if_not_exist(
headers: &mut HeaderMap<HeaderValue>,
name: &str,
value: &str,
) -> Result<()> {
if headers.contains_key(name) {
return Ok(());
}
let values = [(name.to_string(), value.to_string())];
insert_headers(headers, values)
}
pub fn set_no_cache_if_not_exist(headers: &mut HeaderMap<HeaderValue>) {
let _ = set_header_if_not_exist(headers, header::CACHE_CONTROL.as_str(), "no-cache");
}
pub fn get_header_value<'a>(headers: &'a HeaderMap<HeaderValue>, key: &str) -> Option<&'a str> {
headers.get(key).and_then(|value| value.to_str().ok())
}
pub async fn read_http_body(body: Body) -> Result<Bytes> {
let bytes = body.collect().await.context(AxumSnafu)?.to_bytes();
Ok(bytes)
}
const DEVICE_ID_NAME: &str = "device";
const DEVICE_ID_LIFETIME: Duration = Duration::from_secs(365 * 24 * 60 * 60);
pub fn get_device_id_from_cookie(jar: &CookieJar) -> Option<&str> {
jar.get(DEVICE_ID_NAME).map(|cookie| cookie.value())
}
pub fn generate_device_id_cookie() -> CookieBuilder<'static> {
let expires = cookie::time::OffsetDateTime::now_utc()
.saturating_add(cookie::time::Duration::try_from(DEVICE_ID_LIFETIME).unwrap_or_default());
Cookie::build((DEVICE_ID_NAME, nanoid!(16)))
.http_only(true)
.expires(expires)
.path("/")
}