use crate::ascii::Ascii;
use crate::req_resp::RequestOrResponse;
use crate::solicit::header::{HeaderError, HeaderResult};
use bytes::Bytes;
use bytes::BytesMut;
use std::fmt;
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum PseudoHeaderName {
Method = 0,
Scheme = 1,
Authority = 2,
Path = 3,
Status = 4,
}
impl PseudoHeaderName {
pub fn name(&self) -> &'static str {
match *self {
PseudoHeaderName::Method => ":method",
PseudoHeaderName::Scheme => ":scheme",
PseudoHeaderName::Authority => ":authority",
PseudoHeaderName::Path => ":path",
PseudoHeaderName::Status => ":status",
}
}
pub fn parse(value: &[u8]) -> HeaderResult<PseudoHeaderName> {
match value {
b":method" => Ok(PseudoHeaderName::Method),
b":scheme" => Ok(PseudoHeaderName::Scheme),
b":authority" => Ok(PseudoHeaderName::Authority),
b":path" => Ok(PseudoHeaderName::Path),
b":status" => Ok(PseudoHeaderName::Status),
_ => Err(HeaderError::UnknownPseudoHeader),
}
}
pub fn req_or_resp(&self) -> RequestOrResponse {
match *self {
PseudoHeaderName::Method => RequestOrResponse::Request,
PseudoHeaderName::Scheme => RequestOrResponse::Request,
PseudoHeaderName::Authority => RequestOrResponse::Request,
PseudoHeaderName::Path => RequestOrResponse::Request,
PseudoHeaderName::Status => RequestOrResponse::Response,
}
}
pub fn name_bytes(&self) -> Bytes {
Bytes::from_static(self.name().as_bytes())
}
pub fn names(request_or_response: RequestOrResponse) -> &'static [PseudoHeaderName] {
static REQUEST_HEADERS: &[PseudoHeaderName] = &[
PseudoHeaderName::Method,
PseudoHeaderName::Scheme,
PseudoHeaderName::Authority,
PseudoHeaderName::Path,
];
static RESPONSE_HEADERS: &[PseudoHeaderName] = &[PseudoHeaderName::Status];
match request_or_response {
RequestOrResponse::Request => REQUEST_HEADERS,
RequestOrResponse::Response => RESPONSE_HEADERS,
}
}
pub fn all_names() -> &'static [PseudoHeaderName] {
static ALL_HEADERS: &[PseudoHeaderName] = &[
PseudoHeaderName::Method,
PseudoHeaderName::Scheme,
PseudoHeaderName::Authority,
PseudoHeaderName::Path,
PseudoHeaderName::Status,
];
ALL_HEADERS
}
}
impl Into<Bytes> for PseudoHeaderName {
fn into(self) -> Bytes {
self.name_bytes()
}
}
impl fmt::Display for PseudoHeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self.name(), f)
}
}
#[derive(Default)]
pub(crate) struct PseudoHeaderNameSet {
headers_mask: u32,
}
impl PseudoHeaderNameSet {
pub fn new() -> PseudoHeaderNameSet {
Default::default()
}
pub fn insert(&mut self, value: PseudoHeaderName) -> bool {
let contains = self.contains(value);
self.headers_mask |= 1 << (value as u32);
!contains
}
pub fn contains(&self, value: PseudoHeaderName) -> bool {
self.headers_mask & (1 << (value as u32)) != 0
}
}
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
struct RegularHeaderName(Ascii);
impl RegularHeaderName {
pub fn from_bytes(bs: Bytes) -> Result<RegularHeaderName, (HeaderError, Bytes)> {
if bs.is_empty() {
return Err((HeaderError::EmptyName, bs));
}
for &b in &bs {
if !b.is_ascii() {
return Err((HeaderError::HeaderNameNotAscii, bs));
}
if b.is_ascii_control() {
return Err((HeaderError::IncorrectCharInName, bs));
}
if b.is_ascii_uppercase() {
return Err((HeaderError::IncorrectCharInName, bs));
}
let bad_chars = b"()<>@,;:\\\"/[]?={} \t";
if bad_chars.contains(&b) {
return Err((HeaderError::IncorrectCharInName, bs));
}
}
unsafe { Ok(RegularHeaderName(Ascii::from_bytes_unchecked(bs))) }
}
pub const unsafe fn _from_bytes_unchecked(bs: Bytes) -> RegularHeaderName {
RegularHeaderName(Ascii::from_bytes_unchecked(bs))
}
}
impl Into<Bytes> for RegularHeaderName {
fn into(self) -> Bytes {
self.0.into()
}
}
impl fmt::Display for RegularHeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
#[derive(Eq, PartialEq, Hash, Clone)]
enum HeaderNameEnum {
Pseudo(PseudoHeaderName),
Regular(RegularHeaderName),
}
impl fmt::Display for HeaderNameEnum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
HeaderNameEnum::Pseudo(p) => fmt::Display::fmt(p, f),
HeaderNameEnum::Regular(r) => fmt::Display::fmt(r, f),
}
}
}
#[derive(Eq, PartialEq, Hash, Clone)]
pub struct HeaderName(HeaderNameEnum);
impl From<PseudoHeaderName> for HeaderName {
fn from(p: PseudoHeaderName) -> Self {
HeaderName(HeaderNameEnum::Pseudo(p))
}
}
impl<'a> From<&'a str> for HeaderName {
fn from(s: &'a str) -> Self {
HeaderName::new(Bytes::copy_from_slice(s.as_bytes()))
}
}
impl<'a> From<&'a [u8]> for HeaderName {
fn from(s: &'a [u8]) -> Self {
HeaderName::new(Bytes::copy_from_slice(s))
}
}
impl From<String> for HeaderName {
fn from(s: String) -> Self {
HeaderName::new(s)
}
}
impl From<Vec<u8>> for HeaderName {
fn from(s: Vec<u8>) -> Self {
HeaderName::new(s)
}
}
impl From<Bytes> for HeaderName {
fn from(s: Bytes) -> Self {
HeaderName::new(s)
}
}
impl Into<Bytes> for HeaderName {
fn into(self) -> Bytes {
match self.0 {
HeaderNameEnum::Pseudo(n) => n.into(),
HeaderNameEnum::Regular(n) => n.into(),
}
}
}
impl AsRef<[u8]> for HeaderName {
fn as_ref(&self) -> &[u8] {
self.name().as_bytes()
}
}
impl AsRef<str> for HeaderName {
fn as_ref(&self) -> &str {
self.name()
}
}
impl HeaderName {
pub fn pseudo(name: PseudoHeaderName) -> HeaderName {
HeaderName(HeaderNameEnum::Pseudo(name))
}
pub fn new(name: impl Into<Bytes>) -> HeaderName {
let mut name = name.into();
make_ascii_lowercase(&mut name);
match HeaderName::new_validate(name) {
Ok(h) => h,
Err((e, name)) => panic!("incorrect header name: {:?}: {:?}", name, e),
}
}
pub fn new_validate(name: Bytes) -> Result<HeaderName, (HeaderError, Bytes)> {
if name.len() == 0 {
return Err((HeaderError::EmptyName, name));
}
Ok(if name[0] == b':' {
HeaderName(HeaderNameEnum::Pseudo(
PseudoHeaderName::parse(&name).map_err(|e| (e, name))?,
))
} else {
let connection_specific_headers = [
"connection",
"keep-alive",
"proxy-connection",
"transfer-encoding",
"upgrade",
];
for s in &connection_specific_headers {
if name == s.as_bytes() {
return Err((HeaderError::ConnectionSpecificHeader(s), name));
}
}
HeaderName(HeaderNameEnum::Regular(RegularHeaderName::from_bytes(
name,
)?))
})
}
pub fn name(&self) -> &str {
match &self.0 {
HeaderNameEnum::Pseudo(p) => p.name(),
HeaderNameEnum::Regular(r) => r.0.as_str(),
}
}
pub fn is_pseudo(&self) -> bool {
match self.0 {
HeaderNameEnum::Pseudo(_) => true,
HeaderNameEnum::Regular(_) => false,
}
}
pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
match self.0 {
HeaderNameEnum::Pseudo(p) => Some(p),
HeaderNameEnum::Regular(_) => None,
}
}
}
impl fmt::Debug for HeaderName {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HeaderName({:?})", self.name())
}
}
impl fmt::Display for HeaderName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
fn make_ascii_lowercase(bytes: &mut Bytes) {
if bytes.as_ref().iter().all(|c| !c.is_ascii_uppercase()) {
return;
}
let mut bytes_mut = BytesMut::from(&bytes[..]);
bytes_mut.as_mut().make_ascii_lowercase();
*bytes = bytes_mut.freeze();
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn header_name_new_to_lower() {
assert_eq!("content-type", HeaderName::new("Content-Type").name());
}
#[test]
fn header_name_display() {
assert_eq!(
":method",
format!(
"{}",
HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
)
);
assert_eq!(
"x-fgfg",
format!(
"{}",
HeaderName(HeaderNameEnum::Regular(
RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
))
)
);
}
#[test]
fn header_name_debug() {
assert_eq!(
"HeaderName(\":method\")",
format!(
"{:?}",
HeaderName(HeaderNameEnum::Pseudo(PseudoHeaderName::Method))
)
);
assert_eq!(
"HeaderName(\"x-fgfg\")",
format!(
"{:?}",
HeaderName(HeaderNameEnum::Regular(
RegularHeaderName::from_bytes(Bytes::from("x-fgfg")).unwrap()
))
)
);
}
}