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

feat(headers): add Warning header #910

Merged
merged 1 commit into from
Sep 6, 2016
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
2 changes: 2 additions & 0 deletions src/header/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ pub use self::transfer_encoding::TransferEncoding;
pub use self::upgrade::{Upgrade, Protocol, ProtocolName};
pub use self::user_agent::UserAgent;
pub use self::vary::Vary;
pub use self::warning::Warning;

#[doc(hidden)]
#[macro_export]
Expand Down Expand Up @@ -420,3 +421,4 @@ mod transfer_encoding;
mod upgrade;
mod user_agent;
mod vary;
mod warning;
174 changes: 174 additions & 0 deletions src/header/common/warning.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
use std::fmt;
use std::str::{FromStr};
use header::{Header, HttpDate, Raw};
use header::parsing::from_one_raw_str;

/// `Warning` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.5)
///
/// The `Warning` header field can be be used to carry additional information
/// about the status or transformation of a message that might not be reflected
/// in the status code. This header is sometimes used as backwards
/// compatible way to notify of a deprecated API.
///
/// # ABNF
/// ```plain
/// Warning = 1#warning-value
/// warning-value = warn-code SP warn-agent SP warn-text
/// [ SP warn-date ]
/// warn-code = 3DIGIT
/// warn-agent = ( uri-host [ ":" port ] ) / pseudonym
/// ; the name or pseudonym of the server adding
/// ; the Warning header field, for use in debugging
/// ; a single "-" is recommended when agent unknown
/// warn-text = quoted-string
/// warn-date = DQUOTE HTTP-date DQUOTE
/// ```
///
/// # Example values
/// * `Warning: 112 - "network down" "Sat, 25 Aug 2012 23:34:45 GMT"`
/// * `Warning: 299 - "Deprecated API " "Tue, 15 Nov 1994 08:12:31 GMT"`
/// * `Warning: 299 api.hyper.rs:8080 "Deprecated API : use newapi.hyper.rs instead."`
/// * `Warning: 299 api.hyper.rs:8080 "Deprecated API : use newapi.hyper.rs instead." "Tue, 15 Nov 1994 08:12:31 GMT"`
///
/// # Examples
/// ```
/// use hyper::header::{Headers, Warning};
///
/// let mut headers = Headers::new();
/// headers.set(
/// Warning{
/// code: 299,
/// agent: "api.hyper.rs".to_owned(),
/// text: "Deprecated".to_owned(),
/// date: None
/// }
/// );
/// ```
/// ```
/// use hyper::header::{Headers, HttpDate, Warning};
///
/// let mut headers = Headers::new();
/// headers.set(
/// Warning{
/// code: 299,
/// agent: "api.hyper.rs".to_owned(),
/// text: "Deprecated".to_owned(),
/// date: "Tue, 15 Nov 1994 08:12:31 GMT".parse::<HttpDate>().ok()
/// }
/// );
/// ```
/// ```
/// # extern crate hyper;
/// # extern crate time;
/// # fn main() {
/// use hyper::header::{Headers, HttpDate, Warning};
///
/// let mut headers = Headers::new();
/// headers.set(
/// Warning{
/// code: 199,
/// agent: "api.hyper.rs".to_owned(),
/// text: "Deprecated".to_owned(),
/// date: Some(HttpDate(time::now()))
/// }
/// );
/// # }
/// ```
#[derive(PartialEq, Clone, Debug)]
pub struct Warning {
/// The 3 digit warn code.
pub code: u16,
/// The name or pseudonym of the server adding this header.
pub agent: String,
/// The warning message describing the error.
pub text: String,
/// An optional warning date.
pub date: Option<HttpDate>
}

impl Header for Warning {
fn header_name() -> &'static str {
static NAME: &'static str = "Warning";
NAME
}

fn parse_header(raw: &Raw) -> ::Result<Warning> {
from_one_raw_str(raw)
}

fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.date {
Some(date) => write!(f, "{:03} {} \"{}\" \"{}\"", self.code, self.agent, self.text, date),
None => write!(f, "{:03} {} \"{}\"", self.code, self.agent, self.text)
}
}
}

impl FromStr for Warning {
type Err = ::Error;

fn from_str(s: &str) -> ::Result<Warning> {
let mut warning_split = s.split_whitespace();
let code = match warning_split.next() {
Some(c) => match c.parse::<u16>() {
Ok(c) => c,
Err(..) => return Err(::Error::Header)
},
None => return Err(::Error::Header)
};
let agent = match warning_split.next() {
Some(a) => a.to_string(),
None => return Err(::Error::Header)
};

let mut warning_split = s.split('"').skip(1);
let text = match warning_split.next() {
Some(t) => t.to_string(),
None => return Err(::Error::Header)
};
let date = match warning_split.skip(1).next() {
Some(d) => d.parse::<HttpDate>().ok(),
None => None // Optional
};

Ok(Warning {
code: code,
agent: agent,
text: text,
date: date
})
}
}

#[cfg(test)]
mod tests {
use super::Warning;
use header::{Header, HttpDate};

#[test]
fn test_parsing() {
let warning = Header::parse_header(&vec![b"112 - \"network down\" \"Sat, 25 Aug 2012 23:34:45 GMT\"".to_vec()].into());
assert_eq!(warning.ok(), Some(Warning {
code: 112,
agent: "-".to_owned(),
text: "network down".to_owned(),
date: "Sat, 25 Aug 2012 23:34:45 GMT".parse::<HttpDate>().ok()
}));

let warning = Header::parse_header(&vec![b"299 api.hyper.rs:8080 \"Deprecated API : use newapi.hyper.rs instead.\"".to_vec()].into());
assert_eq!(warning.ok(), Some(Warning {
code: 299,
agent: "api.hyper.rs:8080".to_owned(),
text: "Deprecated API : use newapi.hyper.rs instead.".to_owned(),
date: None
}));

let warning = Header::parse_header(&vec![b"299 api.hyper.rs:8080 \"Deprecated API : use newapi.hyper.rs instead.\" \"Tue, 15 Nov 1994 08:12:31 GMT\"".to_vec()].into());
assert_eq!(warning.ok(), Some(Warning {
code: 299,
agent: "api.hyper.rs:8080".to_owned(),
text: "Deprecated API : use newapi.hyper.rs instead.".to_owned(),
date: "Tue, 15 Nov 1994 08:12:31 GMT".parse::<HttpDate>().ok()
}));
}
}