From 52961f71c8e585dbe91bf8f5a561e9b26218504b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 2 Nov 2020 01:00:19 +0100 Subject: [PATCH] Add `other::Expect` header --- src/other/expect.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++ src/other/mod.rs | 2 + 2 files changed, 112 insertions(+) create mode 100644 src/other/expect.rs diff --git a/src/other/expect.rs b/src/other/expect.rs new file mode 100644 index 00000000..5e8fd307 --- /dev/null +++ b/src/other/expect.rs @@ -0,0 +1,110 @@ +use crate::ensure_eq_status; +use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, EXPECT}; + +use std::fmt::Debug; +use std::option; + +/// HTTP `Expect` header +/// +/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect) +/// +/// # Specifications +/// +/// - [RFC 7231, section 5.1.1: Expect](https://tools.ietf.org/html/rfc7231#section-5.1.1) +/// +/// # Examples +/// +/// ``` +/// # fn main() -> http_types::Result<()> { +/// # +/// use http_types::Response; +/// use http_types::other::Expect; +/// +/// let expect = Expect::new(); +/// +/// let mut res = Response::new(200); +/// expect.apply(&mut res); +/// +/// let expect = Expect::from_headers(res)?.unwrap(); +/// assert_eq!(expect, Expect::new()); +/// # +/// # Ok(()) } +/// ``` +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] +pub struct Expect { + _priv: (), +} + +impl Expect { + /// Create a new instance of `Expect`. + pub fn new() -> Self { + Self { _priv: () } + } + + /// Create an instance of `Expect` from a `Headers` instance. + pub fn from_headers(headers: impl AsRef) -> crate::Result> { + let headers = match headers.as_ref().get(EXPECT) { + Some(headers) => headers, + None => return Ok(None), + }; + + // If we successfully parsed the header then there's always at least one + // entry. We want the last entry. + let header = headers.iter().last().unwrap(); + ensure_eq_status!(header, "100-continue", 400, "malformed `Expect` header"); + + Ok(Some(Self { _priv: () })) + } + + /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance. + pub fn apply(&self, mut headers: impl AsMut) { + headers.as_mut().insert(EXPECT, self.value()); + } + + /// Get the `HeaderName`. + pub fn name(&self) -> HeaderName { + EXPECT + } + + /// Get the `HeaderValue`. + pub fn value(&self) -> HeaderValue { + let value = "100-continue"; + // SAFETY: the internal string is validated to be ASCII. + unsafe { HeaderValue::from_bytes_unchecked(value.into()) } + } +} + +impl ToHeaderValues for Expect { + type Iter = option::IntoIter; + fn to_header_values(&self) -> crate::Result { + // A HeaderValue will always convert into itself. + Ok(self.value().to_header_values().unwrap()) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::headers::Headers; + + #[test] + fn smoke() -> crate::Result<()> { + let expect = Expect::new(); + + let mut headers = Headers::new(); + expect.apply(&mut headers); + + let expect = Expect::from_headers(headers)?.unwrap(); + assert_eq!(expect, Expect::new()); + Ok(()) + } + + #[test] + fn bad_request_on_parse_error() -> crate::Result<()> { + let mut headers = Headers::new(); + headers.insert(EXPECT, ""); + let err = Expect::from_headers(headers).unwrap_err(); + assert_eq!(err.status(), 400); + Ok(()) + } +} diff --git a/src/other/mod.rs b/src/other/mod.rs index f78a49f7..81b66daf 100644 --- a/src/other/mod.rs +++ b/src/other/mod.rs @@ -1,5 +1,7 @@ //! Miscellaneous HTTP headers. mod date; +mod expect; pub use date::Date; +pub use expect::Expect;