use std::fmt;
use std::iter::FromIterator;
use std::result;
use std::str;
use std::str::FromStr;
use crate::headers_place::HeadersPlace;
use crate::req_resp::RequestOrResponse;
use crate::assert_types::*;
use bytes::Bytes;
use crate::solicit::header::method::METHOD_GET;
use crate::solicit::header::method::METHOD_POST;
use crate::solicit::header::name::HeaderName;
use crate::solicit::header::name::PseudoHeaderName;
use crate::solicit::header::name::PseudoHeaderNameSet;
use crate::solicit::header::status::status_to_header_value;
use crate::HeaderValue;
pub(crate) mod method;
pub(crate) mod name;
pub(crate) mod status;
pub(crate) mod value;
#[derive(PartialEq, Eq, Hash, Clone)]
pub struct Header {
name: HeaderName,
pub value: HeaderValue,
}
impl fmt::Debug for Header {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Header")
.field("name", &self.name.name())
.field("value", &self.value)
.finish()
}
}
fn _assert_header_sync_send() {
assert_sync::<Header>();
assert_send::<Header>();
}
#[derive(Debug)]
pub enum HeaderError {
UnknownPseudoHeader,
EmptyName,
EmptyValue(PseudoHeaderName),
IncorrectCharInName,
IncorrectCharInValue,
HeaderNameNotAscii,
HeaderValueNotAscii,
UnexpectedPseudoHeader(PseudoHeaderName),
PseudoHeadersInTrailers,
PseudoHeadersAfterRegularHeaders,
MoreThanOnePseudoHeader(PseudoHeaderName),
MissingPseudoHeader(PseudoHeaderName),
ConnectionSpecificHeader(&'static str),
TeCanOnlyContainTrailer,
}
pub type HeaderResult<T> = result::Result<T, HeaderError>;
impl Header {
pub fn new_validate(name: Bytes, value: Bytes) -> HeaderResult<Header> {
let name = HeaderName::new_validate(name).map_err(|(e, _)| e)?;
Ok(Header {
name,
value: HeaderValue::from(value),
})
}
pub fn new<N: Into<HeaderName>, V: Into<HeaderValue>>(name: N, value: V) -> Header {
Header {
name: name.into(),
value: value.into(),
}
}
fn method(value: impl Into<HeaderValue>) -> Header {
Header::new(PseudoHeaderName::Method, value.into())
}
fn method_get() -> Header {
Header::method(METHOD_GET)
}
fn path(path: impl Into<HeaderValue>) -> Header {
Header::new(PseudoHeaderName::Path, path.into())
}
fn status(code: u32) -> Header {
Header::new(PseudoHeaderName::Status, status_to_header_value(code))
}
pub fn name(&self) -> &str {
self.name.name()
}
pub fn value(&self) -> &[u8] {
self.value.as_slice()
}
pub fn format(&self) -> String {
format!(
"{}: {}",
self.name(),
String::from_utf8_lossy(self.value.as_slice())
)
}
pub fn is_preudo_header(&self) -> bool {
self.name.is_pseudo()
}
pub fn pseudo_header_name(&self) -> Option<PseudoHeaderName> {
self.name.pseudo_header_name()
}
pub fn validate(&self, req_or_resp: RequestOrResponse) -> HeaderResult<()> {
if let Some(h) = self.pseudo_header_name() {
if h.req_or_resp() != req_or_resp {
return Err(HeaderError::UnexpectedPseudoHeader(h));
}
}
if req_or_resp == RequestOrResponse::Request && self.name() == "te" {
if self.value.as_slice() != b"trailers" {
return Err(HeaderError::TeCanOnlyContainTrailer);
}
}
Ok(())
}
}
impl<N: Into<HeaderName>, V: Into<HeaderValue>> From<(N, V)> for Header {
fn from(p: (N, V)) -> Header {
Header::new(p.0, p.1)
}
}
#[derive(Default, Debug, PartialEq, Eq, Clone)]
pub struct Headers {
headers: Vec<Header>,
pseudo_count: usize,
}
impl Headers {
pub fn new() -> Headers {
Default::default()
}
pub fn from_vec(mut headers: Vec<Header>) -> Headers {
headers.sort_by_key(|h| !h.is_preudo_header());
let pseudo_count = headers.iter().take_while(|h| h.is_preudo_header()).count();
Headers {
headers,
pseudo_count,
}
}
pub(crate) fn from_vec_pseudo_first(headers: Vec<Header>) -> Result<Headers, HeaderError> {
let mut saw_regular_header = false;
let mut pseudo_count = 0;
for header in &headers {
if header.is_preudo_header() {
if saw_regular_header {
return Err(HeaderError::PseudoHeadersAfterRegularHeaders);
}
pseudo_count += 1;
} else {
saw_regular_header = true;
}
}
return Ok(Headers {
headers,
pseudo_count,
});
}
pub fn iter(&self) -> impl Iterator<Item = &Header> {
self.headers.iter()
}
fn pseudo_headers(&self) -> &[Header] {
&self.headers[..self.pseudo_count]
}
fn regular_headers(&self) -> &[Header] {
&self.headers[self.pseudo_count..]
}
pub fn dump(&self) -> String {
let mut r = String::new();
for h in &self.headers {
r.push_str(&h.format());
r.push_str("\n");
}
r
}
pub fn new_get(path: impl Into<HeaderValue>) -> Headers {
Headers::from_vec(vec![Header::method_get(), Header::path(path)])
}
pub fn new_post(path: impl Into<HeaderValue>) -> Headers {
Headers::from_vec(vec![Header::method(METHOD_POST), Header::path(path)])
}
pub fn new_status(code: u32) -> Headers {
Headers::from_vec(vec![Header::status(code)])
}
pub fn ok_200() -> Headers {
Headers::new_status(200)
}
pub fn not_found_404() -> Headers {
Headers::new_status(404)
}
pub fn internal_error_500() -> Headers {
Headers::new_status(500)
}
pub fn redirect_302(location: impl Into<HeaderValue>) -> Headers {
let mut headers = Headers::new_status(302);
headers.add("location", location);
headers
}
pub(crate) fn validate(
&self,
req_or_resp: RequestOrResponse,
headers_place: HeadersPlace,
) -> HeaderResult<()> {
let mut pseudo_headers_met = PseudoHeaderNameSet::new();
for header in self.pseudo_headers() {
debug_assert!(header.is_preudo_header());
header.validate(req_or_resp)?;
let header_name = header.pseudo_header_name().unwrap();
if headers_place == HeadersPlace::Trailing {
return Err(HeaderError::PseudoHeadersInTrailers);
}
if !pseudo_headers_met.insert(header_name) {
return Err(HeaderError::MoreThanOnePseudoHeader(header_name));
}
if header_name == PseudoHeaderName::Path {
if header.value.as_slice().is_empty() {
return Err(HeaderError::EmptyValue(header_name));
}
}
}
for header in self.regular_headers() {
header.validate(req_or_resp)?;
debug_assert!(!header.is_preudo_header());
}
if headers_place == HeadersPlace::Initial {
let required_headers = match req_or_resp {
RequestOrResponse::Request => &[
PseudoHeaderName::Method,
PseudoHeaderName::Scheme,
PseudoHeaderName::Path,
][..],
RequestOrResponse::Response => &[PseudoHeaderName::Status][..],
};
for &required in required_headers {
if !pseudo_headers_met.contains(required) {
return Err(HeaderError::MissingPseudoHeader(required));
}
}
}
Ok(())
}
pub fn get_opt<'a>(&'a self, name: &str) -> Option<&'a str> {
let headers = if name.starts_with(':') {
self.pseudo_headers()
} else {
self.regular_headers()
};
headers
.iter()
.find(|h| h.name() == name)
.and_then(|h| str::from_utf8(h.value()).ok())
}
pub fn get<'a>(&'a self, name: &str) -> &'a str {
self.get_opt(name).unwrap()
}
pub fn get_opt_parse<I: FromStr>(&self, name: &str) -> Option<I> {
self.get_opt(name).and_then(|h| h.parse().ok())
}
pub fn status(&self) -> u32 {
self.get_opt_parse(":status").unwrap()
}
pub fn path(&self) -> &str {
self.get(":path")
}
pub fn method(&self) -> &str {
self.get(":method")
}
pub fn content_length(&self) -> Option<u64> {
match self.get_opt("content-length") {
Some(v) => v.parse().ok(),
None => None,
}
}
pub fn add(&mut self, name: impl Into<HeaderName>, value: impl Into<HeaderValue>) {
self.add_header(Header::new(name, value));
}
pub fn add_header(&mut self, header: Header) {
if header.is_preudo_header() {
let pseudo_count = self.pseudo_count;
self.headers.insert(pseudo_count, header);
self.pseudo_count += 1;
} else {
self.headers.push(header);
}
}
pub fn extend(&mut self, headers: Headers) {
self.headers.reserve(headers.headers.len());
for h in headers.headers {
self.add_header(h);
}
}
}
impl FromIterator<Header> for Headers {
fn from_iter<T: IntoIterator<Item = Header>>(iter: T) -> Headers {
Headers::from_vec(iter.into_iter().collect())
}
}
#[cfg(test)]
mod test {
use crate::solicit::header::Header;
#[test]
fn test_partial_eq_of_headers() {
let fully_static = Header::new(&b":method"[..], &b"GET"[..]);
let static_name = Header::new(&b":method"[..], b"GET".to_vec());
let other = Header::new(&b":path"[..], &b"/"[..]);
assert_eq!(fully_static, static_name);
assert_ne!(fully_static, other);
assert_ne!(static_name, other);
}
#[test]
fn test_debug() {
assert_eq!(
"Header { name: \":method\", value: \"GET\" }",
format!("{:?}", Header::new(&b":method"[..], &b"GET"[..]))
);
assert_eq!(
"Header { name: \":method\", value: \"\\t\" }",
format!("{:?}", Header::new(&b":method"[..], &b"\t"[..]))
);
}
}