From 262c450f908dbf27754daff0784f0f20145036dd Mon Sep 17 00:00:00 2001 From: Pyfisch Date: Thu, 2 Apr 2015 12:41:04 +0200 Subject: [PATCH] refactor(headers): Introduce header!() macro, improve documentation The new macro handles single value headers, list headers, and list headers with at least one item. It creates the item for the header and contains its documentation. The new macro allows handling more header cases in the future, it will also be possible to include tests inside the macro. BREAKING CHANGE: Removed impl_header!() and impl_list_header!() macros, use new header!() macro. --- src/header/common/accept.rs | 54 +++++++------- src/header/common/accept_charset.rs | 27 ++++--- src/header/common/accept_encoding.rs | 27 ++++--- src/header/common/accept_language.rs | 25 ++++--- src/header/common/allow.rs | 23 +++--- src/header/common/content_encoding.rs | 32 ++++---- src/header/common/content_length.rs | 28 ++++--- src/header/common/content_type.rs | 28 ++++--- src/header/common/date.rs | 17 +++-- src/header/common/expires.rs | 20 ++++- src/header/common/if_modified_since.rs | 20 ++++- src/header/common/if_unmodified_since.rs | 21 ++++-- src/header/common/last_modified.rs | 19 +++-- src/header/common/location.rs | 32 ++++---- src/header/common/mod.rs | 95 +++++++++++++++--------- src/header/common/referer.rs | 31 ++++---- src/header/common/server.rs | 27 ++++--- src/header/common/transfer_encoding.rs | 34 ++++----- src/header/common/user_agent.rs | 32 +++++--- 19 files changed, 367 insertions(+), 225 deletions(-) diff --git a/src/header/common/accept.rs b/src/header/common/accept.rs index 14ed97b4..02522dea 100644 --- a/src/header/common/accept.rs +++ b/src/header/common/accept.rs @@ -2,39 +2,39 @@ use mime::Mime; use header::QualityItem; -/// The `Accept` header. -/// -/// The `Accept` header is used to tell a server which content-types the client -/// is capable of using. It can be a comma-separated list of `Mime`s, and the -/// priority can be indicated with a `q` parameter. -/// -/// Example: -/// -/// ``` -/// # use hyper::header::Headers; -/// # use hyper::header::Accept; -/// # use hyper::header::qitem; -/// use hyper::mime::Mime; -/// use hyper::mime::TopLevel::Text; -/// use hyper::mime::SubLevel::{Html, Xml}; -/// # let mut headers = Headers::new(); -/// headers.set(Accept(vec![ -/// qitem(Mime(Text, Html, vec![])), -/// qitem(Mime(Text, Xml, vec![])) ])); -/// ``` -#[derive(Clone, PartialEq, Debug)] -pub struct Accept(pub Vec>); - -impl_list_header!(Accept, - "Accept", - Vec>); +header! { + #[doc="`Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2)"] + #[doc=""] + #[doc="The `Accept` header field can be used by user agents to specify"] + #[doc="response media types that are acceptable. Accept header fields can"] + #[doc="be used to indicate that the request is specifically limited to a"] + #[doc="small set of desired types, as in the case of a request for an"] + #[doc="in-line image"] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Accept = #( media-range [ accept-params ] )"] + #[doc=""] + #[doc="media-range = ( \"*/*\""] + #[doc=" / ( type \"/\" \"*\" )"] + #[doc=" / ( type \"/\" subtype )"] + #[doc=" ) *( OWS \";\" OWS parameter )"] + #[doc="accept-params = weight *( accept-ext )"] + #[doc="accept-ext = OWS \";\" OWS token [ \"=\" ( token / quoted-string ) ]"] + #[doc="```"] + #[doc=""] + #[doc="# Notes"] + #[doc="* Using always Mime types to represent `media-range` differs from the ABNF."] + #[doc="* **FIXME**: `accept-ext` is not supported."] + (Accept, "Accept") => (QualityItem)+ +} #[cfg(test)] mod tests { use mime::*; use header::{Header, Quality, QualityItem, qitem}; - + use super::Accept; #[test] diff --git a/src/header/common/accept_charset.rs b/src/header/common/accept_charset.rs index 59bb3724..7b36bd9d 100644 --- a/src/header/common/accept_charset.rs +++ b/src/header/common/accept_charset.rs @@ -1,15 +1,22 @@ use header::{Charset, QualityItem}; -/// The `Accept-Charset` header -/// -/// The `Accept-Charset` header can be used by clients to indicate what -/// response charsets they accept. -#[derive(Clone, PartialEq, Debug)] -pub struct AcceptCharset(pub Vec>); - -impl_list_header!(AcceptCharset, - "Accept-Charset", - Vec>); +header! { + #[doc="`Accept-Charset` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)"] + #[doc=""] + #[doc="The `Accept-Charset` header field can be sent by a user agent to"] + #[doc="indicate what charsets are acceptable in textual response content."] + #[doc="This field allows user agents capable of understanding more"] + #[doc="comprehensive or special-purpose charsets to signal that capability"] + #[doc="to an origin server that is capable of representing information in"] + #[doc="those charsets."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Accept-Charset = 1#( ( charset / \"*\" ) [ weight ] )"] + #[doc="```"] + (AcceptCharset, "Accept-Charset") => (QualityItem)+ +} #[test] diff --git a/src/header/common/accept_encoding.rs b/src/header/common/accept_encoding.rs index 1799156d..03a254f5 100644 --- a/src/header/common/accept_encoding.rs +++ b/src/header/common/accept_encoding.rs @@ -1,15 +1,22 @@ use header::{Encoding, QualityItem}; -/// The `Accept-Encoding` header -/// -/// The `Accept-Encoding` header can be used by clients to indicate what -/// response encodings they accept. -#[derive(Clone, PartialEq, Debug)] -pub struct AcceptEncoding(pub Vec>); - -impl_list_header!(AcceptEncoding, - "Accept-Encoding", - Vec>); +header! { + #[doc="`Accept-Encoding` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.4)"] + #[doc=""] + #[doc="The `Accept-Encoding` header field can be used by user agents to"] + #[doc="indicate what response content-codings are"] + #[doc="acceptable in the response. An `identity` token is used as a synonym"] + #[doc="for \"no encoding\" in order to communicate when no encoding is"] + #[doc="preferred."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Accept-Encoding = #( codings [ weight ] )"] + #[doc="codings = content-coding / \"identity\" / \"*\""] + #[doc="```"] + (AcceptEncoding, "Accept-Encoding") => (QualityItem)* +} #[cfg(test)] mod tests { diff --git a/src/header/common/accept_language.rs b/src/header/common/accept_language.rs index 838ebbee..3cc95c52 100644 --- a/src/header/common/accept_language.rs +++ b/src/header/common/accept_language.rs @@ -35,16 +35,21 @@ impl fmt::Display for Language { } } -/// The `Accept-Language` header -/// -/// The `Accept-Language` header can be used by clients to indicate what -/// response languages they accept. -#[derive(Clone, PartialEq, Debug)] -pub struct AcceptLanguage(pub Vec>); - -impl_list_header!(AcceptLanguage, - "Accept-Language", - Vec>); +header! { + #[doc="`Accept-Language` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)"] + #[doc=""] + #[doc="The `Accept-Language` header field can be used by user agents to"] + #[doc="indicate the set of natural languages that are preferred in the"] + #[doc="response."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Accept-Language = 1#( language-range [ weight ] )"] + #[doc="language-range = "] + #[doc="```"] + (AcceptLanguage, "Accept-Language") => (QualityItem)+ +} #[cfg(test)] mod tests { diff --git a/src/header/common/allow.rs b/src/header/common/allow.rs index b3026a7b..41464388 100644 --- a/src/header/common/allow.rs +++ b/src/header/common/allow.rs @@ -1,14 +1,19 @@ use method::Method; -/// The `Allow` header. -/// See also https://tools.ietf.org/html/rfc7231#section-7.4.1 - -#[derive(Clone, PartialEq, Debug)] -pub struct Allow(pub Vec); - -impl_list_header!(Allow, - "Allow", - Vec); +header! { + #[doc="`Allow` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.1)"] + #[doc=""] + #[doc="The `Allow` header field lists the set of methods advertised as"] + #[doc="supported by the target resource. The purpose of this field is"] + #[doc="strictly to inform the recipient of valid request methods associated"] + #[doc="with the resource."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Allow = #method"] + #[doc="```"] + (Allow, "Allow") => (Method)* +} #[cfg(test)] mod tests { diff --git a/src/header/common/content_encoding.rs b/src/header/common/content_encoding.rs index 6e56c388..0d836bb6 100644 --- a/src/header/common/content_encoding.rs +++ b/src/header/common/content_encoding.rs @@ -1,19 +1,23 @@ use header::Encoding; -/// The `Content-Encoding` header. -/// -/// This header describes the encoding of the message body. It can be -/// comma-separated, including multiple encodings. -/// -/// ```notrust -/// Content-Encoding: gzip -/// ``` -#[derive(Clone, PartialEq, Debug)] -pub struct ContentEncoding(pub Vec); - -impl_list_header!(ContentEncoding, - "Content-Encoding", - Vec); +header! { + #[doc="`Content-Encoding` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.2.2)"] + #[doc=""] + #[doc="The `Content-Encoding` header field indicates what content codings"] + #[doc="have been applied to the representation, beyond those inherent in the"] + #[doc="media type, and thus what decoding mechanisms have to be applied in"] + #[doc="order to obtain data in the media type referenced by the Content-Type"] + #[doc="header field. Content-Encoding is primarily used to allow a"] + #[doc="representation's data to be compressed without losing the identity of"] + #[doc="its underlying media type."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Content-Encoding = 1#content-coding"] + #[doc="```"] + (ContentEncoding, "ContentEncoding") => (Encoding)+ +} bench_header!(single, ContentEncoding, { vec![b"gzip".to_vec()] }); bench_header!(multiple, ContentEncoding, { vec![b"gzip, deflate".to_vec()] }); diff --git a/src/header/common/content_length.rs b/src/header/common/content_length.rs index f65744ab..ac630880 100644 --- a/src/header/common/content_length.rs +++ b/src/header/common/content_length.rs @@ -1,11 +1,21 @@ -/// The `Content-Length` header. -/// -/// Simply a wrapper around a `u64`. -#[derive(Copy, Clone, PartialEq, Debug)] -pub struct ContentLength(pub u64); - -impl_header!(ContentLength, - "Content-Length", - u64); +header! { + #[doc="`Content-Length` header, defined in"] + #[doc="[RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.2)"] + #[doc=""] + #[doc="When a message does not have a `Transfer-Encoding` header field, a"] + #[doc="Content-Length header field can provide the anticipated size, as a"] + #[doc="decimal number of octets, for a potential payload body. For messages"] + #[doc="that do include a payload body, the Content-Length field-value"] + #[doc="provides the framing information necessary for determining where the"] + #[doc="body (and message) ends. For messages that do not include a payload"] + #[doc="body, the Content-Length indicates the size of the selected"] + #[doc="representation."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Content-Length = 1*DIGIT"] + #[doc="```"] + (ContentLength, "Content-Length") => [u64] +} bench_header!(bench, ContentLength, { vec![b"42349984".to_vec()] }); diff --git a/src/header/common/content_type.rs b/src/header/common/content_type.rs index 264ed257..427de5ee 100644 --- a/src/header/common/content_type.rs +++ b/src/header/common/content_type.rs @@ -1,14 +1,22 @@ use mime::Mime; -/// The `Content-Type` header. -/// -/// Used to describe the MIME type of message body. Can be used with both -/// requests and responses. -#[derive(Clone, PartialEq, Debug)] -pub struct ContentType(pub Mime); - -impl_header!(ContentType, - "Content-Type", - Mime); +header! { + #[doc="`Content-Type` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-3.1.1.5)"] + #[doc=""] + #[doc="The `Content-Type` header field indicates the media type of the"] + #[doc="associated representation: either the representation enclosed in the"] + #[doc="message payload or the selected representation, as determined by the"] + #[doc="message semantics. The indicated media type defines both the data"] + #[doc="format and how that data is intended to be processed by a recipient,"] + #[doc="within the scope of the received message semantics, after any content"] + #[doc="codings indicated by Content-Encoding are decoded."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Content-Type = media-type"] + #[doc="```"] + (ContentType, "Content-Type") => [Mime] +} bench_header!(bench, ContentType, { vec![b"application/json; charset=utf-8".to_vec()] }); diff --git a/src/header/common/date.rs b/src/header/common/date.rs index 47ca75da..70fcb5fe 100644 --- a/src/header/common/date.rs +++ b/src/header/common/date.rs @@ -1,10 +1,17 @@ use header::HttpDate; -/// The `Date` header field. -#[derive(Copy, PartialEq, Clone, Debug)] -pub struct Date(pub HttpDate); - -impl_header!(Date, "Date", HttpDate); +header! { + #[doc="`Date` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.1.2)"] + #[doc=""] + #[doc="The `Date` header field represents the date and time at which the"] + #[doc="message was originated."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Date = HTTP-date"] + #[doc="```"] + (Date, "Date") => [HttpDate] +} bench_header!(imf_fixdate, Date, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, Date, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/expires.rs b/src/header/common/expires.rs index 57819180..613ef6a0 100644 --- a/src/header/common/expires.rs +++ b/src/header/common/expires.rs @@ -1,9 +1,21 @@ use header::HttpDate; -/// The `Expires` header field. -#[derive(Copy, PartialEq, Clone, Debug)] -pub struct Expires(pub HttpDate); -impl_header!(Expires, "Expires", HttpDate); +header! { + #[doc="`Expires` header, defined in [RFC7234](http://tools.ietf.org/html/rfc7234#section-5.3)"] + #[doc=""] + #[doc="The `Expires` header field gives the date/time after which the"] + #[doc="response is considered stale."] + #[doc=""] + #[doc="The presence of an Expires field does not imply that the original"] + #[doc="resource will change or cease to exist at, before, or after that"] + #[doc="time."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Expires = HTTP-date"] + #[doc="```"] + (Expires, "Expires") => [HttpDate] +} bench_header!(imf_fixdate, Expires, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, Expires, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/if_modified_since.rs b/src/header/common/if_modified_since.rs index 53dae88a..b8453203 100644 --- a/src/header/common/if_modified_since.rs +++ b/src/header/common/if_modified_since.rs @@ -1,9 +1,21 @@ use header::HttpDate; -/// The `If-Modified-Since` header field. -#[derive(Copy, PartialEq, Clone, Debug)] -pub struct IfModifiedSince(pub HttpDate); -impl_header!(IfModifiedSince, "If-Modified-Since", HttpDate); +header! { + #[doc="`If-Modified-Since` header, defined in"] + #[doc="[RFC7232](http://tools.ietf.org/html/rfc7232#section-3.3)"] + #[doc=""] + #[doc="The `If-Modified-Since` header field makes a GET or HEAD request"] + #[doc="method conditional on the selected representation's modification date"] + #[doc="being more recent than the date provided in the field-value."] + #[doc="Transfer of the selected representation's data is avoided if that"] + #[doc="data has not changed."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="If-Unmodified-Since = HTTP-date"] + #[doc="```"] + (IfModifiedSince, "If-Modified-Since") => [HttpDate] +} bench_header!(imf_fixdate, IfModifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, IfModifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/if_unmodified_since.rs b/src/header/common/if_unmodified_since.rs index 88d0f481..200d030d 100644 --- a/src/header/common/if_unmodified_since.rs +++ b/src/header/common/if_unmodified_since.rs @@ -1,10 +1,21 @@ use header::HttpDate; -/// The `If-Unmodified-Since` header field. -#[derive(Copy, PartialEq, Clone, Debug)] -pub struct IfUnmodifiedSince(pub HttpDate); - -impl_header!(IfUnmodifiedSince, "If-Unmodified-Since", HttpDate); +header! { + #[doc="`If-Unmodified-Since` header, defined in"] + #[doc="[RFC7232](http://tools.ietf.org/html/rfc7232#section-3.4)"] + #[doc=""] + #[doc="The `If-Unmodified-Since` header field makes the request method"] + #[doc="conditional on the selected representation's last modification date"] + #[doc="being earlier than or equal to the date provided in the field-value."] + #[doc="This field accomplishes the same purpose as If-Match for cases where"] + #[doc="the user agent does not have an entity-tag for the representation."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="If-Unmodified-Since = HTTP-date"] + #[doc="```"] + (IfUnmodifiedSince, "If-Unmodified-Since") => [HttpDate] +} bench_header!(imf_fixdate, IfUnmodifiedSince, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, IfUnmodifiedSince, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/last_modified.rs b/src/header/common/last_modified.rs index 5b90dce6..cf3f0cf5 100644 --- a/src/header/common/last_modified.rs +++ b/src/header/common/last_modified.rs @@ -1,10 +1,19 @@ use header::HttpDate; -/// The `LastModified` header field. -#[derive(Copy, PartialEq, Clone, Debug)] -pub struct LastModified(pub HttpDate); - -impl_header!(LastModified, "Last-Modified", HttpDate); +header! { + #[doc="`Last-Modified` header, defined in [RFC7232](http://tools.ietf.org/html/rfc7232#section-2.2)"] + #[doc=""] + #[doc="The `Last-Modified` header field in a response provides a timestamp"] + #[doc="indicating the date and time at which the origin server believes the"] + #[doc="selected representation was last modified, as determined at the"] + #[doc="conclusion of handling the request."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Expires = HTTP-date"] + #[doc="```"] + (LastModified, "Last-Modified") => [HttpDate] +} bench_header!(imf_fixdate, LastModified, { vec![b"Sun, 07 Nov 1994 08:48:37 GMT".to_vec()] }); bench_header!(rfc_850, LastModified, { vec![b"Sunday, 06-Nov-94 08:49:37 GMT".to_vec()] }); diff --git a/src/header/common/location.rs b/src/header/common/location.rs index a1232482..f5d23023 100644 --- a/src/header/common/location.rs +++ b/src/header/common/location.rs @@ -1,19 +1,19 @@ -/// The `Location` header. -/// -/// The Location response-header field is used to redirect the recipient to -/// a location other than the Request-URI for completion of the request or identification -/// of a new resource. For 201 (Created) responses, the Location is that of the new -/// resource which was created by the request. For 3xx responses, the location SHOULD -/// indicate the server's preferred URI for automatic redirection to the resource. -/// The field value consists of a single absolute URI. -/// -/// Currently is just a String, but it should probably become a better type, -/// like url::Url or something. -#[derive(Clone, PartialEq, Debug)] -pub struct Location(pub String); +header! { + #[doc="`Location` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-7.1.2)"] + #[doc=""] + #[doc="The `Location` header field is used in some responses to refer to a"] + #[doc="specific resource in relation to the response. The type of"] + #[doc="relationship is defined by the combination of request method and"] + #[doc="status code semantics."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Location = URI-reference"] + #[doc="```"] + // TODO: Use URL + (Location, "Location") => [String] -impl_header!(Location, - "Location", - String); +} bench_header!(bench, Location, { vec![b"http://foo.com/hello:3000".to_vec()] }); diff --git a/src/header/common/mod.rs b/src/header/common/mod.rs index 7a374b8d..dd57a0a5 100644 --- a/src/header/common/mod.rs +++ b/src/header/common/mod.rs @@ -89,64 +89,91 @@ macro_rules! deref( ); #[macro_export] -macro_rules! impl_list_header( - ($from:ident, $name:expr, $item:ty) => { - deref!($from => $item); +macro_rules! header { + // $a:meta: Attributes associated with the header item (usually docs) + // $id:ident: Identifier of the header + // $n:expr: Lowercase name of the header + // $nn:expr: Nice name of the header - impl $crate::header::Header for $from { + // List header, zero or more items + ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)*) => { + $(#[$a])* + #[derive(Clone, Debug, PartialEq)] + pub struct $id(pub Vec<$item>); + deref!($id => Vec<$item>); + impl $crate::header::Header for $id { fn header_name() -> &'static str { - $name + $n } - - fn parse_header(raw: &[Vec]) -> Option<$from> { - $crate::header::parsing::from_comma_delimited(raw).map($from) + fn parse_header(raw: &[Vec]) -> Option { + $crate::header::parsing::from_comma_delimited(raw).map($id) } } - - impl $crate::header::HeaderFormat for $from { - fn fmt_header(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - $crate::header::parsing::fmt_comma_delimited(fmt, &self[..]) + impl $crate::header::HeaderFormat for $id { + fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + $crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) } } - - impl ::std::fmt::Display for $from { + impl ::std::fmt::Display for $id { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { use $crate::header::HeaderFormat; self.fmt_header(f) } } - } -); -#[macro_export] -macro_rules! impl_header( - ($from:ident, $name:expr, $item:ty) => { - deref!($from => $item); - - impl $crate::header::Header for $from { + }; + // List header, one or more items + ($(#[$a:meta])*($id:ident, $n:expr) => ($item:ty)+) => { + $(#[$a])* + #[derive(Clone, Debug, PartialEq)] + pub struct $id(pub Vec<$item>); + deref!($id => Vec<$item>); + impl $crate::header::Header for $id { fn header_name() -> &'static str { - $name + $n } - - fn parse_header(raw: &[Vec]) -> Option<$from> { - $crate::header::parsing::from_one_raw_str(raw).map($from) + fn parse_header(raw: &[Vec]) -> Option { + $crate::header::parsing::from_comma_delimited(raw).map($id) } } - - impl $crate::header::HeaderFormat for $from { + impl $crate::header::HeaderFormat for $id { + fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + $crate::header::parsing::fmt_comma_delimited(f, &self.0[..]) + } + } + impl ::std::fmt::Display for $id { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + use $crate::header::HeaderFormat; + self.fmt_header(f) + } + } + }; + // Single value header + ($(#[$a:meta])*($id:ident, $n:expr) => [$value:ty]) => { + $(#[$a])* + #[derive(Clone, Debug, PartialEq)] + pub struct $id(pub $value); + deref!($id => $value); + impl $crate::header::Header for $id { + fn header_name() -> &'static str { + $n + } + fn parse_header(raw: &[Vec]) -> Option { + $crate::header::parsing::from_one_raw_str(raw).map($id) + } + } + impl $crate::header::HeaderFormat for $id { fn fmt_header(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { ::std::fmt::Display::fmt(&**self, f) } } - - impl ::std::fmt::Display for $from { + impl ::std::fmt::Display for $id { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - use $crate::header::HeaderFormat; - self.fmt_header(f) + ::std::fmt::Display::fmt(&**self, f) } } - } -); + }; +} mod access_control; mod accept; diff --git a/src/header/common/referer.rs b/src/header/common/referer.rs index 48394015..ab720537 100644 --- a/src/header/common/referer.rs +++ b/src/header/common/referer.rs @@ -1,16 +1,19 @@ -/// The `Referer` header. -/// -/// The Referer header is used by user agents to inform server about -/// the page URL user has came from. -/// -/// See alse [RFC 1945, section 10.13](http://tools.ietf.org/html/rfc1945#section-10.13). -/// -/// Currently just a string, but maybe better replace it with url::Url or something like it. -#[derive(Clone, PartialEq, Debug)] -pub struct Referer(pub String); - -impl_header!(Referer, - "Referer", - String); +header! { + #[doc="`Referer` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-5.5.2)"] + #[doc=""] + #[doc="The `Referer` [sic] header field allows the user agent to specify a"] + #[doc="URI reference for the resource from which the target URI was obtained"] + #[doc="(i.e., the \"referrer\", though the field name is misspelled). A user"] + #[doc="agent MUST NOT include the fragment and userinfo components of the"] + #[doc="URI reference, if any, when generating the Referer field value."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Referer = absolute-URI / partial-URI"] + #[doc="```"] + // TODO: Use URL + (Referer, "Referer") => [String] +} bench_header!(bench, Referer, { vec![b"http://foo.com/hello:3000".to_vec()] }); diff --git a/src/header/common/server.rs b/src/header/common/server.rs index 2b95033e..476d4bc0 100644 --- a/src/header/common/server.rs +++ b/src/header/common/server.rs @@ -1,11 +1,20 @@ -/// The `Server` header field. -/// -/// They can contain any value, so it just wraps a `String`. -#[derive(Clone, PartialEq, Debug)] -pub struct Server(pub String); - -impl_header!(Server, - "Server", - String); +header! { + #[doc="`Server` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-7.4.2)"] + #[doc=""] + #[doc="The `Server` header field contains information about the software"] + #[doc="used by the origin server to handle the request, which is often used"] + #[doc="by clients to help identify the scope of reported interoperability"] + #[doc="problems, to work around or tailor requests to avoid particular"] + #[doc="server limitations, and for analytics regarding server or operating"] + #[doc="system use. An origin server MAY generate a Server field in its"] + #[doc="responses."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Server = product *( RWS ( product / comment ) )"] + #[doc="```"] + // TODO: Maybe parse as defined in the spec? + (Server, "Server") => [String] +} bench_header!(bench, Server, { vec![b"Some String".to_vec()] }); diff --git a/src/header/common/transfer_encoding.rs b/src/header/common/transfer_encoding.rs index c031f2f5..4e8db0e9 100644 --- a/src/header/common/transfer_encoding.rs +++ b/src/header/common/transfer_encoding.rs @@ -1,24 +1,20 @@ use header::Encoding; -/// The `Transfer-Encoding` header. -/// -/// This header describes the encoding of the message body. It can be -/// comma-separated, including multiple encodings. -/// -/// ```notrust -/// Transfer-Encoding: gzip, chunked -/// ``` -/// -/// According to the spec, if a `Content-Length` header is not included, -/// this header should include `chunked` as the last encoding. -/// -/// The implementation uses a vector of `Encoding` values. -#[derive(Clone, PartialEq, Debug)] -pub struct TransferEncoding(pub Vec); - -impl_list_header!(TransferEncoding, - "Transfer-Encoding", - Vec); +header! { + #[doc="`Transfer-Encoding` header, defined in"] + #[doc="[RFC7230](http://tools.ietf.org/html/rfc7230#section-3.3.1)"] + #[doc=""] + #[doc="The `Transfer-Encoding` header field lists the transfer coding names"] + #[doc="corresponding to the sequence of transfer codings that have been (or"] + #[doc="will be) applied to the payload body in order to form the message"] + #[doc="body."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="Transfer-Encoding = 1#transfer-coding"] + #[doc="```"] + (TransferEncoding, "Transfer-Encoding") => (Encoding)+ +} bench_header!(normal, TransferEncoding, { vec![b"chunked, gzip".to_vec()] }); bench_header!(ext, TransferEncoding, { vec![b"ext".to_vec()] }); diff --git a/src/header/common/user_agent.rs b/src/header/common/user_agent.rs index 51112d1f..2a481e9b 100644 --- a/src/header/common/user_agent.rs +++ b/src/header/common/user_agent.rs @@ -1,14 +1,24 @@ -/// The `User-Agent` header field. -/// -/// They can contain any value, so it just wraps a `String`. -#[derive(Clone, PartialEq, Debug)] -pub struct UserAgent(pub String); - -impl_header!(UserAgent, - "User-Agent", - String); - -bench_header!(bench, UserAgent, { vec![b"cargo bench".to_vec()] }); +header! { + #[doc="`User-Agent` header, defined in"] + #[doc="[RFC7231](http://tools.ietf.org/html/rfc7231#section-5.5.3)"] + #[doc=""] + #[doc="The `User-Agent` header field contains information about the user"] + #[doc="agent originating the request, which is often used by servers to help"] + #[doc="identify the scope of reported interoperability problems, to work"] + #[doc="around or tailor responses to avoid particular user agent"] + #[doc="limitations, and for analytics regarding browser or operating system"] + #[doc="use. A user agent SHOULD send a User-Agent field in each request"] + #[doc="unless specifically configured not to do so."] + #[doc=""] + #[doc="# ABNF"] + #[doc="```plain"] + #[doc="User-Agent = product *( RWS ( product / comment ) )"] + #[doc="product = token [\"/\" product-version]"] + #[doc="product-version = token"] + #[doc="```"] + // TODO: Maybe write parsing according to the spec? (Split the String) + (UserAgent, "User-Agent") => [String] +} #[test] fn test_format() { use std::borrow::ToOwned;