feat(error): revamp hyper::Error type
**The `Error` is now an opaque struct**, which allows for more variants to be added freely, and the internal representation to change without being breaking changes. For inspecting an `Error`, there are several `is_*` methods to check for certain classes of errors, such as `Error::is_parse()`. The `cause` can also be inspected, like before. This likely seems like a downgrade, but more inspection can be added as needed! The `Error` now knows about more states, which gives much more context around when a certain error occurs. This is also expressed in the description and `fmt` messages. **Most places where a user would provide an error to hyper can now pass any error type** (`E: Into<Box<std::error::Error>>`). This error is passed back in relevant places, and can be useful for logging. This should make it much clearer about what error a user should provide to hyper: any it feels is relevant! Closes #1128 Closes #1130 Closes #1431 Closes #1338 BREAKING CHANGE: `Error` is no longer an enum to pattern match over, or to construct. Code will need to be updated accordingly. For body streams or `Service`s, inference might be unable to determine what error type you mean to return. Starting in Rust 1.26, you could just label that as `!` if you never return an error.
This commit is contained in:
106
tests/client.rs
106
tests/client.rs
@@ -8,7 +8,7 @@ extern crate tokio;
|
||||
extern crate tokio_io;
|
||||
extern crate pretty_env_logger;
|
||||
|
||||
use std::io::{self, Read, Write};
|
||||
use std::io::{Read, Write};
|
||||
use std::net::{SocketAddr, TcpListener};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
@@ -142,7 +142,7 @@ macro_rules! test {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
let runtime = Runtime::new().expect("runtime new");
|
||||
|
||||
let err = test! {
|
||||
let err: ::hyper::Error = test! {
|
||||
INNER;
|
||||
name: $name,
|
||||
runtime: &runtime,
|
||||
@@ -157,7 +157,11 @@ macro_rules! test {
|
||||
headers: { $($request_header_name => $request_header_val,)* },
|
||||
body: $request_body,
|
||||
}.unwrap_err();
|
||||
if !$err(&err) {
|
||||
|
||||
fn infer_closure<F: FnOnce(&::hyper::Error) -> bool>(f: F) -> F { f }
|
||||
|
||||
let closure = infer_closure($err);
|
||||
if !closure(&err) {
|
||||
panic!("expected error, unexpected variant: {:?}", err)
|
||||
}
|
||||
}
|
||||
@@ -228,7 +232,7 @@ macro_rules! test {
|
||||
let _ = tx.send(());
|
||||
}).expect("thread spawn");
|
||||
|
||||
let rx = rx.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx.expect("thread panicked");
|
||||
|
||||
res.join(rx).map(|r| r.0).wait()
|
||||
});
|
||||
@@ -485,10 +489,7 @@ test! {
|
||||
url: "http://{addr}/err",
|
||||
headers: {},
|
||||
body: None,
|
||||
error: |err| match err {
|
||||
&hyper::Error::Incomplete => true,
|
||||
_ => false,
|
||||
},
|
||||
error: |err| err.to_string() == "message is incomplete",
|
||||
}
|
||||
|
||||
test! {
|
||||
@@ -511,10 +512,8 @@ test! {
|
||||
url: "http://{addr}/err",
|
||||
headers: {},
|
||||
body: None,
|
||||
error: |err| match err {
|
||||
&hyper::Error::Version => true,
|
||||
_ => false,
|
||||
},
|
||||
// should get a Parse(Version) error
|
||||
error: |err| err.is_parse(),
|
||||
|
||||
}
|
||||
|
||||
@@ -574,10 +573,7 @@ test! {
|
||||
url: "http://{addr}/upgrade",
|
||||
headers: {},
|
||||
body: None,
|
||||
error: |err| match err {
|
||||
&hyper::Error::Upgrade => true,
|
||||
_ => false,
|
||||
},
|
||||
error: |err| err.to_string() == "unsupported protocol upgrade",
|
||||
|
||||
}
|
||||
|
||||
@@ -599,10 +595,7 @@ test! {
|
||||
url: "http://{addr}/",
|
||||
headers: {},
|
||||
body: None,
|
||||
error: |err| match err {
|
||||
&hyper::Error::Method => true,
|
||||
_ => false,
|
||||
},
|
||||
error: |err| err.is_user(),
|
||||
|
||||
}
|
||||
|
||||
@@ -689,9 +682,9 @@ mod dispatch_impl {
|
||||
let res = client.request(req).and_then(move |res| {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
Delay::new(Duration::from_secs(1))
|
||||
.from_err()
|
||||
.expect("timeout")
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
closes.into_future().wait().unwrap().0.expect("closes");
|
||||
@@ -736,11 +729,11 @@ mod dispatch_impl {
|
||||
res.into_body().into_stream().concat2()
|
||||
}).and_then(|_| {
|
||||
Delay::new(Duration::from_secs(1))
|
||||
.from_err()
|
||||
.expect("timeout")
|
||||
})
|
||||
};
|
||||
// client is dropped
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
closes.into_future().wait().unwrap().0.expect("closes");
|
||||
@@ -788,7 +781,7 @@ mod dispatch_impl {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
// not closed yet, just idle
|
||||
@@ -904,7 +897,7 @@ mod dispatch_impl {
|
||||
client.request(req)
|
||||
};
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
let t = Delay::new(Duration::from_millis(100))
|
||||
@@ -955,7 +948,7 @@ mod dispatch_impl {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
let t = Delay::new(Duration::from_millis(100))
|
||||
@@ -1003,10 +996,9 @@ mod dispatch_impl {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
|
||||
let t = Delay::new(Duration::from_millis(100))
|
||||
.map(|_| panic!("time out"));
|
||||
let close = closes.into_future()
|
||||
@@ -1049,13 +1041,12 @@ mod dispatch_impl {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
|
||||
let t = Delay::new(Duration::from_millis(100))
|
||||
.map(|_| panic!("time out"));
|
||||
let close = closes.into_future()
|
||||
@@ -1129,7 +1120,7 @@ mod dispatch_impl {
|
||||
|
||||
assert_eq!(connects.load(Ordering::SeqCst), 0);
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
.body(Body::empty())
|
||||
@@ -1143,7 +1134,7 @@ mod dispatch_impl {
|
||||
// state and back into client pool
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
|
||||
let rx = rx2.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx2.expect("thread panicked");
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/b", addr))
|
||||
.body(Body::empty())
|
||||
@@ -1194,7 +1185,7 @@ mod dispatch_impl {
|
||||
|
||||
assert_eq!(connects.load(Ordering::Relaxed), 0);
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
let req = Request::builder()
|
||||
.method("HEAD")
|
||||
.uri(&*format!("http://{}/a", addr))
|
||||
@@ -1205,7 +1196,7 @@ mod dispatch_impl {
|
||||
|
||||
assert_eq!(connects.load(Ordering::Relaxed), 1);
|
||||
|
||||
let rx = rx2.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx2.expect("thread panicked");
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/b", addr))
|
||||
.body(Body::empty())
|
||||
@@ -1246,7 +1237,7 @@ mod dispatch_impl {
|
||||
});
|
||||
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
let req = Request::builder()
|
||||
.uri(&*format!("http://{}/foo/bar", addr))
|
||||
.body(Body::empty())
|
||||
@@ -1354,7 +1345,7 @@ mod conn {
|
||||
use hyper::{self, Request};
|
||||
use hyper::client::conn;
|
||||
|
||||
use super::{s, tcp_connect};
|
||||
use super::{s, tcp_connect, FutureHyperExt};
|
||||
|
||||
#[test]
|
||||
fn get() {
|
||||
@@ -1395,10 +1386,10 @@ mod conn {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
}
|
||||
|
||||
@@ -1441,10 +1432,10 @@ mod conn {
|
||||
assert_eq!(res.status(), hyper::StatusCode::OK);
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
res.join(rx).map(|r| r.0).wait().unwrap();
|
||||
}
|
||||
|
||||
@@ -1490,17 +1481,14 @@ mod conn {
|
||||
let res2 = client.send_request(req)
|
||||
.then(|result| {
|
||||
let err = result.expect_err("res2");
|
||||
match err {
|
||||
hyper::Error::Cancel(..) => (),
|
||||
other => panic!("expected Cancel, found {:?}", other),
|
||||
}
|
||||
assert!(err.is_canceled(), "err not canceled, {:?}", err);
|
||||
Ok(())
|
||||
});
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
res1.join(res2).join(rx).map(|r| r.0).wait().unwrap();
|
||||
}
|
||||
|
||||
@@ -1558,10 +1546,10 @@ mod conn {
|
||||
res.into_body().into_stream().concat2()
|
||||
});
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
until_upgrade.join(res).join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
// should not be ready now
|
||||
@@ -1641,10 +1629,10 @@ mod conn {
|
||||
assert_eq!(body.as_ref(), b"");
|
||||
});
|
||||
|
||||
let rx = rx1.map_err(|_| hyper::Error::Io(io::Error::new(io::ErrorKind::Other, "thread panicked")));
|
||||
let rx = rx1.expect("thread panicked");
|
||||
|
||||
let timeout = Delay::new(Duration::from_millis(200));
|
||||
let rx = rx.and_then(move |_| timeout.map_err(|e| e.into()));
|
||||
let rx = rx.and_then(move |_| timeout.expect("timeout"));
|
||||
until_tunneled.join(res).join(rx).map(|r| r.0).wait().unwrap();
|
||||
|
||||
// should not be ready now
|
||||
@@ -1697,3 +1685,17 @@ mod conn {
|
||||
|
||||
impl AsyncRead for DebugStream {}
|
||||
}
|
||||
|
||||
trait FutureHyperExt: Future {
|
||||
fn expect<E>(self, msg: &'static str) -> Box<Future<Item=Self::Item, Error=E>>;
|
||||
}
|
||||
|
||||
impl<F> FutureHyperExt for F
|
||||
where
|
||||
F: Future + 'static,
|
||||
F::Error: ::std::fmt::Debug,
|
||||
{
|
||||
fn expect<E>(self, msg: &'static str) -> Box<Future<Item=Self::Item, Error=E>> {
|
||||
Box::new(self.map_err(move |e| panic!("expect: {}; error={:?}", msg, e)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -935,7 +935,7 @@ fn returning_1xx_response_is_error() {
|
||||
let socket = item.unwrap();
|
||||
Http::<hyper::Chunk>::new()
|
||||
.serve_connection(socket, service_fn(|_| {
|
||||
Ok(Response::builder()
|
||||
Ok::<_, hyper::Error>(Response::builder()
|
||||
.status(StatusCode::CONTINUE)
|
||||
.body(Body::empty())
|
||||
.unwrap())
|
||||
@@ -988,7 +988,7 @@ fn upgrades() {
|
||||
.header("upgrade", "foobar")
|
||||
.body(hyper::Body::empty())
|
||||
.unwrap();
|
||||
Ok(res)
|
||||
Ok::<_, hyper::Error>(res)
|
||||
}));
|
||||
|
||||
let mut conn_opt = Some(conn);
|
||||
@@ -1144,10 +1144,10 @@ fn streaming_body() {
|
||||
.keep_alive(false)
|
||||
.serve_connection(socket, service_fn(|_| {
|
||||
static S: &'static [&'static [u8]] = &[&[b'x'; 1_000] as &[u8]; 1_00] as _;
|
||||
let b = ::futures::stream::iter_ok(S.into_iter())
|
||||
let b = ::futures::stream::iter_ok::<_, String>(S.into_iter())
|
||||
.map(|&s| s);
|
||||
let b = hyper::Body::wrap_stream(b);
|
||||
Ok(Response::new(b))
|
||||
Ok::<_, hyper::Error>(Response::new(b))
|
||||
}))
|
||||
.map(|_| ())
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user