//! Pieces pertaining to the HTTP message protocol. use http::{HeaderMap, Method, StatusCode, Uri, Version}; pub(crate) use self::body_length::DecodedLength; pub(crate) use self::h1::{dispatch, Conn, ServerTransaction}; pub(crate) mod h1; pub(crate) mod h2; /// An Incoming Message head. Includes request/status line, and headers. #[derive(Clone, Debug, Default, PartialEq)] pub struct MessageHead { /// HTTP version of the message. pub version: Version, /// Subject (request line or status line) of Incoming message. pub subject: S, /// Headers of the Incoming message. pub headers: HeaderMap, } /// An incoming request message. pub type RequestHead = MessageHead; #[derive(Debug, Default, PartialEq)] pub struct RequestLine(pub Method, pub Uri); /// An incoming response message. pub type ResponseHead = MessageHead; #[derive(Debug)] pub enum BodyLength { /// Content-Length Known(u64), /// Transfer-Encoding: chunked (if h1) Unknown, } /// Status of when a Disaptcher future completes. pub(crate) enum Dispatched { /// Dispatcher completely shutdown connection. Shutdown, /// Dispatcher has pending upgrade, and so did not shutdown. Upgrade(crate::upgrade::Pending), } /// A separate module to encapsulate the invariants of the DecodedLength type. mod body_length { use std::fmt; #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) struct DecodedLength(u64); const MAX_LEN: u64 = std::u64::MAX - 2; impl DecodedLength { pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX); pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1); pub(crate) const ZERO: DecodedLength = DecodedLength(0); #[cfg(test)] pub(crate) fn new(len: u64) -> Self { debug_assert!(len <= MAX_LEN); DecodedLength(len) } /// Takes the length as a content-length without other checks. /// /// Should only be called if previously confirmed this isn't /// CLOSE_DELIMITED or CHUNKED. #[inline] pub(crate) fn danger_len(self) -> u64 { debug_assert!(self.0 < Self::CHUNKED.0); self.0 } /// Converts to an Option representing a Known or Unknown length. pub(crate) fn into_opt(self) -> Option { match self { DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None, DecodedLength(known) => Some(known), } } /// Checks the `u64` is within the maximum allowed for content-length. pub(crate) fn checked_new(len: u64) -> Result { if len <= MAX_LEN { Ok(DecodedLength(len)) } else { warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN); Err(crate::error::Parse::TooLarge) } } pub(crate) fn sub_if(&mut self, amt: u64) { match *self { DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (), DecodedLength(ref mut known) => { *known -= amt; } } } } impl fmt::Debug for DecodedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"), DecodedLength::CHUNKED => f.write_str("CHUNKED"), DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(), } } } impl fmt::Display for DecodedLength { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"), DecodedLength::CHUNKED => f.write_str("chunked encoding"), DecodedLength::ZERO => f.write_str("empty"), DecodedLength(n) => write!(f, "content-length ({} bytes)", n), } } } #[cfg(test)] mod tests { use super::*; #[test] fn sub_if_known() { let mut len = DecodedLength::new(30); len.sub_if(20); assert_eq!(len.0, 10); } #[test] fn sub_if_chunked() { let mut len = DecodedLength::CHUNKED; len.sub_if(20); assert_eq!(len, DecodedLength::CHUNKED); } } }