summaryrefslogtreecommitdiffstats
path: root/third_party/rust/headers/src/disabled
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/headers/src/disabled
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/headers/src/disabled')
-rw-r--r--third_party/rust/headers/src/disabled/accept.rs150
-rw-r--r--third_party/rust/headers/src/disabled/accept_charset.rs57
-rw-r--r--third_party/rust/headers/src/disabled/accept_encoding.rs72
-rw-r--r--third_party/rust/headers/src/disabled/accept_language.rs72
-rw-r--r--third_party/rust/headers/src/disabled/content_language.rs35
-rw-r--r--third_party/rust/headers/src/disabled/from.rs29
-rw-r--r--third_party/rust/headers/src/disabled/last_event_id.rs40
-rw-r--r--third_party/rust/headers/src/disabled/link.rs1105
-rw-r--r--third_party/rust/headers/src/disabled/prefer.rs210
-rw-r--r--third_party/rust/headers/src/disabled/preference_applied.rs110
-rw-r--r--third_party/rust/headers/src/disabled/util/charset.rs229
-rw-r--r--third_party/rust/headers/src/disabled/util/encoding.rs57
-rw-r--r--third_party/rust/headers/src/disabled/util/extended_value.rs192
-rw-r--r--third_party/rust/headers/src/disabled/util/quality_value.rs268
-rw-r--r--third_party/rust/headers/src/disabled/warning.rs182
15 files changed, 2808 insertions, 0 deletions
diff --git a/third_party/rust/headers/src/disabled/accept.rs b/third_party/rust/headers/src/disabled/accept.rs
new file mode 100644
index 0000000000..3e8b77396c
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/accept.rs
@@ -0,0 +1,150 @@
+use mime::{self, Mime};
+
+use {QualityItem, qitem};
+
+header! {
+ /// `Accept` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.2)
+ ///
+ /// The `Accept` header field can be used by user agents to specify
+ /// response media types that are acceptable. Accept header fields can
+ /// be used to indicate that the request is specifically limited to a
+ /// small set of desired types, as in the case of a request for an
+ /// in-line image
+ ///
+ /// # ABNF
+ ///
+ /// ```text
+ /// Accept = #( media-range [ accept-params ] )
+ ///
+ /// media-range = ( "*/*"
+ /// / ( type "/" "*" )
+ /// / ( type "/" subtype )
+ /// ) *( OWS ";" OWS parameter )
+ /// accept-params = weight *( accept-ext )
+ /// accept-ext = OWS ";" OWS token [ "=" ( token / quoted-string ) ]
+ /// ```
+ ///
+ /// # Example values
+ /// * `audio/*; q=0.2, audio/basic`
+ /// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`
+ ///
+ /// # Examples
+ /// ```
+ /// # extern crate headers;
+ /// extern crate mime;
+ /// use headers::{Headers, Accept, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ ///
+ /// headers.set(
+ /// Accept(vec![
+ /// qitem(mime::TEXT_HTML),
+ /// ])
+ /// );
+ /// ```
+ ///
+ /// ```
+ /// # extern crate headers;
+ /// extern crate mime;
+ /// use headers::{Headers, Accept, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// Accept(vec![
+ /// qitem(mime::APPLICATION_JSON),
+ /// ])
+ /// );
+ /// ```
+ /// ```
+ /// # extern crate headers;
+ /// extern crate mime;
+ /// use headers::{Headers, Accept, QualityItem, q, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ ///
+ /// headers.set(
+ /// Accept(vec![
+ /// qitem(mime::TEXT_HTML),
+ /// qitem("application/xhtml+xml".parse().unwrap()),
+ /// QualityItem::new(
+ /// mime::TEXT_XML,
+ /// q(900)
+ /// ),
+ /// qitem("image/webp".parse().unwrap()),
+ /// QualityItem::new(
+ /// mime::STAR_STAR,
+ /// q(800)
+ /// ),
+ /// ])
+ /// );
+ /// ```
+ (Accept, ACCEPT) => (QualityItem<Mime>)+
+
+ test_accept {
+ // Tests from the RFC
+ test_header!(
+ test1,
+ vec![b"audio/*; q=0.2, audio/basic"],
+ Some(HeaderField(vec![
+ QualityItem::new("audio/*".parse().unwrap(), q(200)),
+ qitem("audio/basic".parse().unwrap()),
+ ])));
+ test_header!(
+ test2,
+ vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"],
+ Some(HeaderField(vec![
+ QualityItem::new(TEXT_PLAIN, q(500)),
+ qitem(TEXT_HTML),
+ QualityItem::new(
+ "text/x-dvi".parse().unwrap(),
+ q(800)),
+ qitem("text/x-c".parse().unwrap()),
+ ])));
+ // Custom tests
+ test_header!(
+ test3,
+ vec![b"text/plain; charset=utf-8"],
+ Some(Accept(vec![
+ qitem(TEXT_PLAIN_UTF_8),
+ ])));
+ test_header!(
+ test4,
+ vec![b"text/plain; charset=utf-8; q=0.5"],
+ Some(Accept(vec![
+ QualityItem::new(TEXT_PLAIN_UTF_8,
+ q(500)),
+ ])));
+
+ #[test]
+ fn test_fuzzing1() {
+ let raw: Raw = "chunk#;e".into();
+ let header = Accept::parse_header(&raw);
+ assert!(header.is_ok());
+ }
+ }
+}
+
+impl Accept {
+ /// A constructor to easily create `Accept: */*`.
+ pub fn star() -> Accept {
+ Accept(vec![qitem(mime::STAR_STAR)])
+ }
+
+ /// A constructor to easily create `Accept: application/json`.
+ pub fn json() -> Accept {
+ Accept(vec![qitem(mime::APPLICATION_JSON)])
+ }
+
+ /// A constructor to easily create `Accept: text/*`.
+ pub fn text() -> Accept {
+ Accept(vec![qitem(mime::TEXT_STAR)])
+ }
+
+ /// A constructor to easily create `Accept: image/*`.
+ pub fn image() -> Accept {
+ Accept(vec![qitem(mime::IMAGE_STAR)])
+ }
+}
+
+
+bench_header!(bench, Accept, { vec![b"text/plain; q=0.5, text/html".to_vec()] });
diff --git a/third_party/rust/headers/src/disabled/accept_charset.rs b/third_party/rust/headers/src/disabled/accept_charset.rs
new file mode 100644
index 0000000000..96eec89683
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/accept_charset.rs
@@ -0,0 +1,57 @@
+use {Charset, QualityItem};
+
+header! {
+ /// `Accept-Charset` header, defined in
+ /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.3)
+ ///
+ /// The `Accept-Charset` header field can be sent by a user agent to
+ /// indicate what charsets are acceptable in textual response content.
+ /// This field allows user agents capable of understanding more
+ /// comprehensive or special-purpose charsets to signal that capability
+ /// to an origin server that is capable of representing information in
+ /// those charsets.
+ ///
+ /// # ABNF
+ ///
+ /// ```text
+ /// Accept-Charset = 1#( ( charset / "*" ) [ weight ] )
+ /// ```
+ ///
+ /// # Example values
+ /// * `iso-8859-5, unicode-1-1;q=0.8`
+ ///
+ /// # Examples
+ /// ```
+ /// use headers::{Headers, AcceptCharset, Charset, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptCharset(vec![qitem(Charset::Us_Ascii)])
+ /// );
+ /// ```
+ /// ```
+ /// use headers::{Headers, AcceptCharset, Charset, q, QualityItem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptCharset(vec![
+ /// QualityItem::new(Charset::Us_Ascii, q(900)),
+ /// QualityItem::new(Charset::Iso_8859_10, q(200)),
+ /// ])
+ /// );
+ /// ```
+ /// ```
+ /// use headers::{Headers, AcceptCharset, Charset, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptCharset(vec![qitem(Charset::Ext("utf-8".to_owned()))])
+ /// );
+ /// ```
+ (AcceptCharset, ACCEPT_CHARSET) => (QualityItem<Charset>)+
+
+ test_accept_charset {
+ /// Testcase from RFC
+ test_header!(test1, vec![b"iso-8859-5, unicode-1-1;q=0.8"]);
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/accept_encoding.rs b/third_party/rust/headers/src/disabled/accept_encoding.rs
new file mode 100644
index 0000000000..5eb3adf50b
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/accept_encoding.rs
@@ -0,0 +1,72 @@
+use {Encoding, QualityItem};
+
+header! {
+ /// `Accept-Encoding` header, defined in
+ /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.4)
+ ///
+ /// The `Accept-Encoding` header field can be used by user agents to
+ /// indicate what response content-codings are
+ /// acceptable in the response. An `identity` token is used as a synonym
+ /// for "no encoding" in order to communicate when no encoding is
+ /// preferred.
+ ///
+ /// # ABNF
+ ///
+ /// ```text
+ /// Accept-Encoding = #( codings [ weight ] )
+ /// codings = content-coding / "identity" / "*"
+ /// ```
+ ///
+ /// # Example values
+ /// * `compress, gzip`
+ /// * ``
+ /// * `*`
+ /// * `compress;q=0.5, gzip;q=1`
+ /// * `gzip;q=1.0, identity; q=0.5, *;q=0`
+ ///
+ /// # Examples
+ /// ```
+ /// use headers::{Headers, AcceptEncoding, Encoding, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptEncoding(vec![qitem(Encoding::Chunked)])
+ /// );
+ /// ```
+ /// ```
+ /// use headers::{Headers, AcceptEncoding, Encoding, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptEncoding(vec![
+ /// qitem(Encoding::Chunked),
+ /// qitem(Encoding::Gzip),
+ /// qitem(Encoding::Deflate),
+ /// ])
+ /// );
+ /// ```
+ /// ```
+ /// use headers::{Headers, AcceptEncoding, Encoding, QualityItem, q, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptEncoding(vec![
+ /// qitem(Encoding::Chunked),
+ /// QualityItem::new(Encoding::Gzip, q(600)),
+ /// QualityItem::new(Encoding::EncodingExt("*".to_owned()), q(0)),
+ /// ])
+ /// );
+ /// ```
+ (AcceptEncoding, ACCEPT_ENCODING) => (QualityItem<Encoding>)*
+
+ test_accept_encoding {
+ // From the RFC
+ test_header!(test1, vec![b"compress, gzip"]);
+ test_header!(test2, vec![b""], Some(AcceptEncoding(vec![])));
+ test_header!(test3, vec![b"*"]);
+ // Note: Removed quality 1 from gzip
+ test_header!(test4, vec![b"compress;q=0.5, gzip"]);
+ // Note: Removed quality 1 from gzip
+ test_header!(test5, vec![b"gzip, identity; q=0.5, *;q=0"]);
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/accept_language.rs b/third_party/rust/headers/src/disabled/accept_language.rs
new file mode 100644
index 0000000000..dccfdb0bc3
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/accept_language.rs
@@ -0,0 +1,72 @@
+use language_tags::LanguageTag;
+use QualityItem;
+
+header! {
+ /// `Accept-Language` header, defined in
+ /// [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.3.5)
+ ///
+ /// The `Accept-Language` header field can be used by user agents to
+ /// indicate the set of natural languages that are preferred in the
+ /// response.
+ ///
+ /// # ABNF
+ ///
+ /// ```text
+ /// Accept-Language = 1#( language-range [ weight ] )
+ /// language-range = <language-range, see [RFC4647], Section 2.1>
+ /// ```
+ ///
+ /// # Example values
+ /// * `da, en-gb;q=0.8, en;q=0.7`
+ /// * `en-us;q=1.0, en;q=0.5, fr`
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use headers::{Headers, AcceptLanguage, LanguageTag, qitem};
+ ///
+ /// let mut headers = Headers::new();
+ /// let mut langtag: LanguageTag = Default::default();
+ /// langtag.language = Some("en".to_owned());
+ /// langtag.region = Some("US".to_owned());
+ /// headers.set(
+ /// AcceptLanguage(vec![
+ /// qitem(langtag),
+ /// ])
+ /// );
+ /// ```
+ ///
+ /// ```
+ /// # extern crate headers;
+ /// # #[macro_use] extern crate language_tags;
+ /// # use headers::{Headers, AcceptLanguage, QualityItem, q, qitem};
+ /// #
+ /// # fn main() {
+ /// let mut headers = Headers::new();
+ /// headers.set(
+ /// AcceptLanguage(vec![
+ /// qitem(langtag!(da)),
+ /// QualityItem::new(langtag!(en;;;GB), q(800)),
+ /// QualityItem::new(langtag!(en), q(700)),
+ /// ])
+ /// );
+ /// # }
+ /// ```
+ (AcceptLanguage, ACCEPT_LANGUAGE) => (QualityItem<LanguageTag>)+
+
+ test_accept_language {
+ // From the RFC
+ test_header!(test1, vec![b"da, en-gb;q=0.8, en;q=0.7"]);
+ // Own test
+ test_header!(
+ test2, vec![b"en-US, en; q=0.5, fr"],
+ Some(AcceptLanguage(vec![
+ qitem("en-US".parse().unwrap()),
+ QualityItem::new("en".parse().unwrap(), q(500)),
+ qitem("fr".parse().unwrap()),
+ ])));
+ }
+}
+
+bench_header!(bench, AcceptLanguage,
+ { vec![b"en-us;q=1.0, en;q=0.5, fr".to_vec()] });
diff --git a/third_party/rust/headers/src/disabled/content_language.rs b/third_party/rust/headers/src/disabled/content_language.rs
new file mode 100644
index 0000000000..cca8dcbbf6
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/content_language.rs
@@ -0,0 +1,35 @@
+use util::FlatCsv;
+
+/// `Content-Language` header, defined in
+/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-3.1.3.2)
+///
+/// The `Content-Language` header field describes the natural language(s)
+/// of the intended audience for the representation. Note that this
+/// might not be equivalent to all the languages used within the
+/// representation.
+///
+/// # ABNF
+///
+/// ```text
+/// Content-Language = 1#language-tag
+/// ```
+///
+/// # Example values
+///
+/// * `da`
+/// * `mi, en`
+///
+/// # Examples
+///
+/// ```
+/// # extern crate headers;
+/// #[macro_use] extern crate language_tags;
+/// use headers::ContentLanguage;
+/// #
+/// # fn main() {
+/// let con_lang = ContentLanguage::new([langtag!(en)])
+/// # }
+/// ```
+#[derive(Clone, Debug, PartialEq, Header)]
+pub struct ContentLanguage(FlatCsv);
+
diff --git a/third_party/rust/headers/src/disabled/from.rs b/third_party/rust/headers/src/disabled/from.rs
new file mode 100644
index 0000000000..ed00d8e986
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/from.rs
@@ -0,0 +1,29 @@
+header! {
+ /// `From` header, defined in [RFC7231](http://tools.ietf.org/html/rfc7231#section-5.5.1)
+ ///
+ /// The `From` header field contains an Internet email address for a
+ /// human user who controls the requesting user agent. The address ought
+ /// to be machine-usable.
+ ///
+ /// # ABNF
+ ///
+ /// ```text
+ /// From = mailbox
+ /// mailbox = <mailbox, see [RFC5322], Section 3.4>
+ /// ```
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use headers::{Headers, From};
+ ///
+ /// let mut headers = Headers::new();
+ /// headers.set(From("webmaster@example.org".to_owned()));
+ /// ```
+ // FIXME: Maybe use mailbox?
+ (From, FROM) => [String]
+
+ test_from {
+ test_header!(test1, vec![b"webmaster@example.org"]);
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/last_event_id.rs b/third_party/rust/headers/src/disabled/last_event_id.rs
new file mode 100644
index 0000000000..c2499b0a0d
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/last_event_id.rs
@@ -0,0 +1,40 @@
+use std::fmt;
+
+use util::HeaderValueString;
+
+/// `Last-Event-ID` header, defined in
+/// [RFC3864](https://html.spec.whatwg.org/multipage/references.html#refsRFC3864)
+///
+/// The `Last-Event-ID` header contains information about
+/// the last event in an http interaction so that it's easier to
+/// track of event state. This is helpful when working
+/// with [Server-Sent-Events](http://www.html5rocks.com/en/tutorials/eventsource/basics/). If the connection were to be dropped, for example, it'd
+/// be useful to let the server know what the last event you
+/// received was.
+///
+/// The spec is a String with the id of the last event, it can be
+/// an empty string which acts a sort of "reset".
+// NOTE: This module is disabled since there is no const LAST_EVENT_ID to be
+// used for the `impl Header`. It should be possible to enable this module
+// when `HeaderName::from_static` can become a `const fn`.
+#[derive(Clone, Debug, PartialEq, Header)]
+pub struct LastEventId(HeaderValueString);
+
+
+impl fmt::Display for LastEventId {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+
+ /*
+ // Initial state
+ test_header!(test1, vec![b""]);
+ // Own testcase
+ test_header!(test2, vec![b"1"], Some(LastEventId("1".to_owned())));
+ */
+}
+
diff --git a/third_party/rust/headers/src/disabled/link.rs b/third_party/rust/headers/src/disabled/link.rs
new file mode 100644
index 0000000000..a6d8494438
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/link.rs
@@ -0,0 +1,1105 @@
+use std::fmt;
+use std::borrow::Cow;
+use std::str::FromStr;
+#[allow(unused, deprecated)]
+use std::ascii::AsciiExt;
+
+use mime::Mime;
+use language_tags::LanguageTag;
+
+use parsing;
+use {Header, Raw};
+
+/// The `Link` header, defined in
+/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
+///
+/// # ABNF
+///
+/// ```text
+/// Link = "Link" ":" #link-value
+/// link-value = "<" URI-Reference ">" *( ";" link-param )
+/// link-param = ( ( "rel" "=" relation-types )
+/// | ( "anchor" "=" <"> URI-Reference <"> )
+/// | ( "rev" "=" relation-types )
+/// | ( "hreflang" "=" Language-Tag )
+/// | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
+/// | ( "title" "=" quoted-string )
+/// | ( "title*" "=" ext-value )
+/// | ( "type" "=" ( media-type | quoted-mt ) )
+/// | ( link-extension ) )
+/// link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
+/// | ( ext-name-star "=" ext-value )
+/// ext-name-star = parmname "*" ; reserved for RFC2231-profiled
+/// ; extensions. Whitespace NOT
+/// ; allowed in between.
+/// ptoken = 1*ptokenchar
+/// ptokenchar = "!" | "#" | "$" | "%" | "&" | "'" | "("
+/// | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
+/// | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
+/// | "[" | "]" | "^" | "_" | "`" | "{" | "|"
+/// | "}" | "~"
+/// media-type = type-name "/" subtype-name
+/// quoted-mt = <"> media-type <">
+/// relation-types = relation-type
+/// | <"> relation-type *( 1*SP relation-type ) <">
+/// relation-type = reg-rel-type | ext-rel-type
+/// reg-rel-type = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+/// ext-rel-type = URI
+/// ```
+///
+/// # Example values
+///
+/// `Link: <http://example.com/TheBook/chapter2>; rel="previous";
+/// title="previous chapter"`
+///
+/// `Link: </TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel,
+/// </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel`
+///
+/// # Examples
+///
+/// ```
+/// use headers::{Headers, Link, LinkValue, RelationType};
+///
+/// let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
+/// .push_rel(RelationType::Previous)
+/// .set_title("previous chapter");
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// Link::new(vec![link_value])
+/// );
+/// ```
+#[derive(Clone, PartialEq, Debug)]
+pub struct Link {
+ /// A list of the `link-value`s of the Link entity-header.
+ values: Vec<LinkValue>
+}
+
+/// A single `link-value` of a `Link` header, based on:
+/// [RFC5988](http://tools.ietf.org/html/rfc5988#section-5)
+#[derive(Clone, PartialEq, Debug)]
+pub struct LinkValue {
+ /// Target IRI: `link-value`.
+ link: Cow<'static, str>,
+
+ /// Forward Relation Types: `rel`.
+ rel: Option<Vec<RelationType>>,
+
+ /// Context IRI: `anchor`.
+ anchor: Option<String>,
+
+ /// Reverse Relation Types: `rev`.
+ rev: Option<Vec<RelationType>>,
+
+ /// Hint on the language of the result of dereferencing
+ /// the link: `hreflang`.
+ href_lang: Option<Vec<LanguageTag>>,
+
+ /// Destination medium or media: `media`.
+ media_desc: Option<Vec<MediaDesc>>,
+
+ /// Label of the destination of a Link: `title`.
+ title: Option<String>,
+
+ /// The `title` encoded in a different charset: `title*`.
+ title_star: Option<String>,
+
+ /// Hint on the media type of the result of dereferencing
+ /// the link: `type`.
+ media_type: Option<Mime>,
+}
+
+/// A Media Descriptors Enum based on:
+/// [https://www.w3.org/TR/html401/types.html#h-6.13][url]
+///
+/// [url]: https://www.w3.org/TR/html401/types.html#h-6.13
+#[derive(Clone, PartialEq, Debug)]
+pub enum MediaDesc {
+ /// screen.
+ Screen,
+ /// tty.
+ Tty,
+ /// tv.
+ Tv,
+ /// projection.
+ Projection,
+ /// handheld.
+ Handheld,
+ /// print.
+ Print,
+ /// braille.
+ Braille,
+ /// aural.
+ Aural,
+ /// all.
+ All,
+ /// Unrecognized media descriptor extension.
+ Extension(String)
+}
+
+/// A Link Relation Type Enum based on:
+/// [RFC5988](https://tools.ietf.org/html/rfc5988#section-6.2.2)
+#[derive(Clone, PartialEq, Debug)]
+pub enum RelationType {
+ /// alternate.
+ Alternate,
+ /// appendix.
+ Appendix,
+ /// bookmark.
+ Bookmark,
+ /// chapter.
+ Chapter,
+ /// contents.
+ Contents,
+ /// copyright.
+ Copyright,
+ /// current.
+ Current,
+ /// describedby.
+ DescribedBy,
+ /// edit.
+ Edit,
+ /// edit-media.
+ EditMedia,
+ /// enclosure.
+ Enclosure,
+ /// first.
+ First,
+ /// glossary.
+ Glossary,
+ /// help.
+ Help,
+ /// hub.
+ Hub,
+ /// index.
+ Index,
+ /// last.
+ Last,
+ /// latest-version.
+ LatestVersion,
+ /// license.
+ License,
+ /// next.
+ Next,
+ /// next-archive.
+ NextArchive,
+ /// payment.
+ Payment,
+ /// prev.
+ Prev,
+ /// predecessor-version.
+ PredecessorVersion,
+ /// previous.
+ Previous,
+ /// prev-archive.
+ PrevArchive,
+ /// related.
+ Related,
+ /// replies.
+ Replies,
+ /// section.
+ Section,
+ /// self.
+ RelationTypeSelf,
+ /// service.
+ Service,
+ /// start.
+ Start,
+ /// stylesheet.
+ Stylesheet,
+ /// subsection.
+ Subsection,
+ /// successor-version.
+ SuccessorVersion,
+ /// up.
+ Up,
+ /// versionHistory.
+ VersionHistory,
+ /// via.
+ Via,
+ /// working-copy.
+ WorkingCopy,
+ /// working-copy-of.
+ WorkingCopyOf,
+ /// ext-rel-type.
+ ExtRelType(String)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Struct methods
+////////////////////////////////////////////////////////////////////////////////
+
+impl Link {
+ /// Create `Link` from a `Vec<LinkValue>`.
+ pub fn new(link_values: Vec<LinkValue>) -> Link {
+ Link { values: link_values }
+ }
+
+ /// Get the `Link` header's `LinkValue`s.
+ pub fn values(&self) -> &[LinkValue] {
+ self.values.as_ref()
+ }
+
+ /// Add a `LinkValue` instance to the `Link` header's values.
+ pub fn push_value(&mut self, link_value: LinkValue) {
+ self.values.push(link_value);
+ }
+}
+
+impl LinkValue {
+ /// Create `LinkValue` from URI-Reference.
+ pub fn new<T>(uri: T) -> LinkValue
+ where T: Into<Cow<'static, str>> {
+ LinkValue {
+ link: uri.into(),
+ rel: None,
+ anchor: None,
+ rev: None,
+ href_lang: None,
+ media_desc: None,
+ title: None,
+ title_star: None,
+ media_type: None,
+ }
+ }
+
+ /// Get the `LinkValue`'s value.
+ pub fn link(&self) -> &str {
+ self.link.as_ref()
+ }
+
+ /// Get the `LinkValue`'s `rel` parameter(s).
+ pub fn rel(&self) -> Option<&[RelationType]> {
+ self.rel.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `anchor` parameter.
+ pub fn anchor(&self) -> Option<&str> {
+ self.anchor.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `rev` parameter(s).
+ pub fn rev(&self) -> Option<&[RelationType]> {
+ self.rev.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `hreflang` parameter(s).
+ pub fn href_lang(&self) -> Option<&[LanguageTag]> {
+ self.href_lang.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `media` parameter(s).
+ pub fn media_desc(&self) -> Option<&[MediaDesc]> {
+ self.media_desc.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `title` parameter.
+ pub fn title(&self) -> Option<&str> {
+ self.title.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `title*` parameter.
+ pub fn title_star(&self) -> Option<&str> {
+ self.title_star.as_ref().map(AsRef::as_ref)
+ }
+
+ /// Get the `LinkValue`'s `type` parameter.
+ pub fn media_type(&self) -> Option<&Mime> {
+ self.media_type.as_ref()
+ }
+
+ /// Add a `RelationType` to the `LinkValue`'s `rel` parameter.
+ pub fn push_rel(mut self, rel: RelationType) -> LinkValue {
+ let mut v = self.rel.take().unwrap_or(Vec::new());
+
+ v.push(rel);
+
+ self.rel = Some(v);
+
+ self
+ }
+
+ /// Set `LinkValue`'s `anchor` parameter.
+ pub fn set_anchor<T: Into<String>>(mut self, anchor: T) -> LinkValue {
+ self.anchor = Some(anchor.into());
+
+ self
+ }
+
+ /// Add a `RelationType` to the `LinkValue`'s `rev` parameter.
+ pub fn push_rev(mut self, rev: RelationType) -> LinkValue {
+ let mut v = self.rev.take().unwrap_or(Vec::new());
+
+ v.push(rev);
+
+ self.rev = Some(v);
+
+ self
+ }
+
+ /// Add a `LanguageTag` to the `LinkValue`'s `hreflang` parameter.
+ pub fn push_href_lang(mut self, language_tag: LanguageTag) -> LinkValue {
+ let mut v = self.href_lang.take().unwrap_or(Vec::new());
+
+ v.push(language_tag);
+
+ self.href_lang = Some(v);
+
+ self
+ }
+
+ /// Add a `MediaDesc` to the `LinkValue`'s `media_desc` parameter.
+ pub fn push_media_desc(mut self, media_desc: MediaDesc) -> LinkValue {
+ let mut v = self.media_desc.take().unwrap_or(Vec::new());
+
+ v.push(media_desc);
+
+ self.media_desc = Some(v);
+
+ self
+ }
+
+ /// Set `LinkValue`'s `title` parameter.
+ pub fn set_title<T: Into<String>>(mut self, title: T) -> LinkValue {
+ self.title = Some(title.into());
+
+ self
+ }
+
+ /// Set `LinkValue`'s `title*` parameter.
+ pub fn set_title_star<T: Into<String>>(mut self, title_star: T) -> LinkValue {
+ self.title_star = Some(title_star.into());
+
+ self
+ }
+
+ /// Set `LinkValue`'s `type` parameter.
+ pub fn set_media_type(mut self, media_type: Mime) -> LinkValue {
+ self.media_type = Some(media_type);
+
+ self
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Trait implementations
+////////////////////////////////////////////////////////////////////////////////
+
+impl Header for Link {
+ fn header_name() -> &'static str {
+ static NAME: &'static str = "Link";
+ NAME
+ }
+
+ fn parse_header(raw: &Raw) -> ::Result<Link> {
+ // If more that one `Link` headers are present in a request's
+ // headers they are combined in a single `Link` header containing
+ // all the `link-value`s present in each of those `Link` headers.
+ raw.iter()
+ .map(parsing::from_raw_str::<Link>)
+ .fold(None, |p, c| {
+ match (p, c) {
+ (None, c) => Some(c),
+ (e @ Some(Err(_)), _) => e,
+ (Some(Ok(mut p)), Ok(c)) => {
+ p.values.extend(c.values);
+
+ Some(Ok(p))
+ },
+ _ => Some(Err(::Error::Header)),
+ }
+ })
+ .unwrap_or(Err(::Error::Header))
+ }
+
+ fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result {
+ f.fmt_line(self)
+ }
+}
+
+impl fmt::Display for Link {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt_delimited(f, self.values.as_slice(), ", ", ("", ""))
+ }
+}
+
+impl fmt::Display for LinkValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ try!(write!(f, "<{}>", self.link));
+
+ if let Some(ref rel) = self.rel {
+ try!(fmt_delimited(f, rel.as_slice(), " ", ("; rel=\"", "\"")));
+ }
+ if let Some(ref anchor) = self.anchor {
+ try!(write!(f, "; anchor=\"{}\"", anchor));
+ }
+ if let Some(ref rev) = self.rev {
+ try!(fmt_delimited(f, rev.as_slice(), " ", ("; rev=\"", "\"")));
+ }
+ if let Some(ref href_lang) = self.href_lang {
+ for tag in href_lang {
+ try!(write!(f, "; hreflang={}", tag));
+ }
+ }
+ if let Some(ref media_desc) = self.media_desc {
+ try!(fmt_delimited(f, media_desc.as_slice(), ", ", ("; media=\"", "\"")));
+ }
+ if let Some(ref title) = self.title {
+ try!(write!(f, "; title=\"{}\"", title));
+ }
+ if let Some(ref title_star) = self.title_star {
+ try!(write!(f, "; title*={}", title_star));
+ }
+ if let Some(ref media_type) = self.media_type {
+ try!(write!(f, "; type=\"{}\"", media_type));
+ }
+
+ Ok(())
+ }
+}
+
+impl FromStr for Link {
+ type Err = ::Error;
+
+ fn from_str(s: &str) -> ::Result<Link> {
+ // Create a split iterator with delimiters: `;`, `,`
+ let link_split = SplitAsciiUnquoted::new(s, ";,");
+
+ let mut link_values: Vec<LinkValue> = Vec::new();
+
+ // Loop over the splits parsing the Link header into
+ // a `Vec<LinkValue>`
+ for segment in link_split {
+ // Parse the `Target IRI`
+ // https://tools.ietf.org/html/rfc5988#section-5.1
+ if segment.trim().starts_with('<') {
+ link_values.push(
+ match verify_and_trim(segment.trim(), (b'<', b'>')) {
+ Err(_) => return Err(::Error::Header),
+ Ok(s) => {
+ LinkValue {
+ link: s.to_owned().into(),
+ rel: None,
+ anchor: None,
+ rev: None,
+ href_lang: None,
+ media_desc: None,
+ title: None,
+ title_star: None,
+ media_type: None,
+ }
+ },
+ }
+ );
+ } else {
+ // Parse the current link-value's parameters
+ let mut link_param_split = segment.splitn(2, '=');
+
+ let link_param_name = match link_param_split.next() {
+ None => return Err(::Error::Header),
+ Some(p) => p.trim(),
+ };
+
+ let link_header = match link_values.last_mut() {
+ None => return Err(::Error::Header),
+ Some(l) => l,
+ };
+
+ if "rel".eq_ignore_ascii_case(link_param_name) {
+ // Parse relation type: `rel`.
+ // https://tools.ietf.org/html/rfc5988#section-5.3
+ if link_header.rel.is_none() {
+ link_header.rel = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => {
+ s.trim_matches(|c: char| c == '"' || c.is_whitespace())
+ .split(' ')
+ .map(|t| t.trim().parse())
+ .collect::<Result<Vec<RelationType>, _>>()
+ .or_else(|_| Err(::Error::Header))
+ .ok()
+ },
+ };
+ }
+ } else if "anchor".eq_ignore_ascii_case(link_param_name) {
+ // Parse the `Context IRI`.
+ // https://tools.ietf.org/html/rfc5988#section-5.2
+ link_header.anchor = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
+ Err(_) => return Err(::Error::Header),
+ Ok(a) => Some(String::from(a)),
+ },
+ };
+ } else if "rev".eq_ignore_ascii_case(link_param_name) {
+ // Parse relation type: `rev`.
+ // https://tools.ietf.org/html/rfc5988#section-5.3
+ if link_header.rev.is_none() {
+ link_header.rev = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => {
+ s.trim_matches(|c: char| c == '"' || c.is_whitespace())
+ .split(' ')
+ .map(|t| t.trim().parse())
+ .collect::<Result<Vec<RelationType>, _>>()
+ .or_else(|_| Err(::Error::Header))
+ .ok()
+ },
+ }
+ }
+ } else if "hreflang".eq_ignore_ascii_case(link_param_name) {
+ // Parse target attribute: `hreflang`.
+ // https://tools.ietf.org/html/rfc5988#section-5.4
+ let mut v = link_header.href_lang.take().unwrap_or(Vec::new());
+
+ v.push(
+ match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => match s.trim().parse() {
+ Err(_) => return Err(::Error::Header),
+ Ok(t) => t,
+ },
+ }
+ );
+
+ link_header.href_lang = Some(v);
+ } else if "media".eq_ignore_ascii_case(link_param_name) {
+ // Parse target attribute: `media`.
+ // https://tools.ietf.org/html/rfc5988#section-5.4
+ if link_header.media_desc.is_none() {
+ link_header.media_desc = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => {
+ s.trim_matches(|c: char| c == '"' || c.is_whitespace())
+ .split(',')
+ .map(|t| t.trim().parse())
+ .collect::<Result<Vec<MediaDesc>, _>>()
+ .or_else(|_| Err(::Error::Header))
+ .ok()
+ },
+ };
+ }
+ } else if "title".eq_ignore_ascii_case(link_param_name) {
+ // Parse target attribute: `title`.
+ // https://tools.ietf.org/html/rfc5988#section-5.4
+ if link_header.title.is_none() {
+ link_header.title = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
+ Err(_) => return Err(::Error::Header),
+ Ok(t) => Some(String::from(t)),
+ },
+ };
+ }
+ } else if "title*".eq_ignore_ascii_case(link_param_name) {
+ // Parse target attribute: `title*`.
+ // https://tools.ietf.org/html/rfc5988#section-5.4
+ //
+ // Definition of `ext-value`:
+ // https://tools.ietf.org/html/rfc5987#section-3.2.1
+ if link_header.title_star.is_none() {
+ link_header.title_star = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => Some(String::from(s.trim())),
+ };
+ }
+ } else if "type".eq_ignore_ascii_case(link_param_name) {
+ // Parse target attribute: `type`.
+ // https://tools.ietf.org/html/rfc5988#section-5.4
+ if link_header.media_type.is_none() {
+ link_header.media_type = match link_param_split.next() {
+ None | Some("") => return Err(::Error::Header),
+ Some(s) => match verify_and_trim(s.trim(), (b'"', b'"')) {
+ Err(_) => return Err(::Error::Header),
+ Ok(t) => match t.parse() {
+ Err(_) => return Err(::Error::Header),
+ Ok(m) => Some(m),
+ },
+ },
+
+ };
+ }
+ } else {
+ return Err(::Error::Header);
+ }
+ }
+ }
+
+ Ok(Link::new(link_values))
+ }
+}
+
+impl fmt::Display for MediaDesc {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ MediaDesc::Screen => write!(f, "screen"),
+ MediaDesc::Tty => write!(f, "tty"),
+ MediaDesc::Tv => write!(f, "tv"),
+ MediaDesc::Projection => write!(f, "projection"),
+ MediaDesc::Handheld => write!(f, "handheld"),
+ MediaDesc::Print => write!(f, "print"),
+ MediaDesc::Braille => write!(f, "braille"),
+ MediaDesc::Aural => write!(f, "aural"),
+ MediaDesc::All => write!(f, "all"),
+ MediaDesc::Extension(ref other) => write!(f, "{}", other),
+ }
+ }
+}
+
+impl FromStr for MediaDesc {
+ type Err = ::Error;
+
+ fn from_str(s: &str) -> ::Result<MediaDesc> {
+ match s {
+ "screen" => Ok(MediaDesc::Screen),
+ "tty" => Ok(MediaDesc::Tty),
+ "tv" => Ok(MediaDesc::Tv),
+ "projection" => Ok(MediaDesc::Projection),
+ "handheld" => Ok(MediaDesc::Handheld),
+ "print" => Ok(MediaDesc::Print),
+ "braille" => Ok(MediaDesc::Braille),
+ "aural" => Ok(MediaDesc::Aural),
+ "all" => Ok(MediaDesc::All),
+ _ => Ok(MediaDesc::Extension(String::from(s))),
+ }
+ }
+}
+
+impl fmt::Display for RelationType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ RelationType::Alternate => write!(f, "alternate"),
+ RelationType::Appendix => write!(f, "appendix"),
+ RelationType::Bookmark => write!(f, "bookmark"),
+ RelationType::Chapter => write!(f, "chapter"),
+ RelationType::Contents => write!(f, "contents"),
+ RelationType::Copyright => write!(f, "copyright"),
+ RelationType::Current => write!(f, "current"),
+ RelationType::DescribedBy => write!(f, "describedby"),
+ RelationType::Edit => write!(f, "edit"),
+ RelationType::EditMedia => write!(f, "edit-media"),
+ RelationType::Enclosure => write!(f, "enclosure"),
+ RelationType::First => write!(f, "first"),
+ RelationType::Glossary => write!(f, "glossary"),
+ RelationType::Help => write!(f, "help"),
+ RelationType::Hub => write!(f, "hub"),
+ RelationType::Index => write!(f, "index"),
+ RelationType::Last => write!(f, "last"),
+ RelationType::LatestVersion => write!(f, "latest-version"),
+ RelationType::License => write!(f, "license"),
+ RelationType::Next => write!(f, "next"),
+ RelationType::NextArchive => write!(f, "next-archive"),
+ RelationType::Payment => write!(f, "payment"),
+ RelationType::Prev => write!(f, "prev"),
+ RelationType::PredecessorVersion => write!(f, "predecessor-version"),
+ RelationType::Previous => write!(f, "previous"),
+ RelationType::PrevArchive => write!(f, "prev-archive"),
+ RelationType::Related => write!(f, "related"),
+ RelationType::Replies => write!(f, "replies"),
+ RelationType::Section => write!(f, "section"),
+ RelationType::RelationTypeSelf => write!(f, "self"),
+ RelationType::Service => write!(f, "service"),
+ RelationType::Start => write!(f, "start"),
+ RelationType::Stylesheet => write!(f, "stylesheet"),
+ RelationType::Subsection => write!(f, "subsection"),
+ RelationType::SuccessorVersion => write!(f, "successor-version"),
+ RelationType::Up => write!(f, "up"),
+ RelationType::VersionHistory => write!(f, "version-history"),
+ RelationType::Via => write!(f, "via"),
+ RelationType::WorkingCopy => write!(f, "working-copy"),
+ RelationType::WorkingCopyOf => write!(f, "working-copy-of"),
+ RelationType::ExtRelType(ref uri) => write!(f, "{}", uri),
+ }
+ }
+}
+
+impl FromStr for RelationType {
+ type Err = ::Error;
+
+ fn from_str(s: &str) -> ::Result<RelationType> {
+ if "alternate".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Alternate)
+ } else if "appendix".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Appendix)
+ } else if "bookmark".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Bookmark)
+ } else if "chapter".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Chapter)
+ } else if "contents".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Contents)
+ } else if "copyright".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Copyright)
+ } else if "current".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Current)
+ } else if "describedby".eq_ignore_ascii_case(s) {
+ Ok(RelationType::DescribedBy)
+ } else if "edit".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Edit)
+ } else if "edit-media".eq_ignore_ascii_case(s) {
+ Ok(RelationType::EditMedia)
+ } else if "enclosure".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Enclosure)
+ } else if "first".eq_ignore_ascii_case(s) {
+ Ok(RelationType::First)
+ } else if "glossary".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Glossary)
+ } else if "help".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Help)
+ } else if "hub".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Hub)
+ } else if "index".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Index)
+ } else if "last".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Last)
+ } else if "latest-version".eq_ignore_ascii_case(s) {
+ Ok(RelationType::LatestVersion)
+ } else if "license".eq_ignore_ascii_case(s) {
+ Ok(RelationType::License)
+ } else if "next".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Next)
+ } else if "next-archive".eq_ignore_ascii_case(s) {
+ Ok(RelationType::NextArchive)
+ } else if "payment".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Payment)
+ } else if "prev".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Prev)
+ } else if "predecessor-version".eq_ignore_ascii_case(s) {
+ Ok(RelationType::PredecessorVersion)
+ } else if "previous".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Previous)
+ } else if "prev-archive".eq_ignore_ascii_case(s) {
+ Ok(RelationType::PrevArchive)
+ } else if "related".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Related)
+ } else if "replies".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Replies)
+ } else if "section".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Section)
+ } else if "self".eq_ignore_ascii_case(s) {
+ Ok(RelationType::RelationTypeSelf)
+ } else if "service".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Service)
+ } else if "start".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Start)
+ } else if "stylesheet".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Stylesheet)
+ } else if "subsection".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Subsection)
+ } else if "successor-version".eq_ignore_ascii_case(s) {
+ Ok(RelationType::SuccessorVersion)
+ } else if "up".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Up)
+ } else if "version-history".eq_ignore_ascii_case(s) {
+ Ok(RelationType::VersionHistory)
+ } else if "via".eq_ignore_ascii_case(s) {
+ Ok(RelationType::Via)
+ } else if "working-copy".eq_ignore_ascii_case(s) {
+ Ok(RelationType::WorkingCopy)
+ } else if "working-copy-of".eq_ignore_ascii_case(s) {
+ Ok(RelationType::WorkingCopyOf)
+ } else {
+ Ok(RelationType::ExtRelType(String::from(s)))
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Utilities
+////////////////////////////////////////////////////////////////////////////////
+
+struct SplitAsciiUnquoted<'a> {
+ src: &'a str,
+ pos: usize,
+ del: &'a str
+}
+
+impl<'a> SplitAsciiUnquoted<'a> {
+ fn new(s: &'a str, d: &'a str) -> SplitAsciiUnquoted<'a> {
+ SplitAsciiUnquoted{
+ src: s,
+ pos: 0,
+ del: d,
+ }
+ }
+}
+
+impl<'a> Iterator for SplitAsciiUnquoted<'a> {
+ type Item = &'a str;
+
+ fn next(&mut self) -> Option<&'a str> {
+ if self.pos < self.src.len() {
+ let prev_pos = self.pos;
+ let mut pos = self.pos;
+
+ let mut in_quotes = false;
+
+ for c in self.src[prev_pos..].as_bytes().iter() {
+ in_quotes ^= *c == b'"';
+
+ // Ignore `c` if we're `in_quotes`.
+ if !in_quotes && self.del.as_bytes().contains(c) {
+ break;
+ }
+
+ pos += 1;
+ }
+
+ self.pos = pos + 1;
+
+ Some(&self.src[prev_pos..pos])
+ } else {
+ None
+ }
+ }
+}
+
+fn fmt_delimited<T: fmt::Display>(f: &mut fmt::Formatter, p: &[T], d: &str, b: (&str, &str)) -> fmt::Result {
+ if p.len() != 0 {
+ // Write a starting string `b.0` before the first element
+ try!(write!(f, "{}{}", b.0, p[0]));
+
+ for i in &p[1..] {
+ // Write the next element preceded by the delimiter `d`
+ try!(write!(f, "{}{}", d, i));
+ }
+
+ // Write a ending string `b.1` before the first element
+ try!(write!(f, "{}", b.1));
+ }
+
+ Ok(())
+}
+
+fn verify_and_trim(s: &str, b: (u8, u8)) -> ::Result<&str> {
+ let length = s.len();
+ let byte_array = s.as_bytes();
+
+ // Verify that `s` starts with `b.0` and ends with `b.1` and return
+ // the contained substring after trimming whitespace.
+ if length > 1 && b.0 == byte_array[0] && b.1 == byte_array[length - 1] {
+ Ok(s.trim_matches(
+ |c: char| c == b.0 as char || c == b.1 as char || c.is_whitespace())
+ )
+ } else {
+ Err(::Error::Header)
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Tests
+////////////////////////////////////////////////////////////////////////////////
+
+#[cfg(test)]
+mod tests {
+ use std::fmt;
+ use std::fmt::Write;
+
+ use super::{Link, LinkValue, MediaDesc, RelationType, SplitAsciiUnquoted};
+ use super::{fmt_delimited, verify_and_trim};
+
+ use Header;
+
+ // use proto::ServerTransaction;
+ use bytes::BytesMut;
+
+ use mime;
+
+ #[test]
+ fn test_link() {
+ let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
+ .push_rel(RelationType::Previous)
+ .push_rev(RelationType::Next)
+ .set_title("previous chapter");
+
+ let link_header = b"<http://example.com/TheBook/chapter2>; \
+ rel=\"previous\"; rev=next; title=\"previous chapter\"";
+
+ let expected_link = Link::new(vec![link_value]);
+
+ let link = Header::parse_header(&vec![link_header.to_vec()].into());
+ assert_eq!(link.ok(), Some(expected_link));
+ }
+
+ #[test]
+ fn test_link_multiple_values() {
+ let first_link = LinkValue::new("/TheBook/chapter2")
+ .push_rel(RelationType::Previous)
+ .set_title_star("UTF-8'de'letztes%20Kapitel");
+
+ let second_link = LinkValue::new("/TheBook/chapter4")
+ .push_rel(RelationType::Next)
+ .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
+
+ let link_header = b"</TheBook/chapter2>; \
+ rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
+ </TheBook/chapter4>; \
+ rel=\"next\"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel";
+
+ let expected_link = Link::new(vec![first_link, second_link]);
+
+ let link = Header::parse_header(&vec![link_header.to_vec()].into());
+ assert_eq!(link.ok(), Some(expected_link));
+ }
+
+ #[test]
+ fn test_link_all_attributes() {
+ let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
+ .push_rel(RelationType::Previous)
+ .set_anchor("../anchor/example/")
+ .push_rev(RelationType::Next)
+ .push_href_lang("de".parse().unwrap())
+ .push_media_desc(MediaDesc::Screen)
+ .set_title("previous chapter")
+ .set_title_star("title* unparsed")
+ .set_media_type(mime::TEXT_PLAIN);
+
+ let link_header = b"<http://example.com/TheBook/chapter2>; \
+ rel=\"previous\"; anchor=\"../anchor/example/\"; \
+ rev=\"next\"; hreflang=de; media=\"screen\"; \
+ title=\"previous chapter\"; title*=title* unparsed; \
+ type=\"text/plain\"";
+
+ let expected_link = Link::new(vec![link_value]);
+
+ let link = Header::parse_header(&vec![link_header.to_vec()].into());
+ assert_eq!(link.ok(), Some(expected_link));
+ }
+
+ // TODO
+ // #[test]
+ // fn test_link_multiple_link_headers() {
+ // let first_link = LinkValue::new("/TheBook/chapter2")
+ // .push_rel(RelationType::Previous)
+ // .set_title_star("UTF-8'de'letztes%20Kapitel");
+
+ // let second_link = LinkValue::new("/TheBook/chapter4")
+ // .push_rel(RelationType::Next)
+ // .set_title_star("UTF-8'de'n%c3%a4chstes%20Kapitel");
+
+ // let third_link = LinkValue::new("http://example.com/TheBook/chapter2")
+ // .push_rel(RelationType::Previous)
+ // .push_rev(RelationType::Next)
+ // .set_title("previous chapter");
+
+ // let expected_link = Link::new(vec![first_link, second_link, third_link]);
+
+ // let mut raw = BytesMut::from(b"GET /super_short_uri/and_whatever HTTP/1.1\r\nHost: \
+ // hyper.rs\r\nAccept: a lot of things\r\nAccept-Charset: \
+ // utf8\r\nAccept-Encoding: *\r\nLink: </TheBook/chapter2>; \
+ // rel=\"previous\"; title*=UTF-8'de'letztes%20Kapitel, \
+ // </TheBook/chapter4>; rel=\"next\"; title*=\
+ // UTF-8'de'n%c3%a4chstes%20Kapitel\r\n\
+ // Access-Control-Allow-Credentials: None\r\nLink: \
+ // <http://example.com/TheBook/chapter2>; \
+ // rel=\"previous\"; rev=next; title=\"previous chapter\"\
+ // \r\n\r\n".to_vec());
+
+ // let (mut res, _) = ServerTransaction::parse(&mut raw).unwrap().unwrap();
+
+ // let link = res.headers.remove::<Link>().unwrap();
+
+ // assert_eq!(link, expected_link);
+ // }
+
+ #[test]
+ fn test_link_display() {
+ let link_value = LinkValue::new("http://example.com/TheBook/chapter2")
+ .push_rel(RelationType::Previous)
+ .set_anchor("/anchor/example/")
+ .push_rev(RelationType::Next)
+ .push_href_lang("de".parse().unwrap())
+ .push_media_desc(MediaDesc::Screen)
+ .set_title("previous chapter")
+ .set_title_star("title* unparsed")
+ .set_media_type(mime::TEXT_PLAIN);
+
+ let link = Link::new(vec![link_value]);
+
+ let mut link_header = String::new();
+ write!(&mut link_header, "{}", link).unwrap();
+
+ let expected_link_header = "<http://example.com/TheBook/chapter2>; \
+ rel=\"previous\"; anchor=\"/anchor/example/\"; \
+ rev=\"next\"; hreflang=de; media=\"screen\"; \
+ title=\"previous chapter\"; title*=title* unparsed; \
+ type=\"text/plain\"";
+
+ assert_eq!(link_header, expected_link_header);
+ }
+
+ #[test]
+ fn test_link_parsing_errors() {
+ let link_a = b"http://example.com/TheBook/chapter2; \
+ rel=\"previous\"; rev=next; title=\"previous chapter\"";
+
+ let mut err: Result<Link, _> = Header::parse_header(&vec![link_a.to_vec()].into());
+ assert_eq!(err.is_err(), true);
+
+ let link_b = b"<http://example.com/TheBook/chapter2>; \
+ =\"previous\"; rev=next; title=\"previous chapter\"";
+
+ err = Header::parse_header(&vec![link_b.to_vec()].into());
+ assert_eq!(err.is_err(), true);
+
+ let link_c = b"<http://example.com/TheBook/chapter2>; \
+ rel=; rev=next; title=\"previous chapter\"";
+
+ err = Header::parse_header(&vec![link_c.to_vec()].into());
+ assert_eq!(err.is_err(), true);
+
+ let link_d = b"<http://example.com/TheBook/chapter2>; \
+ rel=\"previous\"; rev=next; title=";
+
+ err = Header::parse_header(&vec![link_d.to_vec()].into());
+ assert_eq!(err.is_err(), true);
+
+ let link_e = b"<http://example.com/TheBook/chapter2>; \
+ rel=\"previous\"; rev=next; attr=unknown";
+
+ err = Header::parse_header(&vec![link_e.to_vec()].into());
+ assert_eq!(err.is_err(), true);
+ }
+
+ #[test]
+ fn test_link_split_ascii_unquoted_iterator() {
+ let string = "some, text; \"and, more; in quotes\", or not";
+ let mut string_split = SplitAsciiUnquoted::new(string, ";,");
+
+ assert_eq!(Some("some"), string_split.next());
+ assert_eq!(Some(" text"), string_split.next());
+ assert_eq!(Some(" \"and, more; in quotes\""), string_split.next());
+ assert_eq!(Some(" or not"), string_split.next());
+ assert_eq!(None, string_split.next());
+ }
+
+ #[test]
+ fn test_link_fmt_delimited() {
+ struct TestFormatterStruct<'a> { v: Vec<&'a str> };
+
+ impl<'a> fmt::Display for TestFormatterStruct<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt_delimited(f, self.v.as_slice(), ", ", (">>", "<<"))
+ }
+ }
+
+ let test_formatter = TestFormatterStruct { v: vec!["first", "second"] };
+
+ let mut string = String::new();
+ write!(&mut string, "{}", test_formatter).unwrap();
+
+ let expected_string = ">>first, second<<";
+
+ assert_eq!(string, expected_string);
+ }
+
+ #[test]
+ fn test_link_verify_and_trim() {
+ let string = verify_and_trim("> some string <", (b'>', b'<'));
+ assert_eq!(string.ok(), Some("some string"));
+
+ let err = verify_and_trim(" > some string <", (b'>', b'<'));
+ assert_eq!(err.is_err(), true);
+ }
+}
+
+bench_header!(bench_link, Link, { vec![b"<http://example.com/TheBook/chapter2>; rel=\"previous\"; rev=next; title=\"previous chapter\"; type=\"text/html\"; media=\"screen, tty\"".to_vec()] });
diff --git a/third_party/rust/headers/src/disabled/prefer.rs b/third_party/rust/headers/src/disabled/prefer.rs
new file mode 100644
index 0000000000..a6e4dc37bc
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/prefer.rs
@@ -0,0 +1,210 @@
+use std::fmt;
+use std::str::FromStr;
+use {Header, Raw};
+use parsing::{from_comma_delimited, fmt_comma_delimited};
+
+/// `Prefer` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
+///
+/// The `Prefer` header field can be used by a client to request that certain
+/// behaviors be employed by a server while processing a request.
+///
+/// # ABNF
+///
+/// ```text
+/// Prefer = "Prefer" ":" 1#preference
+/// preference = token [ BWS "=" BWS word ]
+/// *( OWS ";" [ OWS parameter ] )
+/// parameter = token [ BWS "=" BWS word ]
+/// ```
+///
+/// # Example values
+/// * `respond-async`
+/// * `return=minimal`
+/// * `wait=30`
+///
+/// # Examples
+///
+/// ```
+/// use headers::{Headers, Prefer, Preference};
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// Prefer(vec![Preference::RespondAsync])
+/// );
+/// ```
+///
+/// ```
+/// use headers::{Headers, Prefer, Preference};
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// Prefer(vec![
+/// Preference::RespondAsync,
+/// Preference::ReturnRepresentation,
+/// Preference::Wait(10u32),
+/// Preference::Extension("foo".to_owned(),
+/// "bar".to_owned(),
+/// vec![]),
+/// ])
+/// );
+/// ```
+#[derive(PartialEq, Clone, Debug)]
+pub struct Prefer(pub Vec<Preference>);
+
+__hyper__deref!(Prefer => Vec<Preference>);
+
+impl Header for Prefer {
+ fn header_name() -> &'static str {
+ static NAME: &'static str = "Prefer";
+ NAME
+ }
+
+ fn parse_header(raw: &Raw) -> ::Result<Prefer> {
+ let preferences = try!(from_comma_delimited(raw));
+ if !preferences.is_empty() {
+ Ok(Prefer(preferences))
+ } else {
+ Err(::Error::Header)
+ }
+ }
+
+ fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result {
+ f.fmt_line(self)
+ }
+}
+
+impl fmt::Display for Prefer {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt_comma_delimited(f, &self[..])
+ }
+}
+
+/// Prefer contains a list of these preferences.
+#[derive(PartialEq, Clone, Debug)]
+pub enum Preference {
+ /// "respond-async"
+ RespondAsync,
+ /// "return=representation"
+ ReturnRepresentation,
+ /// "return=minimal"
+ ReturnMinimal,
+ /// "handling=strict"
+ HandlingStrict,
+ /// "handling=lenient"
+ HandlingLenient,
+ /// "wait=delta"
+ Wait(u32),
+
+ /// Extension preferences. Always has a value, if none is specified it is
+ /// just "". A preference can also have a list of parameters.
+ Extension(String, String, Vec<(String, String)>)
+}
+
+impl fmt::Display for Preference {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ use self::Preference::*;
+ fmt::Display::fmt(match *self {
+ RespondAsync => "respond-async",
+ ReturnRepresentation => "return=representation",
+ ReturnMinimal => "return=minimal",
+ HandlingStrict => "handling=strict",
+ HandlingLenient => "handling=lenient",
+
+ Wait(secs) => return write!(f, "wait={}", secs),
+
+ Extension(ref name, ref value, ref params) => {
+ try!(write!(f, "{}", name));
+ if value != "" { try!(write!(f, "={}", value)); }
+ if !params.is_empty() {
+ for &(ref name, ref value) in params {
+ try!(write!(f, "; {}", name));
+ if value != "" { try!(write!(f, "={}", value)); }
+ }
+ }
+ return Ok(());
+ }
+ }, f)
+ }
+}
+
+impl FromStr for Preference {
+ type Err = Option<<u32 as FromStr>::Err>;
+ fn from_str(s: &str) -> Result<Preference, Option<<u32 as FromStr>::Err>> {
+ use self::Preference::*;
+ let mut params = s.split(';').map(|p| {
+ let mut param = p.splitn(2, '=');
+ match (param.next(), param.next()) {
+ (Some(name), Some(value)) => (name.trim(), value.trim().trim_matches('"')),
+ (Some(name), None) => (name.trim(), ""),
+ // This can safely be unreachable because the [`splitn`][1]
+ // function (used above) will always have at least one value.
+ //
+ // [1]: http://doc.rust-lang.org/std/primitive.str.html#method.splitn
+ _ => { unreachable!(); }
+ }
+ });
+ match params.nth(0) {
+ Some(param) => {
+ let rest: Vec<(String, String)> = params.map(|(l, r)| (l.to_owned(), r.to_owned())).collect();
+ match param {
+ ("respond-async", "") => if rest.is_empty() { Ok(RespondAsync) } else { Err(None) },
+ ("return", "representation") => if rest.is_empty() { Ok(ReturnRepresentation) } else { Err(None) },
+ ("return", "minimal") => if rest.is_empty() { Ok(ReturnMinimal) } else { Err(None) },
+ ("handling", "strict") => if rest.is_empty() { Ok(HandlingStrict) } else { Err(None) },
+ ("handling", "lenient") => if rest.is_empty() { Ok(HandlingLenient) } else { Err(None) },
+ ("wait", secs) => if rest.is_empty() { secs.parse().map(Wait).map_err(Some) } else { Err(None) },
+ (left, right) => Ok(Extension(left.to_owned(), right.to_owned(), rest))
+ }
+ },
+ None => Err(None)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use Header;
+ use super::*;
+
+ #[test]
+ fn test_parse_multiple_headers() {
+ let prefer = Header::parse_header(&"respond-async, return=representation".into());
+ assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::RespondAsync,
+ Preference::ReturnRepresentation])))
+ }
+
+ #[test]
+ fn test_parse_argument() {
+ let prefer = Header::parse_header(&"wait=100, handling=lenient, respond-async".into());
+ assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(100),
+ Preference::HandlingLenient,
+ Preference::RespondAsync])))
+ }
+
+ #[test]
+ fn test_parse_quote_form() {
+ let prefer = Header::parse_header(&"wait=\"200\", handling=\"strict\"".into());
+ assert_eq!(prefer.ok(), Some(Prefer(vec![Preference::Wait(200),
+ Preference::HandlingStrict])))
+ }
+
+ #[test]
+ fn test_parse_extension() {
+ let prefer = Header::parse_header(&"foo, bar=baz, baz; foo; bar=baz, bux=\"\"; foo=\"\", buz=\"some parameter\"".into());
+ assert_eq!(prefer.ok(), Some(Prefer(vec![
+ Preference::Extension("foo".to_owned(), "".to_owned(), vec![]),
+ Preference::Extension("bar".to_owned(), "baz".to_owned(), vec![]),
+ Preference::Extension("baz".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned()), ("bar".to_owned(), "baz".to_owned())]),
+ Preference::Extension("bux".to_owned(), "".to_owned(), vec![("foo".to_owned(), "".to_owned())]),
+ Preference::Extension("buz".to_owned(), "some parameter".to_owned(), vec![])])))
+ }
+
+ #[test]
+ fn test_fail_with_args() {
+ let prefer: ::Result<Prefer> = Header::parse_header(&"respond-async; foo=bar".into());
+ assert_eq!(prefer.ok(), None);
+ }
+}
+
+bench_header!(normal,
+ Prefer, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] });
diff --git a/third_party/rust/headers/src/disabled/preference_applied.rs b/third_party/rust/headers/src/disabled/preference_applied.rs
new file mode 100644
index 0000000000..b368bc9bb9
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/preference_applied.rs
@@ -0,0 +1,110 @@
+use std::fmt;
+use {Header, Raw, Preference};
+use parsing::{from_comma_delimited, fmt_comma_delimited};
+
+/// `Preference-Applied` header, defined in [RFC7240](http://tools.ietf.org/html/rfc7240)
+///
+/// The `Preference-Applied` response header may be included within a
+/// response message as an indication as to which `Prefer` header tokens were
+/// honored by the server and applied to the processing of a request.
+///
+/// # ABNF
+///
+/// ```text
+/// Preference-Applied = "Preference-Applied" ":" 1#applied-pref
+/// applied-pref = token [ BWS "=" BWS word ]
+/// ```
+///
+/// # Example values
+///
+/// * `respond-async`
+/// * `return=minimal`
+/// * `wait=30`
+///
+/// # Examples
+///
+/// ```
+/// use headers::{Headers, PreferenceApplied, Preference};
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// PreferenceApplied(vec![Preference::RespondAsync])
+/// );
+/// ```
+///
+/// ```
+/// use headers::{Headers, PreferenceApplied, Preference};
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// PreferenceApplied(vec![
+/// Preference::RespondAsync,
+/// Preference::ReturnRepresentation,
+/// Preference::Wait(10u32),
+/// Preference::Extension("foo".to_owned(),
+/// "bar".to_owned(),
+/// vec![]),
+/// ])
+/// );
+/// ```
+#[derive(PartialEq, Clone, Debug)]
+pub struct PreferenceApplied(pub Vec<Preference>);
+
+__hyper__deref!(PreferenceApplied => Vec<Preference>);
+
+impl Header for PreferenceApplied {
+ fn header_name() -> &'static str {
+ static NAME: &'static str = "Preference-Applied";
+ NAME
+ }
+
+ fn parse_header(raw: &Raw) -> ::Result<PreferenceApplied> {
+ let preferences = try!(from_comma_delimited(raw));
+ if !preferences.is_empty() {
+ Ok(PreferenceApplied(preferences))
+ } else {
+ Err(::Error::Header)
+ }
+ }
+
+ fn fmt_header(&self, f: &mut ::Formatter) -> fmt::Result {
+ f.fmt_line(self)
+ }
+}
+
+impl fmt::Display for PreferenceApplied {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ //TODO: format this without allocating a Vec and cloning contents
+ let preferences: Vec<_> = self.0.iter().map(|pref| match pref {
+ // The spec ignores parameters in `Preferences-Applied`
+ &Preference::Extension(ref name, ref value, _) => Preference::Extension(
+ name.to_owned(),
+ value.to_owned(),
+ vec![]
+ ),
+ preference => preference.clone()
+ }).collect();
+ fmt_comma_delimited(f, &preferences)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use Preference;
+ use super::*;
+
+ #[test]
+ fn test_format_ignore_parameters() {
+ assert_eq!(
+ format!("{}", PreferenceApplied(vec![Preference::Extension(
+ "foo".to_owned(),
+ "bar".to_owned(),
+ vec![("bar".to_owned(), "foo".to_owned()), ("buz".to_owned(), "".to_owned())]
+ )])),
+ "foo=bar".to_owned()
+ );
+ }
+}
+
+bench_header!(normal,
+ PreferenceApplied, { vec![b"respond-async, return=representation".to_vec(), b"wait=100".to_vec()] });
diff --git a/third_party/rust/headers/src/disabled/util/charset.rs b/third_party/rust/headers/src/disabled/util/charset.rs
new file mode 100644
index 0000000000..5a3f462829
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/charset.rs
@@ -0,0 +1,229 @@
+use std::fmt;
+use std::str::FromStr;
+
+/// A Mime charset.
+///
+/// The string representation is normalised to upper case.
+///
+/// See [http://www.iana.org/assignments/character-sets/character-sets.xhtml][url].
+///
+/// [url]: http://www.iana.org/assignments/character-sets/character-sets.xhtml
+#[derive(Clone, PartialEq)]
+pub struct Charset(Charset_);
+
+impl Charset {
+ /// US ASCII
+ pub const US_ASCII: Charset = Charset(Charset_::Us_Ascii);
+
+ /// ISO-8859-1
+ pub const ISO_8859_1: Charset = Charset(Charset_::Iso_8859_1);
+
+ /// ISO-8859-2
+ pub const ISO_8859_2: Charset = Charset(Charset_::Iso_8859_2);
+
+ /// ISO-8859-3
+ pub const ISO_8859_3: Charset = Charset(Charset_::Iso_8859_3);
+
+ /// ISO-8859-4
+ pub const ISO_8859_4: Charset = Charset(Charset_::Iso_8859_4);
+
+ /// ISO-8859-5
+ pub const ISO_8859_5: Charset = Charset(Charset_::Iso_8859_5);
+
+ /// ISO-8859-6
+ pub const ISO_8859_6: Charset = Charset(Charset_::Iso_8859_6);
+
+ /// ISO-8859-7
+ pub const ISO_8859_7: Charset = Charset(Charset_::Iso_8859_7);
+
+ /// ISO-8859-8
+ pub const ISO_8859_8: Charset = Charset(Charset_::Iso_8859_8);
+
+ /// ISO-8859-9
+ pub const ISO_8859_9: Charset = Charset(Charset_::Iso_8859_9);
+
+ /// ISO-8859-10
+ pub const ISO_8859_10: Charset = Charset(Charset_::Iso_8859_10);
+
+ /// Shift_JIS
+ pub const SHIFT_JIS: Charset = Charset(Charset_::Shift_Jis);
+
+ /// EUC-JP
+ pub const EUC_JP: Charset = Charset(Charset_::Euc_Jp);
+
+ /// ISO-2022-KR
+ pub const ISO_2022_KR: Charset = Charset(Charset_::Iso_2022_Kr);
+
+ /// EUC-KR
+ pub const EUC_KR: Charset: Charset(Charset_::Euc_Kr);
+
+ /// ISO-2022-JP
+ pub const ISO_2022_JP: Charset = Charset(Charset_::Iso_2022_Jp);
+
+ /// ISO-2022-JP-2
+ pub const ISO_2022_JP_2: Charset = Charset(Charset_::Iso_2022_Jp_2);
+
+ /// ISO-8859-6-E
+ pub const ISO_8859_6_E: Charset = Charset(Charset_::Iso_8859_6_E);
+
+ /// ISO-8859-6-I
+ pub const ISO_8859_6_I: Charset = Charset(Charset_::Iso_8859_6_I);
+
+ /// ISO-8859-8-E
+ pub const ISO_8859_8_E: Charset = Charset(Charset_::Iso_8859_8_E);
+
+ /// ISO-8859-8-I
+ pub const ISO_8859_8_I: Charset = Charset(Charset_::Iso_8859_8_I);
+
+ /// GB2312
+ pub const GB_2312: Charset = Charset(Charset_::Gb2312);
+
+ /// Big5
+ pub const BIG_5: Charset = Charset(Charset_::Big5);
+
+ /// KOI8-R
+ pub const KOI8_R: Charset = Charset(Charset_::Koi8_R);
+}
+
+#[derive(Clone, Debug, PartialEq)]
+#[allow(non_camel_case_types)]
+enum Charset_ {
+ /// US ASCII
+ Us_Ascii,
+ /// ISO-8859-1
+ Iso_8859_1,
+ /// ISO-8859-2
+ Iso_8859_2,
+ /// ISO-8859-3
+ Iso_8859_3,
+ /// ISO-8859-4
+ Iso_8859_4,
+ /// ISO-8859-5
+ Iso_8859_5,
+ /// ISO-8859-6
+ Iso_8859_6,
+ /// ISO-8859-7
+ Iso_8859_7,
+ /// ISO-8859-8
+ Iso_8859_8,
+ /// ISO-8859-9
+ Iso_8859_9,
+ /// ISO-8859-10
+ Iso_8859_10,
+ /// Shift_JIS
+ Shift_Jis,
+ /// EUC-JP
+ Euc_Jp,
+ /// ISO-2022-KR
+ Iso_2022_Kr,
+ /// EUC-KR
+ Euc_Kr,
+ /// ISO-2022-JP
+ Iso_2022_Jp,
+ /// ISO-2022-JP-2
+ Iso_2022_Jp_2,
+ /// ISO-8859-6-E
+ Iso_8859_6_E,
+ /// ISO-8859-6-I
+ Iso_8859_6_I,
+ /// ISO-8859-8-E
+ Iso_8859_8_E,
+ /// ISO-8859-8-I
+ Iso_8859_8_I,
+ /// GB2312
+ Gb2312,
+ /// Big5
+ Big5,
+ /// KOI8-R
+ Koi8_R,
+
+ _Unknown,
+}
+
+impl Charset {
+ fn name(&self) -> &'static str {
+ match self.0 {
+ Charset_::Us_Ascii => "US-ASCII",
+ Charset_::Iso_8859_1 => "ISO-8859-1",
+ Charset_::Iso_8859_2 => "ISO-8859-2",
+ Charset_::Iso_8859_3 => "ISO-8859-3",
+ Charset_::Iso_8859_4 => "ISO-8859-4",
+ Charset_::Iso_8859_5 => "ISO-8859-5",
+ Charset_::Iso_8859_6 => "ISO-8859-6",
+ Charset_::Iso_8859_7 => "ISO-8859-7",
+ Charset_::Iso_8859_8 => "ISO-8859-8",
+ Charset_::Iso_8859_9 => "ISO-8859-9",
+ Charset_::Iso_8859_10 => "ISO-8859-10",
+ Charset_::Shift_Jis => "Shift-JIS",
+ Charset_::Euc_Jp => "EUC-JP",
+ Charset_::Iso_2022_Kr => "ISO-2022-KR",
+ Charset_::Euc_Kr => "EUC-KR",
+ Charset_::Iso_2022_Jp => "ISO-2022-JP",
+ Charset_::Iso_2022_Jp_2 => "ISO-2022-JP-2",
+ Charset_::Iso_8859_6_E => "ISO-8859-6-E",
+ Charset_::Iso_8859_6_I => "ISO-8859-6-I",
+ Charset_::Iso_8859_8_E => "ISO-8859-8-E",
+ Charset_::Iso_8859_8_I => "ISO-8859-8-I",
+ Charset_::Gb2312 => "GB2312",
+ Charset_::Big5 => "5",
+ Charset_::Koi8_R => "KOI8-R",
+ Charset_::_Unknown => unreachable!("Charset::_Unknown"),
+ }
+ }
+}
+
+impl fmt::Display for Charset {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.name())
+ }
+}
+
+#[derive(Debug)]
+pub struct CharsetFromStrError(());
+
+impl FromStr for Charset {
+ type Err = CharsetFromStrError;
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ Ok(Charset(match s.to_ascii_uppercase().as_ref() {
+ "US-ASCII" => Charset_::Us_Ascii,
+ "ISO-8859-1" => Charset_::Iso_8859_1,
+ "ISO-8859-2" => Charset_::Iso_8859_2,
+ "ISO-8859-3" => Charset_::Iso_8859_3,
+ "ISO-8859-4" => Charset_::Iso_8859_4,
+ "ISO-8859-5" => Charset_::Iso_8859_5,
+ "ISO-8859-6" => Charset_::Iso_8859_6,
+ "ISO-8859-7" => Charset_::Iso_8859_7,
+ "ISO-8859-8" => Charset_::Iso_8859_8,
+ "ISO-8859-9" => Charset_::Iso_8859_9,
+ "ISO-8859-10" => Charset_::Iso_8859_10,
+ "SHIFT-JIS" => Charset_::Shift_Jis,
+ "EUC-JP" => Charset_::Euc_Jp,
+ "ISO-2022-KR" => Charset_::Iso_2022_Kr,
+ "EUC-KR" => Charset_::Euc_Kr,
+ "ISO-2022-JP" => Charset_::Iso_2022_Jp,
+ "ISO-2022-JP-2" => Charset_::Iso_2022_Jp_2,
+ "ISO-8859-6-E" => Charset_::Iso_8859_6_E,
+ "ISO-8859-6-I" => Charset_::Iso_8859_6_I,
+ "ISO-8859-8-E" => Charset_::Iso_8859_8_E,
+ "ISO-8859-8-I" => Charset_::Iso_8859_8_I,
+ "GB2312" => Charset_::Gb2312,
+ "5" => Charset_::Big5,
+ "KOI8-R" => Charset_::Koi8_R,
+ _unknown => return Err(CharsetFromStrError(())),
+ }))
+ }
+}
+
+#[test]
+fn test_parse() {
+ assert_eq!(Charset::US_ASCII,"us-ascii".parse().unwrap());
+ assert_eq!(Charset::US_ASCII,"US-Ascii".parse().unwrap());
+ assert_eq!(Charset::US_ASCII,"US-ASCII".parse().unwrap());
+ assert_eq!(Charset::SHIFT_JIS,"Shift-JIS".parse().unwrap());
+ assert!("abcd".parse(::<Charset>().is_err());
+}
+
+#[test]
+fn test_display() {
+ assert_eq!("US-ASCII", format!("{}", Charset::US_ASCII));
+}
diff --git a/third_party/rust/headers/src/disabled/util/encoding.rs b/third_party/rust/headers/src/disabled/util/encoding.rs
new file mode 100644
index 0000000000..fc972dd3c7
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/encoding.rs
@@ -0,0 +1,57 @@
+use std::fmt;
+use std::str;
+
+pub use self::Encoding::{Chunked, Brotli, Gzip, Deflate, Compress, Identity, EncodingExt, Trailers};
+
+/// A value to represent an encoding used in `Transfer-Encoding`
+/// or `Accept-Encoding` header.
+#[derive(Clone, PartialEq, Debug)]
+pub enum Encoding {
+ /// The `chunked` encoding.
+ Chunked,
+ /// The `br` encoding.
+ Brotli,
+ /// The `gzip` encoding.
+ Gzip,
+ /// The `deflate` encoding.
+ Deflate,
+ /// The `compress` encoding.
+ Compress,
+ /// The `identity` encoding.
+ Identity,
+ /// The `trailers` encoding.
+ Trailers,
+ /// Some other encoding that is less common, can be any String.
+ EncodingExt(String)
+}
+
+impl fmt::Display for Encoding {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(match *self {
+ Chunked => "chunked",
+ Brotli => "br",
+ Gzip => "gzip",
+ Deflate => "deflate",
+ Compress => "compress",
+ Identity => "identity",
+ Trailers => "trailers",
+ EncodingExt(ref s) => s.as_ref()
+ })
+ }
+}
+
+impl str::FromStr for Encoding {
+ type Err = ::Error;
+ fn from_str(s: &str) -> ::Result<Encoding> {
+ match s {
+ "chunked" => Ok(Chunked),
+ "br" => Ok(Brotli),
+ "deflate" => Ok(Deflate),
+ "gzip" => Ok(Gzip),
+ "compress" => Ok(Compress),
+ "identity" => Ok(Identity),
+ "trailers" => Ok(Trailers),
+ _ => Ok(EncodingExt(s.to_owned()))
+ }
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/util/extended_value.rs b/third_party/rust/headers/src/disabled/util/extended_value.rs
new file mode 100644
index 0000000000..0098f6270c
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/extended_value.rs
@@ -0,0 +1,192 @@
+/// An extended header parameter value (i.e., tagged with a character set and optionally,
+/// a language), as defined in [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
+#[derive(Clone, Debug, PartialEq)]
+pub struct ExtendedValue {
+ /// The character set that is used to encode the `value` to a string.
+ pub charset: Charset,
+ /// The human language details of the `value`, if available.
+ pub language_tag: Option<LanguageTag>,
+ /// The parameter value, as expressed in octets.
+ pub value: Vec<u8>,
+}
+
+/// Parses extended header parameter values (`ext-value`), as defined in
+/// [RFC 5987](https://tools.ietf.org/html/rfc5987#section-3.2).
+///
+/// Extended values are denoted by parameter names that end with `*`.
+///
+/// ## ABNF
+///
+/// ```text
+/// ext-value = charset "'" [ language ] "'" value-chars
+/// ; like RFC 2231's <extended-initial-value>
+/// ; (see [RFC2231], Section 7)
+///
+/// charset = "UTF-8" / "ISO-8859-1" / mime-charset
+///
+/// mime-charset = 1*mime-charsetc
+/// mime-charsetc = ALPHA / DIGIT
+/// / "!" / "#" / "$" / "%" / "&"
+/// / "+" / "-" / "^" / "_" / "`"
+/// / "{" / "}" / "~"
+/// ; as <mime-charset> in Section 2.3 of [RFC2978]
+/// ; except that the single quote is not included
+/// ; SHOULD be registered in the IANA charset registry
+///
+/// language = <Language-Tag, defined in [RFC5646], Section 2.1>
+///
+/// value-chars = *( pct-encoded / attr-char )
+///
+/// pct-encoded = "%" HEXDIG HEXDIG
+/// ; see [RFC3986], Section 2.1
+///
+/// attr-char = ALPHA / DIGIT
+/// / "!" / "#" / "$" / "&" / "+" / "-" / "."
+/// / "^" / "_" / "`" / "|" / "~"
+/// ; token except ( "*" / "'" / "%" )
+/// ```
+pub fn parse_extended_value(val: &str) -> ::Result<ExtendedValue> {
+
+ // Break into three pieces separated by the single-quote character
+ let mut parts = val.splitn(3,'\'');
+
+ // Interpret the first piece as a Charset
+ let charset: Charset = match parts.next() {
+ None => return Err(::Error::Header),
+ Some(n) => try!(FromStr::from_str(n)),
+ };
+
+ // Interpret the second piece as a language tag
+ let lang: Option<LanguageTag> = match parts.next() {
+ None => return Err(::Error::Header),
+ Some("") => None,
+ Some(s) => match s.parse() {
+ Ok(lt) => Some(lt),
+ Err(_) => return Err(::Error::Header),
+ }
+ };
+
+ // Interpret the third piece as a sequence of value characters
+ let value: Vec<u8> = match parts.next() {
+ None => return Err(::Error::Header),
+ Some(v) => percent_encoding::percent_decode(v.as_bytes()).collect(),
+ };
+
+ Ok(ExtendedValue {
+ charset: charset,
+ language_tag: lang,
+ value: value,
+ })
+}
+
+
+impl Display for ExtendedValue {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let encoded_value =
+ percent_encoding::percent_encode(&self.value[..], self::percent_encoding_http::HTTP_VALUE);
+ if let Some(ref lang) = self.language_tag {
+ write!(f, "{}'{}'{}", self.charset, lang, encoded_value)
+ } else {
+ write!(f, "{}''{}", self.charset, encoded_value)
+ }
+ }
+}
+
+/// Percent encode a sequence of bytes with a character set defined in
+/// [https://tools.ietf.org/html/rfc5987#section-3.2][url]
+///
+/// [url]: https://tools.ietf.org/html/rfc5987#section-3.2
+pub fn http_percent_encode(f: &mut fmt::Formatter, bytes: &[u8]) -> fmt::Result {
+ let encoded = percent_encoding::percent_encode(bytes, self::percent_encoding_http::HTTP_VALUE);
+ fmt::Display::fmt(&encoded, f)
+}
+
+mod percent_encoding_http {
+ use percent_encoding;
+
+ // internal module because macro is hard-coded to make a public item
+ // but we don't want to public export this item
+ define_encode_set! {
+ // This encode set is used for HTTP header values and is defined at
+ // https://tools.ietf.org/html/rfc5987#section-3.2
+ pub HTTP_VALUE = [percent_encoding::SIMPLE_ENCODE_SET] | {
+ ' ', '"', '%', '\'', '(', ')', '*', ',', '/', ':', ';', '<', '-', '>', '?',
+ '[', '\\', ']', '{', '}'
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use shared::Charset;
+ use super::{ExtendedValue, parse_extended_value};
+ use language_tags::LanguageTag;
+
+ #[test]
+ fn test_parse_extended_value_with_encoding_and_language_tag() {
+ let expected_language_tag = "en".parse::<LanguageTag>().unwrap();
+ // RFC 5987, Section 3.2.2
+ // Extended notation, using the Unicode character U+00A3 (POUND SIGN)
+ let result = parse_extended_value("iso-8859-1'en'%A3%20rates");
+ assert!(result.is_ok());
+ let extended_value = result.unwrap();
+ assert_eq!(Charset::Iso_8859_1, extended_value.charset);
+ assert!(extended_value.language_tag.is_some());
+ assert_eq!(expected_language_tag, extended_value.language_tag.unwrap());
+ assert_eq!(vec![163, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
+ }
+
+ #[test]
+ fn test_parse_extended_value_with_encoding() {
+ // RFC 5987, Section 3.2.2
+ // Extended notation, using the Unicode characters U+00A3 (POUND SIGN)
+ // and U+20AC (EURO SIGN)
+ let result = parse_extended_value("UTF-8''%c2%a3%20and%20%e2%82%ac%20rates");
+ assert!(result.is_ok());
+ let extended_value = result.unwrap();
+ assert_eq!(Charset::Ext("UTF-8".to_string()), extended_value.charset);
+ assert!(extended_value.language_tag.is_none());
+ assert_eq!(vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a', b't', b'e', b's'], extended_value.value);
+ }
+
+ #[test]
+ fn test_parse_extended_value_missing_language_tag_and_encoding() {
+ // From: https://greenbytes.de/tech/tc2231/#attwithfn2231quot2
+ let result = parse_extended_value("foo%20bar.html");
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_parse_extended_value_partially_formatted() {
+ let result = parse_extended_value("UTF-8'missing third part");
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_parse_extended_value_partially_formatted_blank() {
+ let result = parse_extended_value("blank second part'");
+ assert!(result.is_err());
+ }
+
+ #[test]
+ fn test_fmt_extended_value_with_encoding_and_language_tag() {
+ let extended_value = ExtendedValue {
+ charset: Charset::Iso_8859_1,
+ language_tag: Some("en".parse().expect("Could not parse language tag")),
+ value: vec![163, b' ', b'r', b'a', b't', b'e', b's'],
+ };
+ assert_eq!("ISO-8859-1'en'%A3%20rates", format!("{}", extended_value));
+ }
+
+ #[test]
+ fn test_fmt_extended_value_with_encoding() {
+ let extended_value = ExtendedValue {
+ charset: Charset::Ext("UTF-8".to_string()),
+ language_tag: None,
+ value: vec![194, 163, b' ', b'a', b'n', b'd', b' ', 226, 130, 172, b' ', b'r', b'a',
+ b't', b'e', b's'],
+ };
+ assert_eq!("UTF-8''%C2%A3%20and%20%E2%82%AC%20rates",
+ format!("{}", extended_value));
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/util/quality_value.rs b/third_party/rust/headers/src/disabled/util/quality_value.rs
new file mode 100644
index 0000000000..bcc797280d
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/util/quality_value.rs
@@ -0,0 +1,268 @@
+#[allow(unused, deprecated)]
+use std::ascii::AsciiExt;
+use std::cmp;
+use std::default::Default;
+use std::fmt;
+use std::str;
+
+#[cfg(test)]
+use self::internal::IntoQuality;
+
+/// Represents a quality used in quality values.
+///
+/// Can be created with the `q` function.
+///
+/// # Implementation notes
+///
+/// The quality value is defined as a number between 0 and 1 with three decimal places. This means
+/// there are 1001 possible values. Since floating point numbers are not exact and the smallest
+/// floating point data type (`f32`) consumes four bytes, hyper uses an `u16` value to store the
+/// quality internally. For performance reasons you may set quality directly to a value between
+/// 0 and 1000 e.g. `Quality(532)` matches the quality `q=0.532`.
+///
+/// [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1)
+/// gives more information on quality values in HTTP header fields.
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub struct Quality(u16);
+
+impl Default for Quality {
+ fn default() -> Quality {
+ Quality(1000)
+ }
+}
+
+/// Represents an item with a quality value as defined in
+/// [RFC7231](https://tools.ietf.org/html/rfc7231#section-5.3.1).
+#[derive(Clone, PartialEq, Debug)]
+pub struct QualityValue<T> {
+ /// The actual contents of the field.
+ value: T,
+ /// The quality (client or server preference) for the value.
+ quality: Quality,
+}
+
+impl<T> QualityValue<T> {
+ /// Creates a new `QualityValue` from an item and a quality.
+ pub fn new(value: T, quality: Quality) -> QualityValue<T> {
+ QualityValue {
+ value,
+ quality,
+ }
+ }
+
+ /*
+ /// Convenience function to set a `Quality` from a float or integer.
+ ///
+ /// Implemented for `u16` and `f32`.
+ ///
+ /// # Panic
+ ///
+ /// Panics if value is out of range.
+ pub fn with_q<Q: IntoQuality>(mut self, q: Q) -> QualityValue<T> {
+ self.quality = q.into_quality();
+ self
+ }
+ */
+}
+
+impl<T> From<T> for QualityValue<T> {
+ fn from(value: T) -> QualityValue<T> {
+ QualityValue {
+ value,
+ quality: Quality::default(),
+ }
+ }
+}
+
+impl<T: PartialEq> cmp::PartialOrd for QualityValue<T> {
+ fn partial_cmp(&self, other: &QualityValue<T>) -> Option<cmp::Ordering> {
+ self.quality.partial_cmp(&other.quality)
+ }
+}
+
+impl<T: fmt::Display> fmt::Display for QualityValue<T> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.value, f)?;
+ match self.quality.0 {
+ 1000 => Ok(()),
+ 0 => f.write_str("; q=0"),
+ x => write!(f, "; q=0.{}", format!("{:03}", x).trim_right_matches('0'))
+ }
+ }
+}
+
+impl<T: str::FromStr> str::FromStr for QualityValue<T> {
+ type Err = ::Error;
+ fn from_str(s: &str) -> ::Result<QualityValue<T>> {
+ // Set defaults used if parsing fails.
+ let mut raw_item = s;
+ let mut quality = 1f32;
+
+ let parts: Vec<&str> = s.rsplitn(2, ';').map(|x| x.trim()).collect();
+ if parts.len() == 2 {
+ if parts[0].len() < 2 {
+ return Err(::Error::invalid());
+ }
+ if parts[0].starts_with("q=") || parts[0].starts_with("Q=") {
+ let q_part = &parts[0][2..parts[0].len()];
+ if q_part.len() > 5 {
+ return Err(::Error::invalid());
+ }
+ match q_part.parse::<f32>() {
+ Ok(q_value) => {
+ if 0f32 <= q_value && q_value <= 1f32 {
+ quality = q_value;
+ raw_item = parts[1];
+ } else {
+ return Err(::Error::invalid());
+ }
+ },
+ Err(_) => {
+ return Err(::Error::invalid())
+ },
+ }
+ }
+ }
+ match raw_item.parse::<T>() {
+ // we already checked above that the quality is within range
+ Ok(item) => Ok(QualityValue::new(item, from_f32(quality))),
+ Err(_) => {
+ Err(::Error::invalid())
+ },
+ }
+ }
+}
+
+#[inline]
+fn from_f32(f: f32) -> Quality {
+ // this function is only used internally. A check that `f` is within range
+ // should be done before calling this method. Just in case, this
+ // debug_assert should catch if we were forgetful
+ debug_assert!(f >= 0f32 && f <= 1f32, "q value must be between 0.0 and 1.0");
+ Quality((f * 1000f32) as u16)
+}
+
+#[cfg(test)]
+fn q<T: IntoQuality>(val: T) -> Quality {
+ val.into_quality()
+}
+
+mod internal {
+ use super::Quality;
+
+ // TryFrom is probably better, but it's not stable. For now, we want to
+ // keep the functionality of the `q` function, while allowing it to be
+ // generic over `f32` and `u16`.
+ //
+ // `q` would panic before, so keep that behavior. `TryFrom` can be
+ // introduced later for a non-panicking conversion.
+
+ pub trait IntoQuality: Sealed + Sized {
+ fn into_quality(self) -> Quality;
+ }
+
+ impl IntoQuality for f32 {
+ fn into_quality(self) -> Quality {
+ assert!(self >= 0f32 && self <= 1f32, "float must be between 0.0 and 1.0");
+ super::from_f32(self)
+ }
+ }
+
+ impl IntoQuality for u16 {
+ fn into_quality(self) -> Quality {
+ assert!(self <= 1000, "u16 must be between 0 and 1000");
+ Quality(self)
+ }
+ }
+
+
+ pub trait Sealed {}
+ impl Sealed for u16 {}
+ impl Sealed for f32 {}
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_quality_item_fmt_q_1() {
+ let x = QualityValue::from("foo");
+ assert_eq!(format!("{}", x), "foo");
+ }
+ #[test]
+ fn test_quality_item_fmt_q_0001() {
+ let x = QualityValue::new("foo", Quality(1));
+ assert_eq!(format!("{}", x), "foo; q=0.001");
+ }
+ #[test]
+ fn test_quality_item_fmt_q_05() {
+ let x = QualityValue::new("foo", Quality(500));
+ assert_eq!(format!("{}", x), "foo; q=0.5");
+ }
+
+ #[test]
+ fn test_quality_item_fmt_q_0() {
+ let x = QualityValue::new("foo", Quality(0));
+ assert_eq!(x.to_string(), "foo; q=0");
+ }
+
+ #[test]
+ fn test_quality_item_from_str1() {
+ let x: QualityValue<String> = "chunked".parse().unwrap();
+ assert_eq!(x, QualityValue { value: "chunked".to_owned(), quality: Quality(1000), });
+ }
+ #[test]
+ fn test_quality_item_from_str2() {
+ let x: QualityValue<String> = "chunked; q=1".parse().unwrap();
+ assert_eq!(x, QualityValue { value: "chunked".to_owned(), quality: Quality(1000), });
+ }
+ #[test]
+ fn test_quality_item_from_str3() {
+ let x: QualityValue<String> = "gzip; q=0.5".parse().unwrap();
+ assert_eq!(x, QualityValue { value: "gzip".to_owned(), quality: Quality(500), });
+ }
+ #[test]
+ fn test_quality_item_from_str4() {
+ let x: QualityValue<String> = "gzip; q=0.273".parse().unwrap();
+ assert_eq!(x, QualityValue { value: "gzip".to_owned(), quality: Quality(273), });
+ }
+ #[test]
+ fn test_quality_item_from_str5() {
+ assert!("gzip; q=0.2739999".parse::<QualityValue<String>>().is_err());
+ }
+
+ #[test]
+ fn test_quality_item_from_str6() {
+ assert!("gzip; q=2".parse::<QualityValue<String>>().is_err());
+ }
+ #[test]
+ fn test_quality_item_ordering() {
+ let x: QualityValue<String> = "gzip; q=0.5".parse().unwrap();
+ let y: QualityValue<String> = "gzip; q=0.273".parse().unwrap();
+ assert!(x > y)
+ }
+
+ #[test]
+ fn test_quality() {
+ assert_eq!(q(0.5), Quality(500));
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_quality_invalid() {
+ q(-1.0);
+ }
+
+ #[test]
+ #[should_panic]
+ fn test_quality_invalid2() {
+ q(2.0);
+ }
+
+ #[test]
+ fn test_fuzzing_bugs() {
+ assert!("99999;".parse::<QualityValue<String>>().is_err());
+ assert!("\x0d;;;=\u{d6aa}==".parse::<QualityValue<String>>().is_ok())
+ }
+}
diff --git a/third_party/rust/headers/src/disabled/warning.rs b/third_party/rust/headers/src/disabled/warning.rs
new file mode 100644
index 0000000000..9227201540
--- /dev/null
+++ b/third_party/rust/headers/src/disabled/warning.rs
@@ -0,0 +1,182 @@
+use std::fmt;
+use std::str::{FromStr};
+use {Header, HttpDate, Raw};
+use 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
+///
+/// ```text
+/// 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 headers::{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 headers::{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()
+/// }
+/// );
+/// ```
+///
+/// ```
+/// use std::time::SystemTime;
+/// use headers::{Headers, Warning};
+///
+/// let mut headers = Headers::new();
+/// headers.set(
+/// Warning{
+/// code: 199,
+/// agent: "api.hyper.rs".to_owned(),
+/// text: "Deprecated".to_owned(),
+/// date: Some(SystemTime::now().into())
+/// }
+/// );
+/// ```
+#[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 ::Formatter) -> fmt::Result {
+ f.fmt_line(self)
+ }
+}
+
+impl fmt::Display for Warning {
+ fn fmt(&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, 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()
+ }));
+ }
+}