pub(crate) fn is_token_char(b: u8) -> bool {
matches!(
b,
b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' |
b'0'..=b'9' | b'A'..=b'Z' | b'^' | b'_' | b'`' | b'a'..=b'z' | b'|' | b'~'
)
}
pub(crate) fn is_valid_header_name(name: &str) -> bool {
!name.is_empty() && name.bytes().all(is_token_char)
}
pub(crate) fn is_valid_token(s: &str) -> bool {
!s.is_empty() && s.bytes().all(is_token_char)
}
pub(crate) fn is_valid_field_vchar(b: u8) -> bool {
matches!(b, 0x09 | 0x20..=0x7E | 0x80..=0xFF)
}
pub(crate) fn is_valid_field_value(value: &str) -> bool {
value.bytes().all(is_valid_field_vchar)
}
pub(crate) fn is_valid_method(method: &str) -> bool {
!method.is_empty() && method.bytes().all(is_token_char)
}
pub(crate) fn is_valid_protocol_version(version: &str) -> bool {
let bytes = version.as_bytes();
let slash_pos = match bytes.iter().position(|&b| b == b'/') {
Some(pos) => pos,
None => return false,
};
if slash_pos == 0 {
return false;
}
if !bytes[..slash_pos].iter().all(|&b| is_token_char(b)) {
return false;
}
let after_slash = &bytes[slash_pos + 1..];
let dot_pos = match after_slash.iter().position(|&b| b == b'.') {
Some(pos) => pos,
None => return false,
};
if dot_pos == 0 {
return false;
}
if !after_slash[..dot_pos].iter().all(|b| b.is_ascii_digit()) {
return false;
}
let after_dot = &after_slash[dot_pos + 1..];
if after_dot.is_empty() {
return false;
}
after_dot.iter().all(|b| b.is_ascii_digit())
}
pub(crate) fn is_valid_version_for_encode(version: &str) -> bool {
!version.is_empty() && version.bytes().all(|b| matches!(b, 0x21..=0x7E))
}
pub(crate) fn is_valid_status_code(code: u16) -> bool {
(100..=599).contains(&code)
}
pub(crate) fn is_valid_reason_phrase(phrase: &str) -> bool {
phrase
.bytes()
.all(|b| matches!(b, 0x09 | 0x20..=0x7E | 0x80..=0xFF))
}
pub(crate) const RFC3986_EXCLUDED: &[u8] = b"\"#<>\\^`{|}";
pub(crate) fn is_valid_request_target(target: &str) -> bool {
if target.is_empty() {
return false;
}
let bytes = target.as_bytes();
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b <= 0x20 || b == 0x7F {
return false;
}
if RFC3986_EXCLUDED.contains(&b) {
return false;
}
if b == b'%' {
if i + 2 >= bytes.len() {
return false; }
let high = bytes[i + 1];
let low = bytes[i + 2];
if !high.is_ascii_hexdigit() || !low.is_ascii_hexdigit() {
return false; }
if high == b'0' && low == b'0' {
return false;
}
i += 3;
continue;
}
i += 1;
}
true
}
pub(crate) fn is_pchar_or_slash(b: u8) -> bool {
is_pchar_byte(b) || b == b'/'
}
pub(crate) fn is_pchar_byte(b: u8) -> bool {
is_unreserved_byte(b) || is_sub_delim_byte(b) || b == b':' || b == b'@'
}
pub(crate) fn is_query_char(b: u8) -> bool {
is_pchar_byte(b) || b == b'/' || b == b'?'
}
pub(crate) fn is_unreserved_byte(b: u8) -> bool {
b.is_ascii_alphanumeric() || b == b'-' || b == b'.' || b == b'_' || b == b'~'
}
pub(crate) fn is_sub_delim_byte(b: u8) -> bool {
matches!(
b,
b'!' | b'$' | b'&' | b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | b'='
)
}