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