Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add other::Expect header #266

Merged
merged 1 commit into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions src/other/expect.rs
Original file line number Diff line number Diff line change
@@ -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<Headers>) -> crate::Result<Option<Self>> {
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>) {
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";
yoshuawuyts marked this conversation as resolved.
Show resolved Hide resolved
// SAFETY: the internal string is validated to be ASCII.
unsafe { HeaderValue::from_bytes_unchecked(value.into()) }
}
}

impl ToHeaderValues for Expect {
type Iter = option::IntoIter<HeaderValue>;
fn to_header_values(&self) -> crate::Result<Self::Iter> {
// 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, "<nori ate the tag. yum.>");
let err = Expect::from_headers(headers).unwrap_err();
assert_eq!(err.status(), 400);
Ok(())
}
}
2 changes: 2 additions & 0 deletions src/other/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Miscellaneous HTTP headers.

mod date;
mod expect;

pub use date::Date;
pub use expect::Expect;