use crate::bytes;
#[derive(PartialEq, Clone, Debug)]
pub enum Type {
Simple,
Relaxed,
}
impl std::string::ToString for Type {
fn to_string(&self) -> String {
match self {
Self::Simple => "simple".to_owned(),
Self::Relaxed => "relaxed".to_owned(),
}
}
}
pub(crate) fn canonicalize_body_simple(mut body: &[u8]) -> Vec<u8> {
if body.is_empty() {
return b"\r\n".to_vec();
}
while body.ends_with(b"\r\n\r\n") {
body = &body[..body.len() - 2];
}
body.to_vec()
}
pub(crate) fn canonicalize_body_relaxed(body: &[u8]) -> Vec<u8> {
let mut body = body.to_vec();
bytes::replace(&mut body, '\t', ' ');
let mut previous = false;
body.retain(|c| {
if *c == b' ' {
if previous {
false
} else {
previous = true;
true
}
} else {
previous = false;
true
}
});
while let Some(idx) = bytes::find(&body, b" \r\n") {
body.remove(idx);
}
while body.ends_with(b"\r\n\r\n") {
body.remove(body.len() - 1);
body.remove(body.len() - 1);
}
if !body.is_empty() && !body.ends_with(b"\r\n") {
body.push(b'\r');
body.push(b'\n');
}
body
}
pub(crate) fn canonicalize_header_simple(key: &str, value: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(key.as_bytes());
out.extend_from_slice(b": ");
out.extend_from_slice(value);
out.extend_from_slice(b"\r\n");
out
}
pub(crate) fn canonicalize_header_relaxed(key: &str, value: &[u8]) -> Vec<u8> {
let key = key.to_lowercase();
let key = key.trim_end();
let value = canonicalize_header_value_relaxed(value);
let mut out = Vec::new();
out.extend_from_slice(key.as_bytes());
out.extend_from_slice(b":");
out.extend_from_slice(&value);
out.extend_from_slice(b"\r\n");
out
}
fn canonicalize_header_value_relaxed(value: &[u8]) -> Vec<u8> {
let mut value = value.to_vec();
bytes::replace(&mut value, '\t', ' ');
value = bytes::replace_slice(&value, b"\r\n", b"");
while value.ends_with(b" ") {
value.remove(value.len() - 1);
}
while value.starts_with(b" ") {
value.remove(0);
}
let mut previous = false;
value.retain(|c| {
if *c == b' ' {
if previous {
false
} else {
previous = true;
true
}
} else {
previous = false;
true
}
});
value
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_canonicalize_header_relaxed() {
assert_eq!(
canonicalize_header_relaxed("SUBJect", b" AbC\r\n"),
b"subject:AbC\r\n"
);
assert_eq!(
canonicalize_header_relaxed("Subject \t", b"\t Your Name\t \r\n"),
b"subject:Your Name\r\n"
);
assert_eq!(
canonicalize_header_relaxed("Subject \t", b"\t Kimi \t \r\n No \t\r\n Na Wa\r\n"),
b"subject:Kimi No Na Wa\r\n"
);
}
#[test]
fn test_canonicalize_body_relaxed() {
assert_eq!(canonicalize_body_relaxed(b"\r\n"), b"\r\n");
assert_eq!(canonicalize_body_relaxed(b"hey \r\n"), b"hey\r\n");
}
}