Skip to content

Commit 24f6fca

Browse files
allan2hjr3
authored andcommitted
feat: Update to hyper 1.0
Fixes #24. hyper has its own IO traits. The dependency on tokio-io-timeout is dropped. Its types are implemented here, implementing hyper IO traits instead of tokio. - `tokio::io::AsyncRead` is switched to `hyper::rt::Read`, and AsyncWrite to `hyper::rt::Write`. - `tokio::io::AsyncSeek` is no longer needed. The wrapper type `TimeoutConnectorStream` that formerly wrapped tokio_io_timeout::TimeoutStream` is no longer needed. `tokio::io` has the handy AsyncReadExt and AsyncWriteExt traits, which were preivously used in tests. Now that we rely on Hyper IO, those had to be ported over: - AsyncReadExt -> ReadExt - AsyncWriteExt -> WriteExt - tokio internal Read -> ReadFut - tokio internal Write -> WriteFut
1 parent 92d5610 commit 24f6fca

File tree

5 files changed

+688
-100
lines changed

5 files changed

+688
-100
lines changed

Cargo.toml

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hyper-timeout"
3-
version = "0.4.1"
3+
version = "0.5.0"
44
authors = ["Herman J. Radtke III <[email protected]>"]
55
edition = "2018"
66
description = "A connect, read and write timeout aware connector to be used with hyper Client."
@@ -11,12 +11,14 @@ repository = "https://github.com/hjr3/hyper-timeout"
1111
readme = "README.md"
1212

1313
[dependencies]
14-
hyper = { version = "0.14.2", features = ["client"] }
14+
hyper = "1.0"
15+
hyper-util = { version = "0.1", features = ["client-legacy", "http1"] }
1516
pin-project-lite = "0.2"
16-
tokio = "1.0.0"
17-
tokio-io-timeout = "1.1.0"
17+
tokio = { version = "1.34.0" }
18+
tower-service = "0.3"
1819

1920
[dev-dependencies]
20-
hyper = { version = "0.14", features = ["client", "http1", "tcp"] }
21-
hyper-tls = "0.5"
2221
tokio = { version = "1.0.0", features = ["io-std", "io-util", "macros"] }
22+
hyper = { version = "1.0", features = ["http1"] }
23+
hyper-tls = "0.6"
24+
http-body-util = "0.1"

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,17 @@ There is a `TimeoutConnector` that implements the `hyper::Connect` trait. This c
1919
Hyper version compatibility:
2020

2121
* The `master` branch will track on going development for hyper.
22+
* The `0.5` release supports hyper 1.0.
2223
* The `0.4` release supports hyper 0.14.
2324
* The `0.3` release supports hyper 0.13.
2425
* The `0.2` release supports hyper 0.12.
2526
* The `0.1` release supports hyper 0.11.
2627

27-
Assuming you are using hyper 0.14, add this to your `Cargo.toml`:
28+
Assuming you are using hyper 1.0, add this to your `Cargo.toml`:
2829

2930
```toml
3031
[dependencies]
31-
hyper-timeout = "0.4"
32+
hyper-timeout = "0.5"
3233
```
3334

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

examples/client.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use std::env;
22
use std::time::Duration;
33

4-
use hyper::{body::HttpBody as _, Client};
5-
use tokio::io::{self, AsyncWriteExt as _};
4+
use http_body_util::{BodyExt, Empty};
5+
use hyper::body::Bytes;
6+
use hyper_util::{client::legacy::Client, rt::TokioExecutor};
7+
use tokio::io::{self, AsyncWriteExt};
68

79
use hyper_tls::HttpsConnector;
810

@@ -22,22 +24,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
2224
let url = url.parse::<hyper::Uri>().unwrap();
2325

2426
// This example uses `HttpsConnector`, but you can also use hyper `HttpConnector`
25-
//let h = hyper::client::HttpConnector::new();
27+
//let h = hyper_util::client::legacy::connect::HttpConnector::new();
2628
let h = HttpsConnector::new();
2729
let mut connector = TimeoutConnector::new(h);
2830
connector.set_connect_timeout(Some(Duration::from_secs(5)));
2931
connector.set_read_timeout(Some(Duration::from_secs(5)));
3032
connector.set_write_timeout(Some(Duration::from_secs(5)));
31-
let client = Client::builder().build::<_, hyper::Body>(connector);
33+
let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(connector);
3234

3335
let mut res = client.get(url).await?;
3436

3537
println!("Status: {}", res.status());
3638
println!("Headers:\n{:#?}", res.headers());
3739

38-
while let Some(chunk) = res.body_mut().data().await {
39-
let chunk = chunk?;
40-
io::stdout().write_all(&chunk).await?
40+
while let Some(frame) = res.body_mut().frame().await {
41+
let bytes = frame?
42+
.into_data()
43+
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Error when consuming frame"))?;
44+
io::stdout().write_all(&bytes).await?;
4145
}
46+
4247
Ok(())
4348
}

src/lib.rs

+29-31
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@ use std::pin::Pin;
44
use std::task::{Context, Poll};
55
use std::time::Duration;
66

7-
use tokio::io::{AsyncRead, AsyncWrite};
7+
use hyper::rt::{Read, Write};
88
use tokio::time::timeout;
9-
use tokio_io_timeout::TimeoutStream;
109

11-
use hyper::client::connect::{Connected, Connection};
12-
use hyper::{service::Service, Uri};
10+
use hyper::Uri;
11+
use hyper_util::client::legacy::connect::{Connected, Connection};
12+
use tower_service::Service;
1313

1414
mod stream;
15-
16-
use stream::TimeoutConnectorStream;
15+
use stream::TimeoutStream;
1716

1817
type BoxError = Box<dyn std::error::Error + Send + Sync>;
1918

20-
/// A connector that enforces as connection timeout
19+
/// A connector that enforces a connection timeout
2120
#[derive(Debug, Clone)]
2221
pub struct TimeoutConnector<T> {
2322
/// A connector implementing the `Connect` trait
@@ -33,7 +32,7 @@ pub struct TimeoutConnector<T> {
3332
impl<T> TimeoutConnector<T>
3433
where
3534
T: Service<Uri> + Send,
36-
T::Response: AsyncRead + AsyncWrite + Send + Unpin,
35+
T::Response: Read + Write + Send + Unpin,
3736
T::Future: Send + 'static,
3837
T::Error: Into<BoxError>,
3938
{
@@ -51,11 +50,11 @@ where
5150
impl<T> Service<Uri> for TimeoutConnector<T>
5251
where
5352
T: Service<Uri> + Send,
54-
T::Response: AsyncRead + AsyncWrite + Connection + Send + Unpin,
53+
T::Response: Read + Write + Connection + Send + Unpin,
5554
T::Future: Send + 'static,
5655
T::Error: Into<BoxError>,
5756
{
58-
type Response = Pin<Box<TimeoutConnectorStream<T::Response>>>;
57+
type Response = Pin<Box<TimeoutStream<T::Response>>>;
5958
type Error = BoxError;
6059
#[allow(clippy::type_complexity)]
6160
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
@@ -71,7 +70,7 @@ where
7170
let connecting = self.connector.call(dst);
7271

7372
let fut = async move {
74-
let stream = match connect_timeout {
73+
let mut stream = match connect_timeout {
7574
None => {
7675
let io = connecting.await.map_err(Into::into)?;
7776
TimeoutStream::new(io)
@@ -85,11 +84,9 @@ where
8584
TimeoutStream::new(io)
8685
}
8786
};
88-
89-
let mut tm = TimeoutConnectorStream::new(stream);
90-
tm.set_read_timeout(read_timeout);
91-
tm.set_write_timeout(write_timeout);
92-
Ok(Box::pin(tm))
87+
stream.set_read_timeout(read_timeout);
88+
stream.set_write_timeout(write_timeout);
89+
Ok(Box::pin(stream))
9390
};
9491

9592
Box::pin(fut)
@@ -124,8 +121,8 @@ impl<T> TimeoutConnector<T> {
124121

125122
impl<T> Connection for TimeoutConnector<T>
126123
where
127-
T: AsyncRead + AsyncWrite + Connection + Service<Uri> + Send + Unpin,
128-
T::Response: AsyncRead + AsyncWrite + Send + Unpin,
124+
T: Read + Write + Connection + Service<Uri> + Send + Unpin,
125+
T::Response: Read + Write + Send + Unpin,
129126
T::Future: Send + 'static,
130127
T::Error: Into<BoxError>,
131128
{
@@ -136,12 +133,15 @@ where
136133

137134
#[cfg(test)]
138135
mod tests {
139-
use std::error::Error;
140-
use std::io;
141136
use std::time::Duration;
137+
use std::{error::Error, io};
142138

143-
use hyper::client::HttpConnector;
144-
use hyper::Client;
139+
use http_body_util::Empty;
140+
use hyper::body::Bytes;
141+
use hyper_util::{
142+
client::legacy::{connect::HttpConnector, Client},
143+
rt::TokioExecutor,
144+
};
145145

146146
use super::TimeoutConnector;
147147

@@ -154,7 +154,7 @@ mod tests {
154154
let mut connector = TimeoutConnector::new(http);
155155
connector.set_connect_timeout(Some(Duration::from_millis(1)));
156156

157-
let client = Client::builder().build::<_, hyper::Body>(connector);
157+
let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(connector);
158158

159159
let res = client.get(url).await;
160160

@@ -179,19 +179,17 @@ mod tests {
179179
// A 1 ms read timeout should be so short that we trigger a timeout error
180180
connector.set_read_timeout(Some(Duration::from_millis(1)));
181181

182-
let client = Client::builder().build::<_, hyper::Body>(connector);
182+
let client = Client::builder(TokioExecutor::new()).build::<_, Empty<Bytes>>(connector);
183183

184184
let res = client.get(url).await;
185185

186-
match res {
187-
Ok(_) => panic!("Expected a timeout"),
188-
Err(e) => {
189-
if let Some(io_e) = e.source().unwrap().downcast_ref::<io::Error>() {
190-
assert_eq!(io_e.kind(), io::ErrorKind::TimedOut);
191-
} else {
192-
panic!("Expected timeout error");
186+
if let Err(client_e) = res {
187+
if let Some(hyper_e) = client_e.source() {
188+
if let Some(io_e) = hyper_e.source().unwrap().downcast_ref::<io::Error>() {
189+
return assert_eq!(io_e.kind(), io::ErrorKind::TimedOut);
193190
}
194191
}
195192
}
193+
panic!("Expected timeout error");
196194
}
197195
}

0 commit comments

Comments
 (0)