feat(lib): implement compatibility with http crate
This commit is contained in:
		| @@ -8,6 +8,8 @@ matrix: | |||||||
|           env: FEATURES="--features nightly" |           env: FEATURES="--features nightly" | ||||||
|         - rust: beta |         - rust: beta | ||||||
|         - rust: stable |         - rust: stable | ||||||
|  |         - rust: stable | ||||||
|  |           env: FEATURES="--features compat" | ||||||
|         - rust: 1.15.0 |         - rust: 1.15.0 | ||||||
|  |  | ||||||
| cache: | cache: | ||||||
|   | |||||||
| @@ -23,6 +23,7 @@ base64 = "0.6" | |||||||
| bytes = "0.4.4" | bytes = "0.4.4" | ||||||
| futures = "0.1.14" | futures = "0.1.14" | ||||||
| futures-cpupool = "0.1" | futures-cpupool = "0.1" | ||||||
|  | http = { version = "0.1", optional = true } | ||||||
| httparse = "1.0" | httparse = "1.0" | ||||||
| language-tags = "0.2" | language-tags = "0.2" | ||||||
| log = "0.3" | log = "0.3" | ||||||
| @@ -45,3 +46,4 @@ spmc = "0.2" | |||||||
| default = [] | default = [] | ||||||
| nightly = [] | nightly = [] | ||||||
| raw_status = [] | raw_status = [] | ||||||
|  | compat = [ "http" ] | ||||||
							
								
								
									
										6
									
								
								src/client/compat.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								src/client/compat.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | |||||||
|  | //! Wrappers to build compatibility with the `http` crate. | ||||||
|  |  | ||||||
|  | pub use super::compat_impl::{ | ||||||
|  |     CompatClient, | ||||||
|  |     CompatFutureResponse | ||||||
|  | }; | ||||||
							
								
								
									
										53
									
								
								src/client/compat_impl.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/client/compat_impl.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | use futures::{Future, Poll, Stream}; | ||||||
|  | use http_types; | ||||||
|  | use tokio_service::Service; | ||||||
|  |  | ||||||
|  | use client::{Connect, Client, FutureResponse}; | ||||||
|  | use error::Error; | ||||||
|  | use http::Body; | ||||||
|  |  | ||||||
|  | /// A Client to make outgoing HTTP requests. | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct CompatClient<C, B = Body> { | ||||||
|  |     inner: Client<C, B> | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn client<C, B>(client: Client<C, B>) -> CompatClient<C, B> { | ||||||
|  |     CompatClient { inner: client } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<C, B> Service for CompatClient<C, B> | ||||||
|  | where C: Connect, | ||||||
|  |       B: Stream<Error=Error> + 'static, | ||||||
|  |       B::Item: AsRef<[u8]>, | ||||||
|  | { | ||||||
|  |     type Request = http_types::Request<B>; | ||||||
|  |     type Response = http_types::Response<Body>; | ||||||
|  |     type Error = Error; | ||||||
|  |     type Future = CompatFutureResponse; | ||||||
|  |  | ||||||
|  |     fn call(&self, req: Self::Request) -> Self::Future { | ||||||
|  |         future(self.inner.call(req.into())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// A `Future` that will resolve to an `http::Response`. | ||||||
|  | #[must_use = "futures do nothing unless polled"] | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct CompatFutureResponse { | ||||||
|  |     inner: FutureResponse | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn future(fut: FutureResponse) -> CompatFutureResponse { | ||||||
|  |     CompatFutureResponse { inner: fut } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl Future for CompatFutureResponse { | ||||||
|  |     type Item = http_types::Response<Body>; | ||||||
|  |     type Error = Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Self::Item, Error> { | ||||||
|  |         self.inner.poll() | ||||||
|  |             .map(|a| a.map(|r| r.into())) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -9,6 +9,8 @@ use std::time::Duration; | |||||||
|  |  | ||||||
| use futures::{future, Poll, Async, Future, Stream}; | use futures::{future, Poll, Async, Future, Stream}; | ||||||
| use futures::unsync::oneshot; | use futures::unsync::oneshot; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio::reactor::Handle; | use tokio::reactor::Handle; | ||||||
| use tokio_proto::BindClient; | use tokio_proto::BindClient; | ||||||
| @@ -33,6 +35,10 @@ pub use self::connect::{HttpConnector, Connect}; | |||||||
| mod connect; | mod connect; | ||||||
| mod dns; | mod dns; | ||||||
| mod pool; | mod pool; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | mod compat_impl; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | pub mod compat; | ||||||
|  |  | ||||||
| /// A Client to make outgoing HTTP requests. | /// A Client to make outgoing HTTP requests. | ||||||
| // If the Connector is clone, then the Client can be clone easily. | // If the Connector is clone, then the Client can be clone easily. | ||||||
| @@ -108,6 +114,19 @@ where C: Connect, | |||||||
|     pub fn request(&self, req: Request<B>) -> FutureResponse { |     pub fn request(&self, req: Request<B>) -> FutureResponse { | ||||||
|         self.call(req) |         self.call(req) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Send an `http::Request` using this Client. | ||||||
|  |     #[inline] | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     pub fn request_compat(&self, req: http_types::Request<B>) -> compat::CompatFutureResponse { | ||||||
|  |         self::compat_impl::future(self.call(req.into())) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Convert into a client accepting `http::Request`. | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     pub fn into_compat(self) -> compat::CompatClient<C, B> { | ||||||
|  |         self::compat_impl::client(self) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A `Future` that will resolve to an HTTP Response. | /// A `Future` that will resolve to an HTTP Response. | ||||||
|   | |||||||
| @@ -29,6 +29,11 @@ impl ByteStr { | |||||||
|     pub fn as_str(&self) -> &str { |     pub fn as_str(&self) -> &str { | ||||||
|         unsafe { str::from_utf8_unchecked(self.0.as_ref()) } |         unsafe { str::from_utf8_unchecked(self.0.as_ref()) } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     pub fn into_bytes(self) -> Bytes { | ||||||
|  |         self.0 | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Deref for ByteStr { | impl Deref for ByteStr { | ||||||
|   | |||||||
| @@ -77,9 +77,14 @@ | |||||||
| //! } | //! } | ||||||
| //! ``` | //! ``` | ||||||
| use std::borrow::{Cow, ToOwned}; | use std::borrow::{Cow, ToOwned}; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use std::convert::From; | ||||||
| use std::iter::{FromIterator, IntoIterator}; | use std::iter::{FromIterator, IntoIterator}; | ||||||
| use std::{mem, fmt}; | use std::{mem, fmt}; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use unicase::Ascii; | use unicase::Ascii; | ||||||
|  |  | ||||||
| use self::internals::{Item, VecMap, Entry}; | use self::internals::{Item, VecMap, Entry}; | ||||||
| @@ -546,6 +551,54 @@ impl fmt::Debug for Headers { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<http_types::HeaderMap> for Headers { | ||||||
|  |     fn from(mut header_map: http_types::HeaderMap) -> Headers { | ||||||
|  |         let mut headers = Headers::new(); | ||||||
|  |         for (name, mut value_drain) in header_map.drain() { | ||||||
|  |             if let Some(first_value) = value_drain.next() { | ||||||
|  |                 let mut raw: Raw = first_value.as_bytes().into(); | ||||||
|  |                 for value in value_drain { | ||||||
|  |                     raw.push(value.as_bytes()); | ||||||
|  |                 } | ||||||
|  |                 headers.append_raw(name.as_str().to_string(), raw);         | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         headers | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<Headers> for http_types::HeaderMap { | ||||||
|  |     fn from(headers: Headers) -> http_types::HeaderMap { | ||||||
|  |         let mut header_map = http_types::HeaderMap::new(); | ||||||
|  |         for header in headers.iter() { | ||||||
|  |             let entry = header_map.entry(header.name()) | ||||||
|  |                 .expect("attempted to convert invalid header name"); | ||||||
|  |             let mut value_iter = header.raw().iter().map(|line| { | ||||||
|  |                 http_types::header::HeaderValue::from_bytes(line) | ||||||
|  |                     .expect("attempted to convert invalid header value") | ||||||
|  |             }); | ||||||
|  |             match entry { | ||||||
|  |                 http_types::header::Entry::Occupied(mut  occupied) => { | ||||||
|  |                     for value in value_iter { | ||||||
|  |                         occupied.append(value); | ||||||
|  |                     } | ||||||
|  |                 }, | ||||||
|  |                 http_types::header::Entry::Vacant(vacant) => { | ||||||
|  |                     if let Some(first_value) = value_iter.next() { | ||||||
|  |                         let mut occupied = vacant.insert_entry(first_value); | ||||||
|  |                         for value in value_iter { | ||||||
|  |                             occupied.append(value); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         header_map | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// An `Iterator` over the fields in a `Headers` map. | /// An `Iterator` over the fields in a `Headers` map. | ||||||
| #[allow(missing_debug_implementations)] | #[allow(missing_debug_implementations)] | ||||||
| pub struct HeadersItems<'a> { | pub struct HeadersItems<'a> { | ||||||
| @@ -940,6 +993,29 @@ mod tests { | |||||||
|         assert_ne!(headers1, headers2); |         assert_ne!(headers1, headers2); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     fn test_compat() { | ||||||
|  |         use http_types; | ||||||
|  |  | ||||||
|  |         let mut orig_hyper_headers = Headers::new(); | ||||||
|  |         orig_hyper_headers.set(ContentLength(11)); | ||||||
|  |         orig_hyper_headers.set(Host::new("foo.bar", None)); | ||||||
|  |         orig_hyper_headers.append_raw("x-foo", b"bar".to_vec()); | ||||||
|  |         orig_hyper_headers.append_raw("x-foo", b"quux".to_vec()); | ||||||
|  |  | ||||||
|  |         let mut orig_http_headers = http_types::HeaderMap::new(); | ||||||
|  |         orig_http_headers.insert(http_types::header::CONTENT_LENGTH, "11".parse().unwrap()); | ||||||
|  |         orig_http_headers.insert(http_types::header::HOST, "foo.bar".parse().unwrap()); | ||||||
|  |         orig_http_headers.append("x-foo", "bar".parse().unwrap()); | ||||||
|  |         orig_http_headers.append("x-foo", "quux".parse().unwrap()); | ||||||
|  |  | ||||||
|  |         let conv_hyper_headers: Headers = orig_http_headers.clone().into(); | ||||||
|  |         let conv_http_headers: http_types::HeaderMap = orig_hyper_headers.clone().into(); | ||||||
|  |         assert_eq!(orig_hyper_headers, conv_hyper_headers); | ||||||
|  |         assert_eq!(orig_http_headers, conv_http_headers); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[cfg(feature = "nightly")] |     #[cfg(feature = "nightly")] | ||||||
|     #[bench] |     #[bench] | ||||||
|     fn bench_headers_new(b: &mut Bencher) { |     fn bench_headers_new(b: &mut Bencher) { | ||||||
|   | |||||||
| @@ -1,11 +1,16 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use std::mem::replace; | ||||||
|  | use std::net::SocketAddr; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use header::Headers; | use header::Headers; | ||||||
| use http::{Body, MessageHead, RequestHead, RequestLine}; | use http::{Body, MessageHead, RequestHead, RequestLine}; | ||||||
| use method::Method; | use method::Method; | ||||||
| use uri::{self, Uri}; | use uri::{self, Uri}; | ||||||
| use version::HttpVersion; | use version::HttpVersion; | ||||||
| use std::net::SocketAddr; |  | ||||||
|  |  | ||||||
| /// An HTTP Request | /// An HTTP Request | ||||||
| pub struct Request<B = Body> { | pub struct Request<B = Body> { | ||||||
| @@ -132,6 +137,36 @@ impl<B> fmt::Debug for Request<B> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<Request> for http_types::Request<Body> { | ||||||
|  |     fn from(from_req: Request) -> http_types::Request<Body> { | ||||||
|  |         let (m, u, v, h, b) = from_req.deconstruct(); | ||||||
|  |  | ||||||
|  |         let to_req = http_types::Request::new(()); | ||||||
|  |         let (mut to_parts, _) = to_req.into_parts(); | ||||||
|  |  | ||||||
|  |         to_parts.method = m.into(); | ||||||
|  |         to_parts.uri = u.into(); | ||||||
|  |         to_parts.version = v.into(); | ||||||
|  |         to_parts.headers = h.into(); | ||||||
|  |  | ||||||
|  |         http_types::Request::from_parts(to_parts, b) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl<B> From<http_types::Request<B>> for Request<B> { | ||||||
|  |     fn from(from_req: http_types::Request<B>) -> Request<B> { | ||||||
|  |         let (from_parts, body) = from_req.into_parts(); | ||||||
|  |  | ||||||
|  |         let mut to_req = Request::new(from_parts.method.into(), from_parts.uri.into()); | ||||||
|  |         to_req.set_version(from_parts.version.into()); | ||||||
|  |         replace(to_req.headers_mut(), from_parts.headers.into()); | ||||||
|  |         to_req.set_body(body); | ||||||
|  |         to_req | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Constructs a request using a received ResponseHead and optional body | /// Constructs a request using a received ResponseHead and optional body | ||||||
| pub fn from_wire<B>(addr: Option<SocketAddr>, incoming: RequestHead, body: B) -> Request<B> { | pub fn from_wire<B>(addr: Option<SocketAddr>, incoming: RequestHead, body: B) -> Request<B> { | ||||||
|     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; |     let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming; | ||||||
|   | |||||||
| @@ -1,4 +1,9 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use std::mem::replace; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use header::{Header, Headers}; | use header::{Header, Headers}; | ||||||
| use http::{MessageHead, ResponseHead, Body}; | use http::{MessageHead, ResponseHead, Body}; | ||||||
| @@ -142,6 +147,30 @@ impl fmt::Debug for Response { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl<B> From<http_types::Response<B>> for Response<B> { | ||||||
|  |     fn from(from_res: http_types::Response<B>) -> Response<B> { | ||||||
|  |         let (from_parts, body) = from_res.into_parts(); | ||||||
|  |         let mut to_res = Response::new(); | ||||||
|  |         to_res.version = from_parts.version.into(); | ||||||
|  |         to_res.set_status(from_parts.status.into()); | ||||||
|  |         replace(to_res.headers_mut(), from_parts.headers.into()); | ||||||
|  |         to_res.with_body(body) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<Response> for http_types::Response<Body> { | ||||||
|  |     fn from(mut from_res: Response) -> http_types::Response<Body> { | ||||||
|  |         let (mut to_parts, ()) = http_types::Response::new(()).into_parts(); | ||||||
|  |         to_parts.version = from_res.version().into(); | ||||||
|  |         to_parts.status = from_res.status().into(); | ||||||
|  |         let from_headers = replace(from_res.headers_mut(), Headers::new()); | ||||||
|  |         to_parts.headers = from_headers.into(); | ||||||
|  |         http_types::Response::from_parts(to_parts, from_res.body()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Constructs a response using a received ResponseHead and optional body | /// Constructs a response using a received ResponseHead and optional body | ||||||
| #[inline] | #[inline] | ||||||
| #[cfg(not(feature = "raw_status"))] | #[cfg(not(feature = "raw_status"))] | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ extern crate base64; | |||||||
| extern crate bytes; | extern crate bytes; | ||||||
| #[macro_use] extern crate futures; | #[macro_use] extern crate futures; | ||||||
| extern crate futures_cpupool; | extern crate futures_cpupool; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | extern crate http as http_types; | ||||||
| extern crate httparse; | extern crate httparse; | ||||||
| extern crate language_tags; | extern crate language_tags; | ||||||
| #[macro_use] extern crate log; | #[macro_use] extern crate log; | ||||||
|   | |||||||
| @@ -3,6 +3,9 @@ use std::fmt; | |||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
| use std::convert::AsRef; | use std::convert::AsRef; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use error::Error; | use error::Error; | ||||||
| use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch, | use self::Method::{Options, Get, Post, Put, Delete, Head, Trace, Connect, Patch, | ||||||
|                    Extension}; |                    Extension}; | ||||||
| @@ -156,6 +159,68 @@ impl Default for Method { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<http_types::Method> for Method { | ||||||
|  |     fn from(method: http_types::Method) -> Method { | ||||||
|  |         match method { | ||||||
|  |             http_types::Method::GET => | ||||||
|  |                 Method::Get, | ||||||
|  |             http_types::Method::POST => | ||||||
|  |                 Method::Post, | ||||||
|  |             http_types::Method::PUT => | ||||||
|  |                 Method::Put, | ||||||
|  |             http_types::Method::DELETE => | ||||||
|  |                 Method::Delete, | ||||||
|  |             http_types::Method::HEAD => | ||||||
|  |                 Method::Head, | ||||||
|  |             http_types::Method::OPTIONS => | ||||||
|  |                 Method::Options, | ||||||
|  |             http_types::Method::CONNECT => | ||||||
|  |                 Method::Connect, | ||||||
|  |             http_types::Method::PATCH => | ||||||
|  |                 Method::Patch, | ||||||
|  |             http_types::Method::TRACE => | ||||||
|  |                 Method::Trace, | ||||||
|  |             _ => { | ||||||
|  |                 method.as_ref().parse() | ||||||
|  |                     .expect("attempted to convert invalid method") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<Method> for http_types::Method { | ||||||
|  |     fn from(method: Method) -> http_types::Method { | ||||||
|  |         use http_types::HttpTryFrom; | ||||||
|  |  | ||||||
|  |         match method { | ||||||
|  |             Method::Get => | ||||||
|  |                 http_types::Method::GET, | ||||||
|  |             Method::Post => | ||||||
|  |                 http_types::Method::POST, | ||||||
|  |             Method::Put => | ||||||
|  |                 http_types::Method::PUT, | ||||||
|  |             Method::Delete => | ||||||
|  |                 http_types::Method::DELETE, | ||||||
|  |             Method::Head => | ||||||
|  |                 http_types::Method::HEAD, | ||||||
|  |             Method::Options => | ||||||
|  |                 http_types::Method::OPTIONS, | ||||||
|  |             Method::Connect => | ||||||
|  |                 http_types::Method::CONNECT, | ||||||
|  |             Method::Patch => | ||||||
|  |                 http_types::Method::PATCH, | ||||||
|  |             Method::Trace => | ||||||
|  |                 http_types::Method::TRACE, | ||||||
|  |             Method::Extension(s) => { | ||||||
|  |                 HttpTryFrom::try_from(s.as_str()) | ||||||
|  |                     .expect("attempted to convert invalid method") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::collections::HashMap; |     use std::collections::HashMap; | ||||||
| @@ -210,4 +275,25 @@ mod tests { | |||||||
|         assert_eq!(Put.as_ref(), "PUT"); |         assert_eq!(Put.as_ref(), "PUT"); | ||||||
|         assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE"); |         assert_eq!(Extension("MOVE".to_owned()).as_ref(), "MOVE"); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     fn test_compat() { | ||||||
|  |         use http_types::{self, HttpTryFrom}; | ||||||
|  |  | ||||||
|  |         let methods = vec![ | ||||||
|  |             "GET", | ||||||
|  |             "POST", | ||||||
|  |             "PUT", | ||||||
|  |             "MOVE" | ||||||
|  |         ]; | ||||||
|  |         for method in methods { | ||||||
|  |             let orig_hyper_method = Method::from_str(method).unwrap(); | ||||||
|  |             let orig_http_method = http_types::Method::try_from(method).unwrap(); | ||||||
|  |             let conv_hyper_method: Method = orig_http_method.clone().into(); | ||||||
|  |             let conv_http_method: http_types::Method = orig_hyper_method.clone().into(); | ||||||
|  |             assert_eq!(orig_hyper_method, conv_hyper_method); | ||||||
|  |             assert_eq!(orig_http_method, conv_http_method); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								src/server/compat.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/server/compat.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | //! Wrappers to build compatibility with the `http` crate. | ||||||
|  |  | ||||||
|  | pub use super::compat_impl::{ | ||||||
|  |     CompatFuture, | ||||||
|  |     CompatService, | ||||||
|  |     NewCompatService | ||||||
|  | }; | ||||||
							
								
								
									
										82
									
								
								src/server/compat_impl.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/server/compat_impl.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | use std::io::{Error as IoError}; | ||||||
|  |  | ||||||
|  | use futures::{Future, Poll}; | ||||||
|  | use http_types; | ||||||
|  | use tokio_service::{NewService, Service}; | ||||||
|  |  | ||||||
|  | use error::Error; | ||||||
|  | use http::Body; | ||||||
|  | use http::request::Request; | ||||||
|  | use http::response::Response; | ||||||
|  |  | ||||||
|  | /// Wraps a `Future` returning an `http::Response` into | ||||||
|  | /// a `Future` returning a `hyper::server::Response`. | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct CompatFuture<F> { | ||||||
|  |     future: F | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<F, Bd> Future for CompatFuture<F> | ||||||
|  |     where F: Future<Item=http_types::Response<Bd>, Error=Error> | ||||||
|  | { | ||||||
|  |     type Item = Response<Bd>; | ||||||
|  |     type Error = Error; | ||||||
|  |  | ||||||
|  |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|  |         self.future.poll() | ||||||
|  |             .map(|a| a.map(|res| res.into())) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wraps a `Service` taking an `http::Request` and returning | ||||||
|  | /// an `http::Response` into a `Service` taking a `hyper::server::Request`, | ||||||
|  | /// and returning a `hyper::server::Response`. | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct CompatService<S> { | ||||||
|  |     service: S | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn service<S>(service: S) -> CompatService<S> { | ||||||
|  |     CompatService { service: service } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<S, Bd> Service for CompatService<S> | ||||||
|  |     where S: Service<Request=http_types::Request<Body>, Response=http_types::Response<Bd>, Error=Error> | ||||||
|  | { | ||||||
|  |     type Request = Request; | ||||||
|  |     type Response = Response<Bd>; | ||||||
|  |     type Error = Error; | ||||||
|  |     type Future = CompatFuture<S::Future>; | ||||||
|  |  | ||||||
|  |     fn call(&self, req: Self::Request) -> Self::Future { | ||||||
|  |         CompatFuture { | ||||||
|  |             future: self.service.call(req.into()) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /// Wraps a `NewService` taking an `http::Request` and returning | ||||||
|  | /// an `http::Response` into a `NewService` taking a `hyper::server::Request`, | ||||||
|  | /// and returning a `hyper::server::Response`. | ||||||
|  | #[derive(Debug)] | ||||||
|  | pub struct NewCompatService<S> { | ||||||
|  |     new_service: S | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub fn new_service<S>(new_service: S) -> NewCompatService<S> { | ||||||
|  |     NewCompatService { new_service: new_service } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<S, Bd> NewService for NewCompatService<S> | ||||||
|  |     where S: NewService<Request=http_types::Request<Body>, Response=http_types::Response<Bd>, Error=Error> | ||||||
|  | { | ||||||
|  |     type Request = Request; | ||||||
|  |     type Response = Response<Bd>; | ||||||
|  |     type Error = Error; | ||||||
|  |     type Instance = CompatService<S::Instance>; | ||||||
|  |  | ||||||
|  |     fn new_service(&self) -> Result<Self::Instance, IoError> { | ||||||
|  |         self.new_service.new_service() | ||||||
|  |             .map(service) | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -3,6 +3,11 @@ | |||||||
| //! A `Server` is created to listen on a port, parse HTTP requests, and hand | //! A `Server` is created to listen on a port, parse HTTP requests, and hand | ||||||
| //! them off to a `Service`. | //! them off to a `Service`. | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | mod compat_impl; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | pub mod compat; | ||||||
|  |  | ||||||
| use std::cell::RefCell; | use std::cell::RefCell; | ||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::io; | use std::io; | ||||||
| @@ -16,6 +21,9 @@ use futures::task::{self, Task}; | |||||||
| use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; | use futures::{Future, Stream, Poll, Async, Sink, StartSend, AsyncSink}; | ||||||
| use futures::future::Map; | use futures::future::Map; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use tokio_io::{AsyncRead, AsyncWrite}; | use tokio_io::{AsyncRead, AsyncWrite}; | ||||||
| use tokio::reactor::{Core, Handle, Timeout}; | use tokio::reactor::{Core, Handle, Timeout}; | ||||||
| use tokio::net::TcpListener; | use tokio::net::TcpListener; | ||||||
| @@ -27,6 +35,8 @@ pub use tokio_service::{NewService, Service}; | |||||||
| use http; | use http; | ||||||
| use http::response; | use http::response; | ||||||
| use http::request; | use http::request; | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http::Body; | ||||||
|  |  | ||||||
| pub use http::response::Response; | pub use http::response::Response; | ||||||
| pub use http::request::Request; | pub use http::request::Request; | ||||||
| @@ -103,6 +113,18 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Bind a `NewService` using types from the `http` crate. | ||||||
|  |     /// | ||||||
|  |     /// See `Http::bind`. | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     pub fn bind_compat<S, Bd>(&self, addr: &SocketAddr, new_service: S) -> ::Result<Server<compat::NewCompatService<S>, Bd>> | ||||||
|  |         where S: NewService<Request = http_types::Request<Body>, Response = http_types::Response<Bd>, Error = ::Error> + | ||||||
|  |                     Send + Sync + 'static, | ||||||
|  |               Bd: Stream<Item=B, Error=::Error>, | ||||||
|  |     { | ||||||
|  |         self.bind(addr, self::compat_impl::new_service(new_service)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     /// Use this `Http` instance to create a new server task which handles the |     /// Use this `Http` instance to create a new server task which handles the | ||||||
|     /// connection `io` provided. |     /// connection `io` provided. | ||||||
|     /// |     /// | ||||||
| @@ -131,6 +153,25 @@ impl<B: AsRef<[u8]> + 'static> Http<B> { | |||||||
|             remote_addr: remote_addr, |             remote_addr: remote_addr, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Bind a `Service` using types from the `http` crate. | ||||||
|  |     /// | ||||||
|  |     /// See `Http::bind_connection`. | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     pub fn bind_connection_compat<S, I, Bd>(&self, | ||||||
|  |                                  handle: &Handle, | ||||||
|  |                                  io: I, | ||||||
|  |                                  remote_addr: SocketAddr, | ||||||
|  |                                  service: S) | ||||||
|  |         where S: Service<Request = http_types::Request<Body>, Response = http_types::Response<Bd>, Error = ::Error> + 'static, | ||||||
|  |               Bd: Stream<Item=B, Error=::Error> + 'static, | ||||||
|  |               I: AsyncRead + AsyncWrite + 'static, | ||||||
|  |     { | ||||||
|  |         self.bind_server(handle, io, HttpService { | ||||||
|  |             inner: compat_impl::service(service), | ||||||
|  |             remote_addr: remote_addr, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<B> Clone for Http<B> { | impl<B> Clone for Http<B> { | ||||||
|   | |||||||
| @@ -2,6 +2,9 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::cmp::Ordering; | use std::cmp::Ordering; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| /// An HTTP status code (`status-code` in RFC 7230 et al.). | /// An HTTP status code (`status-code` in RFC 7230 et al.). | ||||||
| /// | /// | ||||||
| /// This enum contains all common status codes and an Unregistered | /// This enum contains all common status codes and an Unregistered | ||||||
| @@ -596,6 +599,22 @@ impl From<StatusCode> for u16 { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<http_types::StatusCode> for StatusCode { | ||||||
|  |     fn from(status: http_types::StatusCode) -> StatusCode { | ||||||
|  |         StatusCode::try_from(status.as_u16()) | ||||||
|  |             .expect("attempted to convert invalid status code") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<StatusCode> for http_types::StatusCode { | ||||||
|  |     fn from(status: StatusCode) -> http_types::StatusCode { | ||||||
|  |         http_types::StatusCode::from_u16(status.as_u16()) | ||||||
|  |             .expect("attempted to convert invalid status code") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// The class of an HTTP `status-code`. | /// The class of an HTTP `status-code`. | ||||||
| /// | /// | ||||||
| /// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6): | /// [RFC 7231, section 6 (Response Status Codes)](https://tools.ietf.org/html/rfc7231#section-6): | ||||||
| @@ -746,4 +765,18 @@ mod tests { | |||||||
|         StatusCode::try_from(0).unwrap_err(); |         StatusCode::try_from(0).unwrap_err(); | ||||||
|         StatusCode::try_from(1000).unwrap_err(); |         StatusCode::try_from(1000).unwrap_err(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     #[cfg(feature = "compat")] | ||||||
|  |     fn test_compat() { | ||||||
|  |         use http_types::{self, HttpTryFrom}; | ||||||
|  |         for i in 100..600 { | ||||||
|  |             let orig_hyper_status = StatusCode::try_from(i).unwrap(); | ||||||
|  |             let orig_http_status = http_types::StatusCode::try_from(i).unwrap(); | ||||||
|  |             let conv_hyper_status: StatusCode = orig_http_status.into(); | ||||||
|  |             let conv_http_status: http_types::StatusCode = orig_hyper_status.into(); | ||||||
|  |             assert_eq!(orig_hyper_status, conv_hyper_status); | ||||||
|  |             assert_eq!(orig_http_status, conv_http_status); | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/uri.rs
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/uri.rs
									
									
									
									
									
								
							| @@ -2,6 +2,9 @@ use std::error::Error as StdError; | |||||||
| use std::fmt::{Display, self}; | use std::fmt::{Display, self}; | ||||||
| use std::str::{self, FromStr}; | use std::str::{self, FromStr}; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use ::common::ByteStr; | use ::common::ByteStr; | ||||||
| use bytes::{BufMut, Bytes, BytesMut}; | use bytes::{BufMut, Bytes, BytesMut}; | ||||||
|  |  | ||||||
| @@ -315,6 +318,23 @@ impl Display for Uri { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<http_types::Uri> for Uri { | ||||||
|  |     fn from(uri: http_types::Uri) -> Uri { | ||||||
|  |         uri.to_string().parse() | ||||||
|  |             .expect("attempted to convert invalid uri") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<Uri> for http_types::Uri { | ||||||
|  |     fn from(uri: Uri) -> http_types::Uri { | ||||||
|  |         let bytes = uri.source.into_bytes(); | ||||||
|  |         http_types::Uri::from_shared(bytes) | ||||||
|  |             .expect("attempted to convert invalid uri") | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| pub unsafe fn from_utf8_unchecked(slice: Bytes) -> Result<Uri, UriError> { | pub unsafe fn from_utf8_unchecked(slice: Bytes) -> Result<Uri, UriError> { | ||||||
|     Uri::new(ByteStr::from_utf8_unchecked(slice)) |     Uri::new(ByteStr::from_utf8_unchecked(slice)) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,6 +5,9 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
| use std::str::FromStr; | use std::str::FromStr; | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | use http_types; | ||||||
|  |  | ||||||
| use error::Error; | use error::Error; | ||||||
| use self::HttpVersion::{Http09, Http10, Http11, H2, H2c}; | use self::HttpVersion::{Http09, Http10, Http11, H2, H2c}; | ||||||
|  |  | ||||||
| @@ -58,6 +61,39 @@ impl Default for HttpVersion { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<http_types::Version> for HttpVersion { | ||||||
|  |     fn from(v: http_types::Version) -> HttpVersion { | ||||||
|  |         match v { | ||||||
|  |             http_types::Version::HTTP_09 => | ||||||
|  |                 HttpVersion::Http09, | ||||||
|  |             http_types::Version::HTTP_10 => | ||||||
|  |                 HttpVersion::Http10, | ||||||
|  |             http_types::Version::HTTP_11 => | ||||||
|  |                 HttpVersion::Http11, | ||||||
|  |             http_types::Version::HTTP_2 => | ||||||
|  |                 HttpVersion::H2 | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[cfg(feature = "compat")] | ||||||
|  | impl From<HttpVersion> for http_types::Version { | ||||||
|  |     fn from(v: HttpVersion) -> http_types::Version { | ||||||
|  |         match v { | ||||||
|  |             HttpVersion::Http09 => | ||||||
|  |                 http_types::Version::HTTP_09, | ||||||
|  |             HttpVersion::Http10 => | ||||||
|  |                 http_types::Version::HTTP_10, | ||||||
|  |             HttpVersion::Http11 => | ||||||
|  |                 http_types::Version::HTTP_11, | ||||||
|  |             HttpVersion::H2 => | ||||||
|  |                 http_types::Version::HTTP_2, | ||||||
|  |             _ => panic!("attempted to convert unexpected http version") | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
| mod tests { | mod tests { | ||||||
|     use std::str::FromStr; |     use std::str::FromStr; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user