Skip to content

Commit 0c7d375

Browse files
srijsseanmonstar
authored andcommitted
feat(lib): implement compatibility with http crate
1 parent 92595e8 commit 0c7d375

17 files changed

+535
-1
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ matrix:
88
env: FEATURES="--features nightly"
99
- rust: beta
1010
- rust: stable
11+
- rust: stable
12+
env: FEATURES="--features compat"
1113
- rust: 1.15.0
1214

1315
cache:

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ base64 = "0.6"
2323
bytes = "0.4.4"
2424
futures = "0.1.14"
2525
futures-cpupool = "0.1"
26+
http = { version = "0.1", optional = true }
2627
httparse = "1.0"
2728
language-tags = "0.2"
2829
log = "0.3"
@@ -45,3 +46,4 @@ spmc = "0.2"
4546
default = []
4647
nightly = []
4748
raw_status = []
49+
compat = [ "http" ]

src/client/compat.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! Wrappers to build compatibility with the `http` crate.
2+
3+
pub use super::compat_impl::{
4+
CompatClient,
5+
CompatFutureResponse
6+
};

src/client/compat_impl.rs

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use futures::{Future, Poll, Stream};
2+
use http_types;
3+
use tokio_service::Service;
4+
5+
use client::{Connect, Client, FutureResponse};
6+
use error::Error;
7+
use http::Body;
8+
9+
/// A Client to make outgoing HTTP requests.
10+
#[derive(Debug)]
11+
pub struct CompatClient<C, B = Body> {
12+
inner: Client<C, B>
13+
}
14+
15+
pub fn client<C, B>(client: Client<C, B>) -> CompatClient<C, B> {
16+
CompatClient { inner: client }
17+
}
18+
19+
impl<C, B> Service for CompatClient<C, B>
20+
where C: Connect,
21+
B: Stream<Error=Error> + 'static,
22+
B::Item: AsRef<[u8]>,
23+
{
24+
type Request = http_types::Request<B>;
25+
type Response = http_types::Response<Body>;
26+
type Error = Error;
27+
type Future = CompatFutureResponse;
28+
29+
fn call(&self, req: Self::Request) -> Self::Future {
30+
future(self.inner.call(req.into()))
31+
}
32+
}
33+
34+
/// A `Future` that will resolve to an `http::Response`.
35+
#[must_use = "futures do nothing unless polled"]
36+
#[derive(Debug)]
37+
pub struct CompatFutureResponse {
38+
inner: FutureResponse
39+
}
40+
41+
pub fn future(fut: FutureResponse) -> CompatFutureResponse {
42+
CompatFutureResponse { inner: fut }
43+
}
44+
45+
impl Future for CompatFutureResponse {
46+
type Item = http_types::Response<Body>;
47+
type Error = Error;
48+
49+
fn poll(&mut self) -> Poll<Self::Item, Error> {
50+
self.inner.poll()
51+
.map(|a| a.map(|r| r.into()))
52+
}
53+
}

src/client/mod.rs

+19
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use std::time::Duration;
99

1010
use futures::{future, Poll, Async, Future, Stream};
1111
use futures::unsync::oneshot;
12+
#[cfg(feature = "compat")]
13+
use http_types;
1214
use tokio_io::{AsyncRead, AsyncWrite};
1315
use tokio::reactor::Handle;
1416
use tokio_proto::BindClient;
@@ -33,6 +35,10 @@ pub use self::connect::{HttpConnector, Connect};
3335
mod connect;
3436
mod dns;
3537
mod pool;
38+
#[cfg(feature = "compat")]
39+
mod compat_impl;
40+
#[cfg(feature = "compat")]
41+
pub mod compat;
3642

3743
/// A Client to make outgoing HTTP requests.
3844
// If the Connector is clone, then the Client can be clone easily.
@@ -108,6 +114,19 @@ where C: Connect,
108114
pub fn request(&self, req: Request<B>) -> FutureResponse {
109115
self.call(req)
110116
}
117+
118+
/// Send an `http::Request` using this Client.
119+
#[inline]
120+
#[cfg(feature = "compat")]
121+
pub fn request_compat(&self, req: http_types::Request<B>) -> compat::CompatFutureResponse {
122+
self::compat_impl::future(self.call(req.into()))
123+
}
124+
125+
/// Convert into a client accepting `http::Request`.
126+
#[cfg(feature = "compat")]
127+
pub fn into_compat(self) -> compat::CompatClient<C, B> {
128+
self::compat_impl::client(self)
129+
}
111130
}
112131

113132
/// A `Future` that will resolve to an HTTP Response.

src/common/str.rs

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ impl ByteStr {
2929
pub fn as_str(&self) -> &str {
3030
unsafe { str::from_utf8_unchecked(self.0.as_ref()) }
3131
}
32+
33+
#[cfg(feature = "compat")]
34+
pub fn into_bytes(self) -> Bytes {
35+
self.0
36+
}
3237
}
3338

3439
impl Deref for ByteStr {

src/header/mod.rs

+76
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,14 @@
7777
//! }
7878
//! ```
7979
use std::borrow::{Cow, ToOwned};
80+
#[cfg(feature = "compat")]
81+
use std::convert::From;
8082
use std::iter::{FromIterator, IntoIterator};
8183
use std::{mem, fmt};
8284

85+
#[cfg(feature = "compat")]
86+
use http_types;
87+
8388
use unicase::Ascii;
8489

8590
use self::internals::{Item, VecMap, Entry};
@@ -546,6 +551,54 @@ impl fmt::Debug for Headers {
546551
}
547552
}
548553

554+
#[cfg(feature = "compat")]
555+
impl From<http_types::HeaderMap> for Headers {
556+
fn from(mut header_map: http_types::HeaderMap) -> Headers {
557+
let mut headers = Headers::new();
558+
for (name, mut value_drain) in header_map.drain() {
559+
if let Some(first_value) = value_drain.next() {
560+
let mut raw: Raw = first_value.as_bytes().into();
561+
for value in value_drain {
562+
raw.push(value.as_bytes());
563+
}
564+
headers.append_raw(name.as_str().to_string(), raw);
565+
}
566+
}
567+
headers
568+
}
569+
}
570+
571+
#[cfg(feature = "compat")]
572+
impl From<Headers> for http_types::HeaderMap {
573+
fn from(headers: Headers) -> http_types::HeaderMap {
574+
let mut header_map = http_types::HeaderMap::new();
575+
for header in headers.iter() {
576+
let entry = header_map.entry(header.name())
577+
.expect("attempted to convert invalid header name");
578+
let mut value_iter = header.raw().iter().map(|line| {
579+
http_types::header::HeaderValue::from_bytes(line)
580+
.expect("attempted to convert invalid header value")
581+
});
582+
match entry {
583+
http_types::header::Entry::Occupied(mut occupied) => {
584+
for value in value_iter {
585+
occupied.append(value);
586+
}
587+
},
588+
http_types::header::Entry::Vacant(vacant) => {
589+
if let Some(first_value) = value_iter.next() {
590+
let mut occupied = vacant.insert_entry(first_value);
591+
for value in value_iter {
592+
occupied.append(value);
593+
}
594+
}
595+
}
596+
}
597+
}
598+
header_map
599+
}
600+
}
601+
549602
/// An `Iterator` over the fields in a `Headers` map.
550603
#[allow(missing_debug_implementations)]
551604
pub struct HeadersItems<'a> {
@@ -940,6 +993,29 @@ mod tests {
940993
assert_ne!(headers1, headers2);
941994
}
942995

996+
#[test]
997+
#[cfg(feature = "compat")]
998+
fn test_compat() {
999+
use http_types;
1000+
1001+
let mut orig_hyper_headers = Headers::new();
1002+
orig_hyper_headers.set(ContentLength(11));
1003+
orig_hyper_headers.set(Host::new("foo.bar", None));
1004+
orig_hyper_headers.append_raw("x-foo", b"bar".to_vec());
1005+
orig_hyper_headers.append_raw("x-foo", b"quux".to_vec());
1006+
1007+
let mut orig_http_headers = http_types::HeaderMap::new();
1008+
orig_http_headers.insert(http_types::header::CONTENT_LENGTH, "11".parse().unwrap());
1009+
orig_http_headers.insert(http_types::header::HOST, "foo.bar".parse().unwrap());
1010+
orig_http_headers.append("x-foo", "bar".parse().unwrap());
1011+
orig_http_headers.append("x-foo", "quux".parse().unwrap());
1012+
1013+
let conv_hyper_headers: Headers = orig_http_headers.clone().into();
1014+
let conv_http_headers: http_types::HeaderMap = orig_hyper_headers.clone().into();
1015+
assert_eq!(orig_hyper_headers, conv_hyper_headers);
1016+
assert_eq!(orig_http_headers, conv_http_headers);
1017+
}
1018+
9431019
#[cfg(feature = "nightly")]
9441020
#[bench]
9451021
fn bench_headers_new(b: &mut Bencher) {

src/http/request.rs

+36-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
use std::fmt;
2+
#[cfg(feature = "compat")]
3+
use std::mem::replace;
4+
use std::net::SocketAddr;
5+
6+
#[cfg(feature = "compat")]
7+
use http_types;
28

39
use header::Headers;
410
use http::{Body, MessageHead, RequestHead, RequestLine};
511
use method::Method;
612
use uri::{self, Uri};
713
use version::HttpVersion;
8-
use std::net::SocketAddr;
914

1015
/// An HTTP Request
1116
pub struct Request<B = Body> {
@@ -132,6 +137,36 @@ impl<B> fmt::Debug for Request<B> {
132137
}
133138
}
134139

140+
#[cfg(feature = "compat")]
141+
impl From<Request> for http_types::Request<Body> {
142+
fn from(from_req: Request) -> http_types::Request<Body> {
143+
let (m, u, v, h, b) = from_req.deconstruct();
144+
145+
let to_req = http_types::Request::new(());
146+
let (mut to_parts, _) = to_req.into_parts();
147+
148+
to_parts.method = m.into();
149+
to_parts.uri = u.into();
150+
to_parts.version = v.into();
151+
to_parts.headers = h.into();
152+
153+
http_types::Request::from_parts(to_parts, b)
154+
}
155+
}
156+
157+
#[cfg(feature = "compat")]
158+
impl<B> From<http_types::Request<B>> for Request<B> {
159+
fn from(from_req: http_types::Request<B>) -> Request<B> {
160+
let (from_parts, body) = from_req.into_parts();
161+
162+
let mut to_req = Request::new(from_parts.method.into(), from_parts.uri.into());
163+
to_req.set_version(from_parts.version.into());
164+
replace(to_req.headers_mut(), from_parts.headers.into());
165+
to_req.set_body(body);
166+
to_req
167+
}
168+
}
169+
135170
/// Constructs a request using a received ResponseHead and optional body
136171
pub fn from_wire<B>(addr: Option<SocketAddr>, incoming: RequestHead, body: B) -> Request<B> {
137172
let MessageHead { version, subject: RequestLine(method, uri), headers } = incoming;

src/http/response.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
use std::fmt;
2+
#[cfg(feature = "compat")]
3+
use std::mem::replace;
4+
5+
#[cfg(feature = "compat")]
6+
use http_types;
27

38
use header::{Header, Headers};
49
use http::{MessageHead, ResponseHead, Body};
@@ -142,6 +147,30 @@ impl fmt::Debug for Response {
142147
}
143148
}
144149

150+
#[cfg(feature = "compat")]
151+
impl<B> From<http_types::Response<B>> for Response<B> {
152+
fn from(from_res: http_types::Response<B>) -> Response<B> {
153+
let (from_parts, body) = from_res.into_parts();
154+
let mut to_res = Response::new();
155+
to_res.version = from_parts.version.into();
156+
to_res.set_status(from_parts.status.into());
157+
replace(to_res.headers_mut(), from_parts.headers.into());
158+
to_res.with_body(body)
159+
}
160+
}
161+
162+
#[cfg(feature = "compat")]
163+
impl From<Response> for http_types::Response<Body> {
164+
fn from(mut from_res: Response) -> http_types::Response<Body> {
165+
let (mut to_parts, ()) = http_types::Response::new(()).into_parts();
166+
to_parts.version = from_res.version().into();
167+
to_parts.status = from_res.status().into();
168+
let from_headers = replace(from_res.headers_mut(), Headers::new());
169+
to_parts.headers = from_headers.into();
170+
http_types::Response::from_parts(to_parts, from_res.body())
171+
}
172+
}
173+
145174
/// Constructs a response using a received ResponseHead and optional body
146175
#[inline]
147176
#[cfg(not(feature = "raw_status"))]

src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ extern crate base64;
2121
extern crate bytes;
2222
#[macro_use] extern crate futures;
2323
extern crate futures_cpupool;
24+
#[cfg(feature = "compat")]
25+
extern crate http as http_types;
2426
extern crate httparse;
2527
extern crate language_tags;
2628
#[macro_use] extern crate log;

0 commit comments

Comments
 (0)