Skip to content

refactor(headers): Improve docs, fix nits, make formatting faster #503

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

Merged
merged 2 commits into from
May 4, 2015
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
8 changes: 6 additions & 2 deletions src/header/common/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ header! {
Some(HeaderField(vec![
QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), Quality(500)),
qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])),
QualityItem::new(Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_string()), vec![]), Quality(800)),
QualityItem::new(
Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_string()), vec![]),
Quality(800)),
qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_string()), vec![])),
])));
// Custom tests
Expand All @@ -62,7 +64,9 @@ header! {
test4,
vec![b"text/plain; charset=utf-8; q=0.5"],
Some(Accept(vec![
QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]), Quality(500)),
QualityItem::new(Mime(TopLevel::Text,
SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]),
Quality(500)),
])));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/header/common/access_control_allow_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use unicase::UniCase;

header! {
#[doc="`Access-Control-Allow-Headers` header, part of"]
#[doc="[CORS](www.w3.org/TR/cors/#access-control-allow-headers-response-header)"]
#[doc="[CORS](http://www.w3.org/TR/cors/#access-control-allow-headers-response-header)"]
#[doc=""]
#[doc="The `Access-Control-Allow-Headers` header indicates, as part of the"]
#[doc="response to a preflight request, which header field names can be used"]
Expand Down
2 changes: 1 addition & 1 deletion src/header/common/access_control_allow_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use method::Method;

header! {
#[doc="`Access-Control-Allow-Methods` header, part of"]
#[doc="[CORS](www.w3.org/TR/cors/#access-control-allow-methods-response-header)"]
#[doc="[CORS](http://www.w3.org/TR/cors/#access-control-allow-methods-response-header)"]
#[doc=""]
#[doc="The `Access-Control-Allow-Methods` header indicates, as part of the"]
#[doc="response to a preflight request, which methods can be used during the"]
Expand Down
2 changes: 1 addition & 1 deletion src/header/common/access_control_max_age.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
header! {
#[doc="`Access-Control-Max-Age` header, part of"]
#[doc="[CORS](www.w3.org/TR/cors/#access-control-max-age-response-header)"]
#[doc="[CORS](http://www.w3.org/TR/cors/#access-control-max-age-response-header)"]
#[doc=""]
#[doc="The `Access-Control-Max-Age` header indicates how long the results of a"]
#[doc="preflight request can be cached in a preflight result cache."]
Expand Down
2 changes: 1 addition & 1 deletion src/header/common/access_control_request_headers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use unicase::UniCase;

header! {
#[doc="`Access-Control-Request-Headers` header, part of"]
#[doc="[CORS](www.w3.org/TR/cors/#access-control-request-headers-request-header)"]
#[doc="[CORS](http://www.w3.org/TR/cors/#access-control-request-headers-request-header)"]
#[doc=""]
#[doc="The `Access-Control-Request-Headers` header indicates which headers will"]
#[doc="be used in the actual request as part of the preflight request."]
Expand Down
2 changes: 1 addition & 1 deletion src/header/common/access_control_request_method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use method::Method;

header! {
#[doc="`Access-Control-Request-Method` header, part of"]
#[doc="[CORS](www.w3.org/TR/cors/#access-control-request-method-request-header)"]
#[doc="[CORS](http://www.w3.org/TR/cors/#access-control-request-method-request-header)"]
#[doc=""]
#[doc="The `Access-Control-Request-Method` header indicates which method will be"]
#[doc="used in the actual request as part of the preflight request."]
Expand Down
3 changes: 2 additions & 1 deletion src/header/common/allow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,5 @@ header! {
}
}

bench_header!(bench, Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] });
bench_header!(bench,
Allow, { vec![b"OPTIONS,GET,PUT,POST,DELETE,HEAD,TRACE,CONNECT,PATCH,fOObAr".to_vec()] });
42 changes: 31 additions & 11 deletions src/header/common/authorization.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,25 @@
use std::any::Any;
use std::fmt;
use std::fmt::{self, Display};
use std::str::{FromStr, from_utf8};
use std::ops::{Deref, DerefMut};
use serialize::base64::{ToBase64, FromBase64, Standard, Config, Newline};
use header::{Header, HeaderFormat};

/// The `Authorization` header field.
/// `Authorization` header, defined in [RFC7235](https://tools.ietf.org/html/rfc7235#section-4.2)
///
/// The `Authorization` header field allows a user agent to authenticate
/// itself with an origin server -- usually, but not necessarily, after
/// receiving a 401 (Unauthorized) response. Its value consists of
/// credentials containing the authentication information of the user
/// agent for the realm of the resource being requested.
///
/// # ABNF
/// ```plain
/// Authorization = credentials
/// ```
///
/// # Example values
/// * `Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==`
#[derive(Clone, PartialEq, Debug)]
pub struct Authorization<S: Scheme>(pub S);

Expand Down Expand Up @@ -69,7 +83,7 @@ impl Scheme for String {
}

fn fmt_scheme(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
Display::fmt(self, f)
}
}

Expand Down Expand Up @@ -97,12 +111,12 @@ impl Scheme for Basic {
if let Some(ref pass) = self.password {
text.push_str(&pass[..]);
}
write!(f, "{}", text.as_bytes().to_base64(Config {
f.write_str(&text.as_bytes().to_base64(Config {
char_set: Standard,
newline: Newline::CRLF,
pad: true,
line_length: None
}))
})[..])
}
}

Expand Down Expand Up @@ -153,15 +167,19 @@ mod tests {

#[test]
fn test_raw_auth_parse() {
let header: Authorization<String> = Header::parse_header(&[b"foo bar baz".to_vec()]).unwrap();
let header: Authorization<String> = Header::parse_header(
&[b"foo bar baz".to_vec()]).unwrap();
assert_eq!(header.0, "foo bar baz");
}

#[test]
fn test_basic_auth() {
let mut headers = Headers::new();
headers.set(Authorization(Basic { username: "Aladdin".to_string(), password: Some("open sesame".to_string()) }));
assert_eq!(headers.to_string(), "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_string());
headers.set(Authorization(
Basic { username: "Aladdin".to_string(), password: Some("open sesame".to_string()) }));
assert_eq!(
headers.to_string(),
"Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\r\n".to_string());
}

#[test]
Expand All @@ -173,19 +191,21 @@ mod tests {

#[test]
fn test_basic_auth_parse() {
let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap();
let auth: Authorization<Basic> = Header::parse_header(
&[b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()]).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("open sesame".to_string()));
}

#[test]
fn test_basic_auth_parse_no_password() {
let auth: Authorization<Basic> = Header::parse_header(&[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap();
let auth: Authorization<Basic> = Header::parse_header(
&[b"Basic QWxhZGRpbjo=".to_vec()]).unwrap();
assert_eq!(auth.0.username, "Aladdin");
assert_eq!(auth.0.password, Some("".to_string()));
}

}

bench_header!(raw, Authorization<String>, { vec![b"foo bar baz".to_vec()] });
bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==".to_vec()] });
bench_header!(basic, Authorization<Basic>, { vec![b"Basic QWxhZGRpbjpuIHNlc2FtZQ==".to_vec()] });
30 changes: 24 additions & 6 deletions src/header/common/cache_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@ use std::str::FromStr;
use header::{Header, HeaderFormat};
use header::parsing::{from_one_comma_delimited, fmt_comma_delimited};

/// The Cache-Control header.
/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
///
/// The `Cache-Control` header field is used to specify directives for
/// caches along the request/response chain. Such cache directives are
/// unidirectional in that the presence of a directive in a request does
/// not imply that the same directive is to be given in the response.
///
/// # ABNF
/// ```plain
/// Cache-Control = 1#cache-directive
/// cache-directive = token [ "=" ( token / quoted-string ) ]
/// ```
///
/// # Example values
/// * `no-cache`
/// * `private, community="UCI"`
/// * `max-age=30`
#[derive(PartialEq, Clone, Debug)]
pub struct CacheControl(pub Vec<CacheDirective>);

Expand All @@ -28,8 +44,8 @@ impl Header for CacheControl {
}

impl HeaderFormat for CacheControl {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt_comma_delimited(fmt, &self[..])
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt_comma_delimited(f, &self[..])
}
}

Expand Down Expand Up @@ -152,8 +168,9 @@ mod tests {
#[test]
fn test_parse_extension() {
let cache = Header::parse_header(&[b"foo, bar=baz".to_vec()]);
assert_eq!(cache, Some(CacheControl(vec![CacheDirective::Extension("foo".to_string(), None),
CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))])))
assert_eq!(cache, Some(CacheControl(vec![
CacheDirective::Extension("foo".to_string(), None),
CacheDirective::Extension("bar".to_string(), Some("baz".to_string()))])))
}

#[test]
Expand All @@ -163,4 +180,5 @@ mod tests {
}
}

bench_header!(normal, CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] });
bench_header!(normal,
CacheControl, { vec![b"no-cache, private".to_vec(), b"max-age=100".to_vec()] });
44 changes: 30 additions & 14 deletions src/header/common/connection.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
use header::{Header, HeaderFormat};
use std::fmt;
use std::fmt::{self, Display};
use std::str::FromStr;
use header::parsing::{from_comma_delimited, fmt_comma_delimited};
use unicase::UniCase;

pub use self::ConnectionOption::{KeepAlive, Close, ConnectionHeader};

/// The `Connection` header.
/// `Connection` header, defined in [RFC7230](https://tools.ietf.org/html/rfc7230#section-6.1)
///
/// The `Connection` header field allows the sender to indicate desired
/// control options for the current connection. In order to avoid
/// confusing downstream recipients, a proxy or gateway MUST remove or
/// replace any received connection options before forwarding the
/// message.
///
/// # ABNF
/// ```plain
/// Connection = 1#connection-option
/// connection-option = token
/// ```
///
/// # Example values
/// * `close`
/// * `upgrade`
/// * `keep-alive`
#[derive(Clone, PartialEq, Debug)]
pub struct Connection(pub Vec<ConnectionOption>);

Expand All @@ -33,20 +50,20 @@ pub enum ConnectionOption {
impl FromStr for ConnectionOption {
type Err = ();
fn from_str(s: &str) -> Result<ConnectionOption, ()> {
match s {
"keep-alive" => Ok(KeepAlive),
"close" => Ok(Close),
s => Ok(ConnectionHeader(UniCase(s.to_string())))
}
Ok(match s {
"keep-alive" => KeepAlive,
"close" => Close,
s => ConnectionHeader(UniCase(s.to_string())),
})
}
}

impl fmt::Display for ConnectionOption {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{}", match *self {
impl Display for ConnectionOption {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match *self {
KeepAlive => "keep-alive",
Close => "close",
ConnectionHeader(UniCase(ref s)) => s.as_ref()
ConnectionHeader(UniCase(ref s)) => s,
})
}
}
Expand All @@ -62,13 +79,12 @@ impl Header for Connection {
}

impl HeaderFormat for Connection {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Connection(ref parts) = *self;
fmt_comma_delimited(fmt, &parts[..])
fmt_comma_delimited(f, &parts[..])
}
}

bench_header!(close, Connection, { vec![b"close".to_vec()] });
bench_header!(keep_alive, Connection, { vec![b"keep-alive".to_vec()] });
bench_header!(header, Connection, { vec![b"authorization".to_vec()] });

5 changes: 4 additions & 1 deletion src/header/common/content_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ header! {
// FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases
// the whole value so parsing and formatting the value gives a different result
vec![b"text/html; charset=iso-8859-4"],
Some(HeaderField(Mime(TopLevel::Text, SubLevel::Html, vec![(Attr::Charset, Value::Ext("iso-8859-4".to_string()))]))));
Some(HeaderField(Mime(
TopLevel::Text,
SubLevel::Html,
vec![(Attr::Charset, Value::Ext("iso-8859-4".to_string()))]))));
}
}

Expand Down
28 changes: 17 additions & 11 deletions src/header/common/cookie.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
use header::{Header, HeaderFormat};
use std::fmt;
use std::fmt::{self, Display};
use std::str::from_utf8;

use cookie::Cookie as CookiePair;
use cookie::CookieJar;

/// The `Cookie` header. Defined in [RFC6265](tools.ietf.org/html/rfc6265#section-5.4):
/// `Cookie` header, defined in [RFC6265](http://tools.ietf.org/html/rfc6265#section-5.4)
///
/// > If the user agent does attach a Cookie header field to an HTTP
/// > request, the user agent must send the cookie-string
/// > as the value of the header field.
/// If the user agent does attach a Cookie header field to an HTTP
/// request, the user agent must send the cookie-string
/// as the value of the header field.
///
/// > When the user agent generates an HTTP request, the user agent MUST NOT
/// > attach more than one Cookie header field.
/// When the user agent generates an HTTP request, the user agent MUST NOT
/// attach more than one Cookie header field.
///
/// # Example values
/// * `SID=31d4d96e407aad42`
/// * `SID=31d4d96e407aad42; lang=en-US`
#[derive(Clone, PartialEq, Debug)]
pub struct Cookie(pub Vec<CookiePair>);

Expand Down Expand Up @@ -48,13 +52,13 @@ impl Header for Cookie {
}

impl HeaderFormat for Cookie {
fn fmt_header(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fn fmt_header(&self, f: &mut fmt::Formatter) -> fmt::Result {
let cookies = &self.0;
for (i, cookie) in cookies.iter().enumerate() {
if i != 0 {
try!(fmt.write_str("; "));
try!(f.write_str("; "));
}
try!(write!(fmt, "{}", cookie.pair()));
try!(Display::fmt(&cookie.pair(), f));
}
Ok(())
}
Expand Down Expand Up @@ -94,7 +98,9 @@ fn test_fmt() {
let mut cookie_pair = CookiePair::new("foo".to_string(), "bar".to_string());
cookie_pair.httponly = true;
cookie_pair.path = Some("/p".to_string());
let cookie_header = Cookie(vec![cookie_pair, CookiePair::new("baz".to_string(), "quux".to_string())]);
let cookie_header = Cookie(vec![
cookie_pair,
CookiePair::new("baz".to_string(),"quux".to_string())]);
let mut headers = Headers::new();
headers.set(cookie_header);

Expand Down
Loading