Skip to content

Commit 46012fc

Browse files
authored
Hyper 0.13 support
Fixes #9
1 parent 12fbe98 commit 46012fc

File tree

5 files changed

+251
-120
lines changed

5 files changed

+251
-120
lines changed

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,18 @@ license = "MIT/Apache-2.0"
77
documentation = "https://github.com/hjr3/hyper-timeout"
88
homepage = "https://github.com/hjr3/hyper-timeout"
99
repository = "https://github.com/hjr3/hyper-timeout"
10+
edition = "2018"
1011

1112
[badges]
1213
travis-ci = { repository = "https://github.com/hjr3/hyper-timeout", branch = "master" }
1314

1415
[dependencies]
15-
futures = "0.1"
16-
hyper = "0.12"
17-
tokio = "0.1"
16+
bytes = "0.5"
17+
hyper = { version = "0.13", default-features = false, features = ["tcp"] }
18+
tokio = "0.2"
1819
tokio-io = "0.1"
19-
tokio-io-timeout = "0.3"
20-
tokio-reactor = "0.1"
21-
tokio-service = "0.1"
20+
tokio-io-timeout = "0.4"
2221

2322
[dev-dependencies]
24-
hyper-tls = "0.3"
23+
hyper-tls = "0.4"
24+
tokio = { version = "0.2", features = ["io-std", "macros"] }

README.md

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,16 @@ There is a `TimeoutConnector` that implements the `hyper::Connect` trait. This c
1919

2020
Hyper version compatibility:
2121

22-
* The `0.1` release supports hyper 0.11.
22+
* The `master` branch will track on going development for hyper.
23+
* The `0.3` release supports hyper 0.13.
2324
* The `0.2` release supports hyper 0.12.
24-
* The `master` branch will track on going developer for hyper 0.13.
25+
* The `0.1` release supports hyper 0.11.
2526

26-
First, (assuming you are using hyper 0.12) add this to your `Cargo.toml`:
27+
First, (assuming you are using hyper 0.13) add this to your `Cargo.toml`:
2728

2829
```toml
2930
[dependencies]
30-
hyper-timeout = "0.2"
31-
```
32-
33-
Next, add this to your crate:
34-
35-
```rust
36-
extern crate hyper_timeout;
31+
hyper-timeout = "0.3"
3732
```
3833

3934
See the [client example](./examples/client.rs) for a working example.

examples/client.rs

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,46 @@
1-
extern crate futures;
2-
extern crate hyper;
3-
extern crate hyper_tls;
4-
extern crate hyper_timeout;
5-
61
use std::env;
7-
use std::io::{self, Write};
82
use std::time::Duration;
93

10-
use futures::Future;
11-
use futures::stream::Stream;
12-
13-
use hyper::{rt, Client};
4+
use hyper::{Client, body::HttpBody as _};
5+
use tokio::io::{self, AsyncWriteExt as _};
146

157
//use hyper::client::HttpConnector;
168
use hyper_tls::HttpsConnector;
179

1810
use hyper_timeout::TimeoutConnector;
1911

20-
fn main() {
21-
12+
#[tokio::main]
13+
async fn main() -> Result<(), Box<dyn std::error::Error>> {
2214
let url = match env::args().nth(1) {
2315
Some(url) => url,
2416
None => {
2517
println!("Usage: client <url>");
2618
println!("Example: client https://example.com");
27-
return;
19+
return Ok(());
2820
}
2921
};
3022

3123
let url = url.parse::<hyper::Uri>().unwrap();
3224

33-
rt::run(rt::lazy(|| {
34-
// This example uses `HttpsConnector`, but you can also use hyper `HttpConnector`
35-
//let connector = HttpConnector::new(1);
36-
let https = HttpsConnector::new(1).unwrap();
37-
let mut connector = TimeoutConnector::new(https);
38-
connector.set_connect_timeout(Some(Duration::from_secs(5)));
39-
connector.set_read_timeout(Some(Duration::from_secs(5)));
40-
connector.set_write_timeout(Some(Duration::from_secs(5)));
41-
let client = Client::builder().build::<_, hyper::Body>(connector);
42-
43-
client.get(url).and_then(|res| {
44-
println!("Response: {}", res.status());
45-
46-
res
47-
.into_body()
48-
// Body is a stream, so as each chunk arrives...
49-
.for_each(|chunk| {
50-
io::stdout()
51-
.write_all(&chunk)
52-
.map_err(|e| {
53-
panic!("example expects stdout is open, error={}", e)
54-
})
55-
})
56-
})
57-
.map_err(|err| {
58-
println!("Error: {}", err);
59-
})
60-
}));
25+
// This example uses `HttpsConnector`, but you can also use hyper `HttpConnector`
26+
//let http = HttpConnector::new();
27+
let https = HttpsConnector::new();
28+
let mut connector = TimeoutConnector::new(https);
29+
connector.set_connect_timeout(Some(Duration::from_secs(5)));
30+
connector.set_read_timeout(Some(Duration::from_secs(5)));
31+
connector.set_write_timeout(Some(Duration::from_secs(5)));
32+
let client = Client::builder().build::<_, hyper::Body>(connector);
33+
34+
let mut res = client.get(url).await?;
35+
36+
println!("Status: {}", res.status());
37+
println!("Headers:\n{:#?}", res.headers());
38+
39+
while let Some(chunk) = res.body_mut().data().await {
40+
let chunk = chunk?;
41+
io::stdout()
42+
.write_all(&chunk)
43+
.await?
44+
}
45+
Ok(())
6146
}

src/lib.rs

Lines changed: 79 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
1-
extern crate futures;
2-
extern crate tokio;
3-
extern crate tokio_io;
4-
extern crate tokio_service;
5-
extern crate tokio_io_timeout;
6-
extern crate hyper;
7-
8-
use std::time::Duration;
1+
use std::future::Future;
92
use std::io;
3+
use std::pin::Pin;
4+
use std::task::{Context, Poll};
5+
use std::time::Duration;
106

11-
use futures::Future;
12-
13-
use tokio::timer::Timeout;
7+
use tokio::io::{AsyncRead, AsyncWrite};
8+
use tokio::time::timeout;
149
use tokio_io_timeout::TimeoutStream;
1510

16-
use hyper::client::connect::{Connect, Connected, Destination};
11+
use hyper::{service::Service, Uri};
12+
use hyper::client::connect::{Connect, Connected, Connection};
13+
14+
mod stream;
15+
16+
use stream::TimeoutConnectorStream;
17+
18+
type BoxError = Box<dyn std::error::Error + Send + Sync>;
1719

1820
/// A connector that enforces as connection timeout
19-
#[derive(Debug)]
21+
#[derive(Debug, Clone)]
2022
pub struct TimeoutConnector<T> {
2123
/// A connector implementing the `Connect` trait
2224
connector: T,
@@ -40,42 +42,57 @@ impl<T: Connect> TimeoutConnector<T> {
4042
}
4143
}
4244

43-
impl<T: Connect> Connect for TimeoutConnector<T>
45+
impl<T> Service<Uri> for TimeoutConnector<T>
4446
where
45-
T: Connect<Error = io::Error> + 'static,
46-
T::Future: 'static,
47+
T: Service<Uri>,
48+
T::Response: AsyncRead + AsyncWrite + Send + Unpin,
49+
T::Future: Send + 'static,
50+
T::Error: Into<BoxError>,
4751
{
48-
type Transport = TimeoutStream<T::Transport>;
49-
type Error = T::Error;
50-
type Future = Box<Future<Item = (Self::Transport, Connected), Error = Self::Error> + Send>;
51-
52-
fn connect(&self, dst: Destination) -> Self::Future {
52+
type Response = TimeoutConnectorStream<T::Response>;
53+
type Error = BoxError;
54+
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
55+
56+
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
57+
match self.connector.poll_ready(cx) {
58+
Poll::Ready(Ok(())) => Poll::Ready(Ok(())),
59+
Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
60+
Poll::Pending => Poll::Pending,
61+
}
62+
}
5363

64+
fn call(&mut self, dst: Uri) -> Self::Future {
5465
let read_timeout = self.read_timeout.clone();
5566
let write_timeout = self.write_timeout.clone();
56-
let connecting = self.connector.connect(dst);
67+
let connecting = self.connector.call(dst);
5768

5869
if self.connect_timeout.is_none() {
59-
return Box::new(connecting.map(move |(io, c)| {
60-
let mut tm = TimeoutStream::new(io);
70+
let fut = async move {
71+
let io = connecting.await.map_err(Into::into)?;
72+
73+
let mut tm = TimeoutConnectorStream::new(TimeoutStream::new(io));
6174
tm.set_read_timeout(read_timeout);
6275
tm.set_write_timeout(write_timeout);
63-
(tm, c)
64-
}));
76+
Ok(tm)
77+
};
78+
79+
return Box::pin(fut);
6580
}
6681

6782
let connect_timeout = self.connect_timeout.expect("Connect timeout should be set");
68-
let timeout = Timeout::new(connecting, connect_timeout);
83+
let timeout = timeout(connect_timeout, connecting);
6984

70-
Box::new(timeout.then(move |res| match res {
71-
Ok((io, c)) => {
72-
let mut tm = TimeoutStream::new(io);
73-
tm.set_read_timeout(read_timeout);
74-
tm.set_write_timeout(write_timeout);
75-
Ok((tm, c))
76-
}
77-
Err(e) => Err(io::Error::new(io::ErrorKind::TimedOut, e)),
78-
}))
85+
let fut = async move {
86+
let connecting = timeout.await.map_err(|e| io::Error::new(io::ErrorKind::TimedOut, e))?;
87+
let io = connecting.map_err(Into::into)?;
88+
89+
let mut tm = TimeoutConnectorStream::new(TimeoutStream::new(io));
90+
tm.set_read_timeout(read_timeout);
91+
tm.set_write_timeout(write_timeout);
92+
Ok(tm)
93+
};
94+
95+
Box::pin(fut)
7996
}
8097
}
8198

@@ -105,32 +122,35 @@ impl<T> TimeoutConnector<T> {
105122
}
106123
}
107124

125+
impl<T> Connection for TimeoutConnector<T> {
126+
fn connected(&self) -> Connected {
127+
Connected::new()
128+
}
129+
}
130+
108131
#[cfg(test)]
109132
mod tests {
110133
use std::error::Error;
111134
use std::io;
112135
use std::time::Duration;
113-
use futures::future;
114-
use tokio::runtime::current_thread::Runtime;
136+
115137
use hyper::Client;
116138
use hyper::client::HttpConnector;
139+
117140
use super::TimeoutConnector;
118141

119-
#[test]
120-
fn test_timeout_connector() {
121-
let mut rt = Runtime::new().unwrap();
122-
let res = rt.block_on(future::lazy(|| {
123-
// 10.255.255.1 is a not a routable IP address
124-
let url = "http://10.255.255.1".parse().unwrap();
142+
#[tokio::test]
143+
async fn test_timeout_connector() {
144+
// 10.255.255.1 is a not a routable IP address
145+
let url = "http://10.255.255.1".parse().unwrap();
125146

126-
let http = HttpConnector::new(1);
127-
let mut connector = TimeoutConnector::new(http);
128-
connector.set_connect_timeout(Some(Duration::from_millis(1)));
147+
let http = HttpConnector::new();
148+
let mut connector = TimeoutConnector::new(http);
149+
connector.set_connect_timeout(Some(Duration::from_millis(1)));
129150

130-
let client = Client::builder().build::<_, hyper::Body>(connector);
151+
let client = Client::builder().build::<_, hyper::Body>(connector);
131152

132-
client.get(url)
133-
}));
153+
let res = client.get(url).await;
134154

135155
match res {
136156
Ok(_) => panic!("Expected a timeout"),
@@ -144,21 +164,18 @@ mod tests {
144164
}
145165
}
146166

147-
#[test]
148-
fn test_read_timeout() {
149-
let mut rt = Runtime::new().unwrap();
150-
let res = rt.block_on(future::lazy(|| {
151-
let url = "http://example.com".parse().unwrap();
167+
#[tokio::test]
168+
async fn test_read_timeout() {
169+
let url = "http://example.com".parse().unwrap();
152170

153-
let http = HttpConnector::new(1);
154-
let mut connector = TimeoutConnector::new(http);
155-
// A 1 ms read timeout should be so short that we trigger a timeout error
156-
connector.set_read_timeout(Some(Duration::from_millis(1)));
171+
let http = HttpConnector::new();
172+
let mut connector = TimeoutConnector::new(http);
173+
// A 1 ms read timeout should be so short that we trigger a timeout error
174+
connector.set_read_timeout(Some(Duration::from_millis(1)));
157175

158-
let client = Client::builder().build::<_, hyper::Body>(connector);
176+
let client = Client::builder().build::<_, hyper::Body>(connector);
159177

160-
client.get(url)
161-
}));
178+
let res = client.get(url).await;
162179

163180
match res {
164181
Ok(_) => panic!("Expected a timeout"),

0 commit comments

Comments
 (0)