feat(mime): upgrade to mime v0.3

The new mime crate has several benefits:

- Faster formatting
- Easier to use. Most common mime types are now just constants, like
  `mime::TEXT_PLAIN`.
- Proper suffix support.
- Extensible without breaking backwards compatiblity. This means we can
  always add new constants, but before we couldn't add new variants to the
  enums.
- It's now impossible for a `Mime` to contain invalid tokens. Before,
  with the `Ext(String)` variants, it was possible to create an illegal
  mime.

Closes #738

BREAKING CHANGE: Most uses of `mime` will likely break. There is no more
  `mime!` macro, nor a `Mime` constructor, nor `TopLevel` and `SubLevel`
  enums.

  Instead, in most cases, a constant exists that can now be used.

  For less common mime types, they can be created by parsing a string.
This commit is contained in:
Sean McArthur
2017-06-08 13:00:12 -07:00
parent e2ed6f5868
commit f273224f21
7 changed files with 59 additions and 94 deletions

View File

@@ -26,7 +26,7 @@ futures-cpupool = "0.1"
httparse = "1.0" httparse = "1.0"
language-tags = "0.2" language-tags = "0.2"
log = "0.3" log = "0.3"
mime = "0.2" mime = "0.3"
time = "0.1" time = "0.1"
tokio-core = "0.1.6" tokio-core = "0.1.6"
tokio-proto = "0.1" tokio-proto = "0.1"

View File

@@ -1,4 +1,4 @@
use mime::Mime; use mime::{self, Mime};
use header::{QualityItem, qitem}; use header::{QualityItem, qitem};
@@ -24,94 +24,89 @@ header! {
/// ``` /// ```
/// ///
/// # Example values /// # Example values
/// * `audio/*; q=0.2, audio/basic` (`*` value won't parse correctly) /// * `audio/*; q=0.2, audio/basic`
/// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c` /// * `text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c`
/// ///
/// # Examples /// # Examples
/// ``` /// ```
/// use hyper::header::{Headers, Accept, qitem}; /// use hyper::header::{Headers, Accept, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel}; /// use hyper::mime;
/// ///
/// let mut headers = Headers::new(); /// let mut headers = Headers::new();
/// ///
/// headers.set( /// headers.set(
/// Accept(vec![ /// Accept(vec![
/// qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), /// qitem(mime::TEXT_HTML),
/// ]) /// ])
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, Accept, qitem}; /// use hyper::header::{Headers, Accept, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; /// use hyper::mime;
/// ///
/// let mut headers = Headers::new(); /// let mut headers = Headers::new();
/// headers.set( /// headers.set(
/// Accept(vec![ /// Accept(vec![
/// qitem(Mime(TopLevel::Application, SubLevel::Json, /// qitem(mime::APPLICATION_JSON),
/// vec![(Attr::Charset, Value::Utf8)])),
/// ]) /// ])
/// ); /// );
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, Accept, QualityItem, q, qitem}; /// use hyper::header::{Headers, Accept, QualityItem, q, qitem};
/// use hyper::mime::{Mime, TopLevel, SubLevel}; /// use hyper::mime;
/// ///
/// let mut headers = Headers::new(); /// let mut headers = Headers::new();
/// ///
/// headers.set( /// headers.set(
/// Accept(vec![ /// Accept(vec![
/// qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), /// qitem(mime::TEXT_HTML),
/// qitem(Mime(TopLevel::Application, /// qitem("application/xhtml+xml".parse().unwrap()),
/// SubLevel::Ext("xhtml+xml".to_owned()), vec![])), /// QualityItem::new(
/// QualityItem::new(Mime(TopLevel::Application, SubLevel::Xml, vec![]), /// mime::TEXT_XML,
/// q(900)), /// q(900)
/// qitem(Mime(TopLevel::Image, /// ),
/// SubLevel::Ext("webp".to_owned()), vec![])), /// qitem("image/webp".parse().unwrap()),
/// QualityItem::new(Mime(TopLevel::Star, SubLevel::Star, vec![]), /// QualityItem::new(
/// q(800)) /// mime::STAR_STAR,
/// q(800)
/// ),
/// ]) /// ])
/// ); /// );
/// ``` /// ```
///
/// # Notes
/// * Using always Mime types to represent `media-range` differs from the ABNF.
/// * **FIXME**: `accept-ext` is not supported.
(Accept, "Accept") => (QualityItem<Mime>)+ (Accept, "Accept") => (QualityItem<Mime>)+
test_accept { test_accept {
// Tests from the RFC // Tests from the RFC
// FIXME: Test fails, first value containing a "*" fails to parse test_header!(
// test_header!( test1,
// test1, vec![b"audio/*; q=0.2, audio/basic"],
// vec![b"audio/*; q=0.2, audio/basic"], Some(HeaderField(vec![
// Some(HeaderField(vec![ QualityItem::new("audio/*".parse().unwrap(), q(200)),
// QualityItem::new(Mime(TopLevel::Audio, SubLevel::Star, vec![]), q(200)), qitem("audio/basic".parse().unwrap()),
// qitem(Mime(TopLevel::Audio, SubLevel::Ext("basic".to_owned()), vec![])), ])));
// ])));
test_header!( test_header!(
test2, test2,
vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"], vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"],
Some(HeaderField(vec![ Some(HeaderField(vec![
QualityItem::new(Mime(TopLevel::Text, SubLevel::Plain, vec![]), q(500)), QualityItem::new(TEXT_PLAIN, q(500)),
qitem(Mime(TopLevel::Text, SubLevel::Html, vec![])), qitem(TEXT_HTML),
QualityItem::new( QualityItem::new(
Mime(TopLevel::Text, SubLevel::Ext("x-dvi".to_owned()), vec![]), "text/x-dvi".parse().unwrap(),
q(800)), q(800)),
qitem(Mime(TopLevel::Text, SubLevel::Ext("x-c".to_owned()), vec![])), qitem("text/x-c".parse().unwrap()),
]))); ])));
// Custom tests // Custom tests
test_header!( test_header!(
test3, test3,
vec![b"text/plain; charset=utf-8"], vec![b"text/plain; charset=utf-8"],
Some(Accept(vec![ Some(Accept(vec![
qitem(Mime(TopLevel::Text, SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)])), qitem(TEXT_PLAIN_UTF_8),
]))); ])));
test_header!( test_header!(
test4, test4,
vec![b"text/plain; charset=utf-8; q=0.5"], vec![b"text/plain; charset=utf-8; q=0.5"],
Some(Accept(vec![ Some(Accept(vec![
QualityItem::new(Mime(TopLevel::Text, QualityItem::new(TEXT_PLAIN_UTF_8,
SubLevel::Plain, vec![(Attr::Charset, Value::Utf8)]),
q(500)), q(500)),
]))); ])));
@@ -127,22 +122,22 @@ header! {
impl Accept { impl Accept {
/// A constructor to easily create `Accept: */*`. /// A constructor to easily create `Accept: */*`.
pub fn star() -> Accept { pub fn star() -> Accept {
Accept(vec![qitem(mime!(Star/Star))]) Accept(vec![qitem(mime::STAR_STAR)])
} }
/// A constructor to easily create `Accept: application/json`. /// A constructor to easily create `Accept: application/json`.
pub fn json() -> Accept { pub fn json() -> Accept {
Accept(vec![qitem(mime!(Application/Json))]) Accept(vec![qitem(mime::APPLICATION_JSON)])
} }
/// A constructor to easily create `Accept: text/*`. /// A constructor to easily create `Accept: text/*`.
pub fn text() -> Accept { pub fn text() -> Accept {
Accept(vec![qitem(mime!(Text/Star))]) Accept(vec![qitem(mime::TEXT_STAR)])
} }
/// A constructor to easily create `Accept: image/*`. /// A constructor to easily create `Accept: image/*`.
pub fn image() -> Accept { pub fn image() -> Accept {
Accept(vec![qitem(mime!(Image/Star))]) Accept(vec![qitem(mime::IMAGE_STAR)])
} }
} }

View File

@@ -1,4 +1,4 @@
use mime::Mime; use mime::{self, Mime};
header! { header! {
/// `Content-Type` header, defined in /// `Content-Type` header, defined in
@@ -22,7 +22,8 @@ header! {
/// ``` /// ```
/// ///
/// # Example values /// # Example values
/// * `text/html; charset=ISO-8859-4` /// * `text/html; charset=utf-8`
/// * `application/json
/// ///
/// # Examples /// # Examples
/// ``` /// ```
@@ -36,13 +37,12 @@ header! {
/// ``` /// ```
/// ``` /// ```
/// use hyper::header::{Headers, ContentType}; /// use hyper::header::{Headers, ContentType};
/// use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value}; /// use hyper::mime;
/// ///
/// let mut headers = Headers::new(); /// let mut headers = Headers::new();
/// ///
/// headers.set( /// headers.set(
/// ContentType(Mime(TopLevel::Text, SubLevel::Html, /// ContentType(mime::TEXT_HTML)
/// vec![(Attr::Charset, Value::Utf8)]))
/// ); /// );
/// ``` /// ```
(ContentType, "Content-Type") => [Mime] (ContentType, "Content-Type") => [Mime]
@@ -50,13 +50,8 @@ header! {
test_content_type { test_content_type {
test_header!( test_header!(
test1, test1,
// FIXME: Should be b"text/html; charset=ISO-8859-4" but mime crate lowercases vec![b"text/html"],
// the whole value so parsing and formatting the value gives a different result Some(HeaderField(TEXT_HTML)));
vec![b"text/html; charset=iso-8859-4"],
Some(HeaderField(Mime(
TopLevel::Text,
SubLevel::Html,
vec![(Attr::Charset, Value::Ext("iso-8859-4".to_owned()))]))));
} }
} }
@@ -64,45 +59,45 @@ impl ContentType {
/// A constructor to easily create a `Content-Type: application/json` header. /// A constructor to easily create a `Content-Type: application/json` header.
#[inline] #[inline]
pub fn json() -> ContentType { pub fn json() -> ContentType {
ContentType(mime!(Application/Json)) ContentType(mime::APPLICATION_JSON)
} }
/// A constructor to easily create a `Content-Type: text/plain; charset=utf-8` header. /// A constructor to easily create a `Content-Type: text/plain; charset=utf-8` header.
#[inline] #[inline]
pub fn plaintext() -> ContentType { pub fn plaintext() -> ContentType {
ContentType(mime!(Text/Plain; Charset=Utf8)) ContentType(mime::TEXT_PLAIN_UTF_8)
} }
/// A constructor to easily create a `Content-Type: text/html; charset=utf-8` header. /// A constructor to easily create a `Content-Type: text/html; charset=utf-8` header.
#[inline] #[inline]
pub fn html() -> ContentType { pub fn html() -> ContentType {
ContentType(mime!(Text/Html; Charset=Utf8)) ContentType(mime::TEXT_HTML)
} }
/// A constructor to easily create a `Content-Type: application/www-form-url-encoded` header. /// A constructor to easily create a `Content-Type: application/www-form-url-encoded` header.
#[inline] #[inline]
pub fn form_url_encoded() -> ContentType { pub fn form_url_encoded() -> ContentType {
ContentType(mime!(Application/WwwFormUrlEncoded)) ContentType(mime::APPLICATION_WWW_FORM_URLENCODED)
} }
/// A constructor to easily create a `Content-Type: image/jpeg` header. /// A constructor to easily create a `Content-Type: image/jpeg` header.
#[inline] #[inline]
pub fn jpeg() -> ContentType { pub fn jpeg() -> ContentType {
ContentType(mime!(Image/Jpeg)) ContentType(mime::IMAGE_JPEG)
} }
/// A constructor to easily create a `Content-Type: image/png` header. /// A constructor to easily create a `Content-Type: image/png` header.
#[inline] #[inline]
pub fn png() -> ContentType { pub fn png() -> ContentType {
ContentType(mime!(Image/Png)) ContentType(mime::IMAGE_PNG)
} }
/// A constructor to easily create a `Content-Type: application/octet-stream` header. /// A constructor to easily create a `Content-Type: application/octet-stream` header.
#[inline] #[inline]
pub fn octet_stream() -> ContentType { pub fn octet_stream() -> ContentType {
ContentType(mime!(Application/OctetStream)) ContentType(mime::APPLICATION_OCTET_STREAM)
} }
} }
impl Eq for ContentType {} impl Eq for ContentType {}
bench_header!(bench, ContentType, { vec![b"application/json; charset=utf-8".to_vec()] }); bench_header!(bench, ContentType, { vec![b"application/json".to_vec()] });

View File

@@ -905,9 +905,7 @@ mod tests {
use http::{ServerTransaction, Http1Transaction}; use http::{ServerTransaction, Http1Transaction};
use bytes::BytesMut; use bytes::BytesMut;
use mime::Mime; use mime;
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
#[test] #[test]
fn test_link() { fn test_link() {
@@ -956,7 +954,7 @@ mod tests {
.push_media_desc(MediaDesc::Screen) .push_media_desc(MediaDesc::Screen)
.set_title("previous chapter") .set_title("previous chapter")
.set_title_star("title* unparsed") .set_title_star("title* unparsed")
.set_media_type(Mime(Text, Plain, vec![])); .set_media_type(mime::TEXT_PLAIN);
let link_header = b"<http://example.com/TheBook/chapter2>; \ let link_header = b"<http://example.com/TheBook/chapter2>; \
rel=\"previous\"; anchor=\"../anchor/example/\"; \ rel=\"previous\"; anchor=\"../anchor/example/\"; \
@@ -1015,7 +1013,7 @@ mod tests {
.push_media_desc(MediaDesc::Screen) .push_media_desc(MediaDesc::Screen)
.set_title("previous chapter") .set_title("previous chapter")
.set_title_star("title* unparsed") .set_title_star("title* unparsed")
.set_media_type(Mime(Text, Plain, vec![])); .set_media_type(mime::TEXT_PLAIN);
let link = Link::new(vec![link_value]); let link = Link::new(vec![link_value]);

View File

@@ -3,7 +3,7 @@
//! ## Mime //! ## Mime
//! //!
//! Several header fields use MIME values for their contents. Keeping with the //! Several header fields use MIME values for their contents. Keeping with the
//! strongly-typed theme, the [mime](http://seanmonstar.github.io/mime.rs) crate //! strongly-typed theme, the [mime](https://docs.rs/mime) crate
//! is used, such as `ContentType(pub Mime)`. //! is used, such as `ContentType(pub Mime)`.
pub use self::accept::Accept; pub use self::accept::Accept;

View File

@@ -696,11 +696,7 @@ impl PartialEq<HeaderName> for str {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::fmt; use std::fmt;
use mime::Mime; use super::{Headers, Header, Raw, ContentLength, ContentType, Host, SetCookie};
use mime::TopLevel::Text;
use mime::SubLevel::Plain;
use super::{Headers, Header, Raw, ContentLength, ContentType,
Accept, Host, qitem, SetCookie};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use test::Bencher; use test::Bencher;
@@ -723,25 +719,6 @@ mod tests {
assert_eq!(headers.get(), Some(&ContentLength(10))); assert_eq!(headers.get(), Some(&ContentLength(10)));
} }
#[test]
fn test_content_type() {
let content_type = Header::parse_header(&b"text/plain".as_ref().into());
assert_eq!(content_type.ok(), Some(ContentType(Mime(Text, Plain, vec![]))));
}
#[test]
fn test_accept() {
let text_plain = qitem(Mime(Text, Plain, vec![]));
let application_vendor = "application/vnd.github.v3.full+json; q=0.5".parse().unwrap();
let accept = Header::parse_header(&b"text/plain".as_ref().into());
assert_eq!(accept.ok(), Some(Accept(vec![text_plain.clone()])));
let bytevec = b"application/vnd.github.v3.full+json; q=0.5, text/plain".as_ref().into();
let accept = Header::parse_header(&bytevec);
assert_eq!(accept.ok(), Some(Accept(vec![application_vendor, text_plain])));
}
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
struct CrazyLength(Option<bool>, usize); struct CrazyLength(Option<bool>, usize);
@@ -882,7 +859,7 @@ mod tests {
let mut headers = Headers::new(); let mut headers = Headers::new();
headers.set(ContentLength(10)); headers.set(ContentLength(10));
assert_eq!(headers.len(), 1); assert_eq!(headers.len(), 1);
headers.set(ContentType(Mime(Text, Plain, vec![]))); headers.set(ContentType::json());
assert_eq!(headers.len(), 2); assert_eq!(headers.len(), 2);
// Redundant, should not increase count. // Redundant, should not increase count.
headers.set(ContentLength(20)); headers.set(ContentLength(20));
@@ -893,7 +870,7 @@ mod tests {
fn test_clear() { fn test_clear() {
let mut headers = Headers::new(); let mut headers = Headers::new();
headers.set(ContentLength(10)); headers.set(ContentLength(10));
headers.set(ContentType(Mime(Text, Plain, vec![]))); headers.set(ContentType::json());
assert_eq!(headers.len(), 2); assert_eq!(headers.len(), 2);
headers.clear(); headers.clear();
assert_eq!(headers.len(), 0); assert_eq!(headers.len(), 0);

View File

@@ -20,7 +20,7 @@ extern crate futures_cpupool;
extern crate httparse; extern crate httparse;
extern crate language_tags; extern crate language_tags;
#[macro_use] extern crate log; #[macro_use] extern crate log;
#[macro_use] pub extern crate mime; pub extern crate mime;
extern crate base64; extern crate base64;
extern crate time; extern crate time;
extern crate tokio_core as tokio; extern crate tokio_core as tokio;