From d591131a7ce45ca12b2e05992ed4b564c054b461 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 2 Jul 2019 15:01:06 +0200 Subject: [PATCH 1/8] Implement HttpTryFrom> for HeaderMap This allows an easy conversion from HashMap to HeaderMap. Doing this conversion used to be unnecessarily verbose because of the required mapping and error type conversions. The implementation is generic: impl HttpTryFrom for HeaderMap where COLLECTION: IntoIterator, K: AsRef, V: AsRef so it also works for HashMap and for vectors. Fixes #325 --- src/header/map.rs | 50 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index 66f987cc..a8c5ab0f 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,5 +1,7 @@ use super::HeaderValue; use super::name::{HeaderName, HdrName, InvalidHeaderName}; +use crate::convert::HttpTryFrom; +use sealed::Sealed; use std::{fmt, mem, ops, ptr, vec}; use std::collections::hash_map::RandomState; @@ -23,10 +25,11 @@ pub use self::into_header_name::IntoHeaderName; /// ``` /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST, LOCATION}; -/// let mut headers = HeaderMap::new(); -/// -/// headers.insert(HOST, "example.com".parse().unwrap()); -/// headers.insert(CONTENT_LENGTH, "123".parse().unwrap()); +/// # use http::HttpTryFrom; +/// let mut headers = HeaderMap::try_from(vec![ +/// (HOST, "example.com"), +/// (CONTENT_LENGTH, "123") +/// ]).unwrap(); /// /// assert!(headers.contains_key(HOST)); /// assert!(!headers.contains_key(LOCATION)); @@ -1720,6 +1723,45 @@ impl FromIterator<(HeaderName, T)> for HeaderMap } } +impl Sealed for HeaderMap {} + +/// Convert a collection of tuples into a HeaderMap +/// +/// # Examples +/// +/// ``` +/// # use http::{HttpTryFrom, Result, header::HeaderMap}; +/// # use std::collections::HashMap; +/// let mut headers_hashmap: HashMap = vec![ +/// ("X-Custom-Header".to_string(), "my value".to_string()), +/// ].iter().cloned().collect(); +/// +/// let good_headers: Result = HeaderMap::try_from(&headers_hashmap); +/// assert!(good_headers.is_ok()); +/// +/// headers_hashmap.insert("\r".into(), "\0".into()); +/// let bad_headers: Result = HeaderMap::try_from(&headers_hashmap); +/// assert!(bad_headers.is_err()); +/// ``` +impl HttpTryFrom for HeaderMap + where + COLLECTION: IntoIterator, + K: AsRef, + V: AsRef +{ + type Error = ::Error; + + fn try_from(c: COLLECTION) -> Result { + c.into_iter() + .map(|(k, v)| -> ::Result<(HeaderName, HeaderValue)> { + let name = k.as_ref().parse()?; + let value = v.as_ref().parse()?; + Ok((name, value)) + }) + .collect() + } +} + impl Extend<(Option, T)> for HeaderMap { /// Extend a `HeaderMap` with the contents of another `HeaderMap`. /// From b76d702ada3020b1caa5af7939f8d6086c9ecb3e Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 2 Jul 2019 15:17:07 +0200 Subject: [PATCH 2/8] Avoid using crate:: paths for compatibility with older rust versions --- src/header/map.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index a8c5ab0f..d17c9504 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,6 +1,7 @@ use super::HeaderValue; use super::name::{HeaderName, HdrName, InvalidHeaderName}; -use crate::convert::HttpTryFrom; +use convert::HttpTryFrom; +use Error; use sealed::Sealed; use std::{fmt, mem, ops, ptr, vec}; @@ -1749,7 +1750,7 @@ impl HttpTryFrom for HeaderMap K: AsRef, V: AsRef { - type Error = ::Error; + type Error = Error; fn try_from(c: COLLECTION) -> Result { c.into_iter() From 660c4bea7358c2f9834c823790b235fe8c2b80a2 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Wed, 3 Jul 2019 11:13:11 +0200 Subject: [PATCH 3/8] Implement conversion from HashMaps of any type, not just str See: https://github.com/hyperium/http/pull/326#issuecomment-507759885 --- src/header/map.rs | 10 +++++----- src/header/name.rs | 8 ++++++++ src/header/value.rs | 8 ++++++++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index d17c9504..b760d768 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,6 +1,6 @@ use super::HeaderValue; use super::name::{HeaderName, HdrName, InvalidHeaderName}; -use convert::HttpTryFrom; +use convert::{HttpTryFrom, HttpTryInto}; use Error; use sealed::Sealed; @@ -1747,16 +1747,16 @@ impl Sealed for HeaderMap {} impl HttpTryFrom for HeaderMap where COLLECTION: IntoIterator, - K: AsRef, - V: AsRef + HeaderName: HttpTryFrom, + HeaderValue: HttpTryFrom { type Error = Error; fn try_from(c: COLLECTION) -> Result { c.into_iter() .map(|(k, v)| -> ::Result<(HeaderName, HeaderValue)> { - let name = k.as_ref().parse()?; - let value = v.as_ref().parse()?; + let name : HeaderName = k.http_try_into()?; + let value : HeaderValue = v.http_try_into()?; Ok((name, value)) }) .collect() diff --git a/src/header/name.rs b/src/header/name.rs index 28b1469e..d5c128c4 100644 --- a/src/header/name.rs +++ b/src/header/name.rs @@ -1772,6 +1772,14 @@ impl<'a> HttpTryFrom<&'a str> for HeaderName { } } +impl<'a> HttpTryFrom<&'a String> for HeaderName { + type Error = InvalidHeaderName; + #[inline] + fn try_from(s: &'a String) -> Result { + Self::from_bytes(s.as_bytes()) + } +} + impl<'a> HttpTryFrom<&'a [u8]> for HeaderName { type Error = InvalidHeaderName; #[inline] diff --git a/src/header/value.rs b/src/header/value.rs index a56baf79..a295ce55 100644 --- a/src/header/value.rs +++ b/src/header/value.rs @@ -521,6 +521,14 @@ impl<'a> HttpTryFrom<&'a str> for HeaderValue { } } +impl<'a> HttpTryFrom<&'a String> for HeaderValue { + type Error = InvalidHeaderValue; + #[inline] + fn try_from(s: &'a String) -> Result { + Self::from_bytes(s.as_bytes()) + } +} + impl<'a> HttpTryFrom<&'a [u8]> for HeaderValue { type Error = InvalidHeaderValue; From f2f91c712c6032380f256dab2ac953bdb4bc7aff Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Thu, 11 Jul 2019 23:58:22 +0200 Subject: [PATCH 4/8] Implement changes suggested by @seanmonstar https://github.com/hyperium/http/pull/326/files#r302696965 https://github.com/hyperium/http/pull/326/files#r302697620 --- src/header/map.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index b760d768..2e738080 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -27,10 +27,10 @@ pub use self::into_header_name::IntoHeaderName; /// # use http::HeaderMap; /// # use http::header::{CONTENT_LENGTH, HOST, LOCATION}; /// # use http::HttpTryFrom; -/// let mut headers = HeaderMap::try_from(vec![ -/// (HOST, "example.com"), -/// (CONTENT_LENGTH, "123") -/// ]).unwrap(); +/// let mut headers = HeaderMap::new(); +/// +/// headers.insert(HOST, "example.com".parse().unwrap()); +/// headers.insert(CONTENT_LENGTH, "123".parse().unwrap()); /// /// assert!(headers.contains_key(HOST)); /// assert!(!headers.contains_key(LOCATION)); @@ -1744,15 +1744,15 @@ impl Sealed for HeaderMap {} /// let bad_headers: Result = HeaderMap::try_from(&headers_hashmap); /// assert!(bad_headers.is_err()); /// ``` -impl HttpTryFrom for HeaderMap +impl HttpTryFrom for HeaderMap where - COLLECTION: IntoIterator, + C: IntoIterator, HeaderName: HttpTryFrom, HeaderValue: HttpTryFrom { type Error = Error; - fn try_from(c: COLLECTION) -> Result { + fn try_from(c: C) -> Result { c.into_iter() .map(|(k, v)| -> ::Result<(HeaderName, HeaderValue)> { let name : HeaderName = k.http_try_into()?; From 1fbb55b2c62deaf5b9d5b2b2ffc0f1c1d06629ab Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Fri, 12 Jul 2019 09:24:15 +0200 Subject: [PATCH 5/8] Implement changes suggested by @seanmonstar https://github.com/hyperium/http/pull/326/files#r302696965 https://github.com/hyperium/http/pull/326/files#r302697620 --- src/convert.rs | 3 ++- src/header/map.rs | 35 ++++++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/src/convert.rs b/src/convert.rs index 7f61c8ca..16943f77 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,5 +1,5 @@ use Error; -use header::{HeaderName, HeaderValue}; +use header::{HeaderName, HeaderValue, HeaderMap}; use method::Method; use sealed::Sealed; use status::StatusCode; @@ -56,6 +56,7 @@ reflexive! { Uri, Method, StatusCode, + HeaderMap, HeaderName, HeaderValue, Scheme, diff --git a/src/header/map.rs b/src/header/map.rs index 2e738080..e9ee7cab 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,15 +1,16 @@ -use super::HeaderValue; -use super::name::{HeaderName, HdrName, InvalidHeaderName}; -use convert::{HttpTryFrom, HttpTryInto}; -use Error; -use sealed::Sealed; - use std::{fmt, mem, ops, ptr, vec}; +use std::collections::{BTreeMap, HashMap}; use std::collections::hash_map::RandomState; -use std::hash::{BuildHasher, Hasher, Hash}; +use std::hash::{BuildHasher, Hash, Hasher}; use std::iter::FromIterator; use std::marker::PhantomData; +use convert::{HttpTryFrom, HttpTryInto}; +use Error; + +use super::HeaderValue; +use super::name::{HdrName, HeaderName, InvalidHeaderName}; + pub use self::as_header_name::AsHeaderName; pub use self::into_header_name::IntoHeaderName; @@ -1724,7 +1725,23 @@ impl FromIterator<(HeaderName, T)> for HeaderMap } } -impl Sealed for HeaderMap {} +/// A marker trait that allows conversion from a type to HeaderMap +/// This trait is needed to have both a fast implementation of +/// HttpTryFrom and a generic implementation +/// of HttpTryFrom> for HeaderMap that do not conflict. +pub trait IntoHeaderMapAllowed {} + +impl IntoHeaderMapAllowed for HashMap {} + +impl<'a, K, V, S> IntoHeaderMapAllowed for &'a HashMap {} + +impl IntoHeaderMapAllowed for BTreeMap {} + +impl<'a, K, V> IntoHeaderMapAllowed for &'a BTreeMap {} + +impl<'a, T> IntoHeaderMapAllowed for &'a [T] {} + +impl IntoHeaderMapAllowed for Vec {} /// Convert a collection of tuples into a HeaderMap /// @@ -1746,7 +1763,7 @@ impl Sealed for HeaderMap {} /// ``` impl HttpTryFrom for HeaderMap where - C: IntoIterator, + C: IntoIterator + IntoHeaderMapAllowed, HeaderName: HttpTryFrom, HeaderValue: HttpTryFrom { From baabb0a0773a15269e17b6faf97416df176d182b Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Tue, 23 Jul 2019 15:44:46 +0200 Subject: [PATCH 6/8] Remove implementation for non-HashMap types See https://github.com/hyperium/http/pull/326#issuecomment-511032065 --- src/header/map.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index e9ee7cab..22dc6787 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,5 +1,5 @@ use std::{fmt, mem, ops, ptr, vec}; -use std::collections::{BTreeMap, HashMap}; +use std::collections::{HashMap}; use std::collections::hash_map::RandomState; use std::hash::{BuildHasher, Hash, Hasher}; use std::iter::FromIterator; @@ -1735,14 +1735,6 @@ impl IntoHeaderMapAllowed for HashMap {} impl<'a, K, V, S> IntoHeaderMapAllowed for &'a HashMap {} -impl IntoHeaderMapAllowed for BTreeMap {} - -impl<'a, K, V> IntoHeaderMapAllowed for &'a BTreeMap {} - -impl<'a, T> IntoHeaderMapAllowed for &'a [T] {} - -impl IntoHeaderMapAllowed for Vec {} - /// Convert a collection of tuples into a HeaderMap /// /// # Examples From c951ff9a82862286aaefa45ccbef31342e5e6e52 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Wed, 24 Jul 2019 12:11:52 +0200 Subject: [PATCH 7/8] Remove implementation for HashMap, leaving only @HashMap See https://github.com/hyperium/http/pull/326#issuecomment-511032065 --- src/header/map.rs | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/src/header/map.rs b/src/header/map.rs index 22dc6787..0a2353c0 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1,6 +1,6 @@ use std::{fmt, mem, ops, ptr, vec}; -use std::collections::{HashMap}; use std::collections::hash_map::RandomState; +use std::collections::HashMap; use std::hash::{BuildHasher, Hash, Hasher}; use std::iter::FromIterator; use std::marker::PhantomData; @@ -1725,16 +1725,6 @@ impl FromIterator<(HeaderName, T)> for HeaderMap } } -/// A marker trait that allows conversion from a type to HeaderMap -/// This trait is needed to have both a fast implementation of -/// HttpTryFrom and a generic implementation -/// of HttpTryFrom> for HeaderMap that do not conflict. -pub trait IntoHeaderMapAllowed {} - -impl IntoHeaderMapAllowed for HashMap {} - -impl<'a, K, V, S> IntoHeaderMapAllowed for &'a HashMap {} - /// Convert a collection of tuples into a HeaderMap /// /// # Examples @@ -1753,15 +1743,14 @@ impl<'a, K, V, S> IntoHeaderMapAllowed for &'a HashMap {} /// let bad_headers: Result = HeaderMap::try_from(&headers_hashmap); /// assert!(bad_headers.is_err()); /// ``` -impl HttpTryFrom for HeaderMap +impl<'a, K, V> HttpTryFrom<&'a HashMap> for HeaderMap where - C: IntoIterator + IntoHeaderMapAllowed, - HeaderName: HttpTryFrom, - HeaderValue: HttpTryFrom + HeaderName: HttpTryFrom<&'a K>, + HeaderValue: HttpTryFrom<&'a V> { type Error = Error; - fn try_from(c: C) -> Result { + fn try_from(c: &'a HashMap) -> Result { c.into_iter() .map(|(k, v)| -> ::Result<(HeaderName, HeaderValue)> { let name : HeaderName = k.http_try_into()?; @@ -3213,7 +3202,7 @@ mod as_header_name { use super::{Entry, HdrName, HeaderMap, HeaderName, InvalidHeaderName}; /// A marker trait used to identify values that can be used as search keys - /// to a `HeaderMap`. + /// to a `HeaderMap`. pub trait AsHeaderName: Sealed {} // All methods are on this pub(super) trait, instead of `AsHeaderName`, From 7c4dafeec2815974dc9b8a450b405eea3b343af8 Mon Sep 17 00:00:00 2001 From: Ophir LOJKINE Date: Thu, 25 Jul 2019 09:48:51 +0200 Subject: [PATCH 8/8] Fix compilation for rust 1.20 --- src/header/map.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/header/map.rs b/src/header/map.rs index 0a2353c0..fd86cc6f 100644 --- a/src/header/map.rs +++ b/src/header/map.rs @@ -1745,6 +1745,7 @@ impl FromIterator<(HeaderName, T)> for HeaderMap /// ``` impl<'a, K, V> HttpTryFrom<&'a HashMap> for HeaderMap where + K: Eq + Hash, HeaderName: HttpTryFrom<&'a K>, HeaderValue: HttpTryFrom<&'a V> {