use std::borrow::Cow;
use http::{HeaderValue, header::HeaderName};
use web_time::{Duration, SystemTime, UNIX_EPOCH};
mod content_disposition;
mod rfc8187;
pub use self::content_disposition::{
ContentDisposition, ContentDispositionParseError, ContentDispositionType, TokenString,
TokenStringParseError,
};
use crate::api::error::{HeaderDeserializationError, HeaderSerializationError};
pub const APPLICATION_JSON: HeaderValue = HeaderValue::from_static("application/json");
pub const APPLICATION_OCTET_STREAM: HeaderValue =
HeaderValue::from_static("application/octet-stream");
pub const CROSS_ORIGIN_RESOURCE_POLICY: HeaderName =
HeaderName::from_static("cross-origin-resource-policy");
pub const fn is_tchar(b: u8) -> bool {
b.is_ascii_alphanumeric()
|| matches!(
b,
b'!' | b'#'
| b'$'
| b'%'
| b'&'
| b'\''
| b'*'
| b'+'
| b'-'
| b'.'
| b'^'
| b'_'
| b'`'
| b'|'
| b'~'
)
}
pub fn is_token(bytes: &[u8]) -> bool {
bytes.iter().all(|b| is_tchar(*b))
}
pub fn is_token_string(s: &str) -> bool {
is_token(s.as_bytes())
}
pub const fn is_vchar(c: char) -> bool {
matches!(c, '\x21'..='\x7E')
}
pub const fn is_ascii_string_quotable(c: char) -> bool {
is_vchar(c) || matches!(c, '\x09' | '\x20')
}
pub fn sanitize_for_ascii_quoted_string(value: &str) -> Cow<'_, str> {
if value.chars().all(is_ascii_string_quotable) {
return Cow::Borrowed(value);
}
Cow::Owned(value.chars().filter(|c| is_ascii_string_quotable(*c)).collect())
}
pub fn quote_ascii_string_if_required(value: &str) -> Cow<'_, str> {
if !value.is_empty() && is_token_string(value) {
return Cow::Borrowed(value);
}
let value = value.replace('\\', r#"\\"#).replace('"', r#"\""#);
Cow::Owned(format!("\"{value}\""))
}
pub fn unescape_string(s: &str) -> String {
let mut is_escaped = false;
s.chars()
.filter(|c| {
is_escaped = *c == '\\' && !is_escaped;
!is_escaped
})
.collect()
}
pub fn system_time_to_http_date(
time: &SystemTime,
) -> Result<HeaderValue, HeaderSerializationError> {
let mut buffer = [0; 29];
let duration =
time.duration_since(UNIX_EPOCH).map_err(|_| HeaderSerializationError::InvalidHttpDate)?;
date_header::format(duration.as_secs(), &mut buffer)
.map_err(|_| HeaderSerializationError::InvalidHttpDate)?;
Ok(HeaderValue::from_bytes(&buffer).expect("date_header should produce a valid header value"))
}
pub fn http_date_to_system_time(
value: &HeaderValue,
) -> Result<SystemTime, HeaderDeserializationError> {
let bytes = value.as_bytes();
let ts = date_header::parse(bytes).map_err(|_| HeaderDeserializationError::InvalidHttpDate)?;
UNIX_EPOCH
.checked_add(Duration::from_secs(ts))
.ok_or(HeaderDeserializationError::InvalidHttpDate)
}