Skip to content

Commit b59621f

Browse files
authored
chore(volo-http): implement source for Errors (#519)
Signed-off-by: Yu Li <[email protected]>
1 parent 092f318 commit b59621f

File tree

8 files changed

+103
-31
lines changed

8 files changed

+103
-31
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

volo-http/Cargo.toml

+26-22
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "volo-http"
3-
version = "0.2.14"
3+
version = "0.3.0-rc.1"
44
edition.workspace = true
55
homepage.workspace = true
66
repository.workspace = true
@@ -15,8 +15,6 @@ keywords = ["async", "rpc", "http"]
1515

1616
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1717

18-
19-
2018
[badges]
2119
maintenance = { status = "actively-developed" }
2220

@@ -61,28 +59,30 @@ tracing.workspace = true
6159
url.workspace = true
6260

6361
# =====optional=====
64-
multer = { workspace = true, optional = true }
6562

6663
# server optional
6764
matchit = { workspace = true, optional = true }
6865

69-
# protocol optional
66+
# serde and form, query, json
67+
serde = { workspace = true, optional = true }
68+
serde_urlencoded = { workspace = true, optional = true }
69+
sonic-rs = { workspace = true, optional = true }
70+
71+
# cookie support
72+
cookie = { workspace = true, optional = true, features = ["percent-encode"] }
73+
cookie_store = { workspace = true, optional = true }
74+
75+
# multipart optional
76+
multer = { workspace = true, optional = true }
77+
78+
# websocket optional
7079
tungstenite = { workspace = true, optional = true }
7180
tokio-tungstenite = { workspace = true, optional = true }
7281

7382
# tls optional
7483
tokio-rustls = { workspace = true, optional = true }
7584
tokio-native-tls = { workspace = true, optional = true }
7685

77-
# cookie support
78-
cookie = { workspace = true, optional = true, features = ["percent-encode"] }
79-
cookie_store = { workspace = true, optional = true }
80-
81-
# serde and form, query, json
82-
serde = { workspace = true, optional = true }
83-
serde_urlencoded = { workspace = true, optional = true }
84-
sonic-rs = { workspace = true, optional = true }
85-
8686
[dev-dependencies]
8787
async-stream.workspace = true
8888
libc.workspace = true
@@ -96,11 +96,22 @@ default = []
9696
default_client = ["client", "json"]
9797
default_server = ["server", "query", "form", "json", "multipart"]
9898

99-
full = ["client", "server", "rustls", "cookie", "query", "form", "json", "multipart", "tls", "ws"]
99+
full = [
100+
"client", "server", # core
101+
"query", "form", "json", # serde
102+
"tls", # https
103+
"cookie", "multipart", "ws",
104+
]
100105

101106
client = ["hyper/client", "hyper/http1"] # client core
102107
server = ["hyper/server", "hyper/http1", "dep:matchit"] # server core
103108

109+
__serde = ["dep:serde"] # a private feature for enabling `serde` by `serde_xxx`
110+
query = ["__serde", "dep:serde_urlencoded"]
111+
form = ["__serde", "dep:serde_urlencoded"]
112+
json = ["__serde", "dep:sonic-rs"]
113+
114+
cookie = ["dep:cookie", "dep:cookie_store"]
104115
multipart = ["dep:multer"]
105116
ws = ["dep:tungstenite", "dep:tokio-tungstenite"]
106117

@@ -110,13 +121,6 @@ rustls = ["__tls", "dep:tokio-rustls", "volo/rustls"]
110121
native-tls = ["__tls", "dep:tokio-native-tls", "volo/native-tls"]
111122
native-tls-vendored = ["native-tls", "volo/native-tls-vendored"]
112123

113-
cookie = ["dep:cookie", "dep:cookie_store"]
114-
115-
__serde = ["dep:serde"] # a private feature for enabling `serde` by `serde_xxx`
116-
query = ["__serde", "dep:serde_urlencoded"]
117-
form = ["__serde", "dep:serde_urlencoded"]
118-
json = ["__serde", "dep:sonic-rs"]
119-
120124
[package.metadata.docs.rs]
121125
all-features = true
122126
rustdoc-args = ["--cfg", "docsrs"]

volo-http/src/body.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,15 @@ impl fmt::Display for BodyConvertError {
278278
}
279279
}
280280

281-
impl Error for BodyConvertError {}
281+
impl Error for BodyConvertError {
282+
fn source(&self) -> Option<&(dyn Error + 'static)> {
283+
match self {
284+
#[cfg(feature = "json")]
285+
Self::JsonDeserializeError(e) => Some(e),
286+
_ => None,
287+
}
288+
}
289+
}
282290

283291
impl From<()> for Body {
284292
fn from(_: ()) -> Self {

volo-http/src/error/client.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ impl fmt::Display for ClientError {
7979
}
8080
}
8181

82-
impl Error for ClientError {}
82+
impl Error for ClientError {
83+
fn source(&self) -> Option<&(dyn Error + 'static)> {
84+
Some(self.source.as_ref()?.as_ref())
85+
}
86+
}
8387

8488
/// Error kind of [`ClientError`]
8589
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -174,7 +178,7 @@ macro_rules! simple_error {
174178
paste! {
175179
#[doc = $kind " error \"" $msg "\""]
176180
$(#[$attr])*
177-
#[derive(Debug)]
181+
#[derive(Debug, PartialEq, Eq)]
178182
pub struct $name;
179183

180184
$(#[$attr])*
@@ -200,3 +204,25 @@ simple_error!(Builder => BadScheme => "bad scheme");
200204
simple_error!(Builder => BadHostName => "bad host name");
201205
simple_error!(Request => Timeout => "request timeout");
202206
simple_error!(LoadBalance => NoAvailableEndpoint => "no available endpoint");
207+
208+
#[cfg(test)]
209+
mod client_error_tests {
210+
use std::error::Error;
211+
212+
use crate::error::client::{
213+
bad_host_name, bad_scheme, no_address, no_available_endpoint, timeout, BadHostName,
214+
BadScheme, NoAddress, NoAvailableEndpoint, Timeout,
215+
};
216+
217+
#[test]
218+
fn types_downcast() {
219+
assert!(no_address().source().unwrap().is::<NoAddress>());
220+
assert!(bad_scheme().source().unwrap().is::<BadScheme>());
221+
assert!(bad_host_name().source().unwrap().is::<BadHostName>());
222+
assert!(timeout().source().unwrap().is::<Timeout>());
223+
assert!(no_available_endpoint()
224+
.source()
225+
.unwrap()
226+
.is::<NoAvailableEndpoint>());
227+
}
228+
}

volo-http/src/error/server.rs

+13
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ impl fmt::Display for ExtractBodyError {
3838
}
3939
}
4040

41+
impl Error for ExtractBodyError {
42+
fn source(&self) -> Option<&(dyn Error + 'static)> {
43+
match self {
44+
Self::Generic(e) => Some(e),
45+
Self::String(e) => Some(e),
46+
#[cfg(feature = "json")]
47+
Self::Json(e) => Some(e),
48+
#[cfg(feature = "form")]
49+
Self::Form(e) => Some(e),
50+
}
51+
}
52+
}
53+
4154
impl IntoResponse for ExtractBodyError {
4255
fn into_response(self) -> ServerResponse {
4356
let status = match self {

volo-http/src/server/param.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,14 @@ impl fmt::Display for PathParamsRejection {
282282
}
283283
}
284284

285-
impl Error for PathParamsRejection {}
285+
impl Error for PathParamsRejection {
286+
fn source(&self) -> Option<&(dyn Error + 'static)> {
287+
match self {
288+
Self::LengthMismatch => None,
289+
Self::ParseError(e) => Some(e.as_ref()),
290+
}
291+
}
292+
}
286293

287294
impl IntoResponse for PathParamsRejection {
288295
fn into_response(self) -> ServerResponse {

volo-http/src/server/route/utils.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,15 @@ impl fmt::Display for MatcherError {
9191
}
9292
}
9393

94-
impl Error for MatcherError {}
94+
impl Error for MatcherError {
95+
fn source(&self) -> Option<&(dyn Error + 'static)> {
96+
match self {
97+
Self::UriConflict(_) => None,
98+
Self::RouterInsertError(e) => Some(e),
99+
Self::RouterMatchError(e) => Some(e),
100+
}
101+
}
102+
}
95103

96104
pub(super) struct StripPrefixLayer;
97105

volo-http/src/server/utils/ws.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
4040
use std::{
4141
borrow::Cow,
42+
error::Error,
4243
fmt,
4344
future::Future,
4445
ops::{Deref, DerefMut},
@@ -467,7 +468,13 @@ impl fmt::Display for WebSocketError {
467468
}
468469
}
469470

470-
impl std::error::Error for WebSocketError {}
471+
impl Error for WebSocketError {
472+
fn source(&self) -> Option<&(dyn Error + 'static)> {
473+
match self {
474+
Self::Upgrade(e) => Some(e),
475+
}
476+
}
477+
}
471478

472479
/// What to do when a connection upgrade fails.
473480
///
@@ -498,7 +505,6 @@ impl OnFailedUpgrade for DefaultOnFailedUpgrade {
498505

499506
/// [`Error`]s while extracting [`WebSocketUpgrade`].
500507
///
501-
/// [`Error`]: std::error::Error
502508
/// [`WebSocketUpgrade`]: crate::server::utils::ws::WebSocketUpgrade
503509
#[derive(Debug)]
504510
pub enum WebSocketUpgradeRejectionError {
@@ -530,7 +536,7 @@ impl WebSocketUpgradeRejectionError {
530536
}
531537
}
532538

533-
impl std::error::Error for WebSocketUpgradeRejectionError {}
539+
impl Error for WebSocketUpgradeRejectionError {}
534540

535541
impl fmt::Display for WebSocketUpgradeRejectionError {
536542
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

0 commit comments

Comments
 (0)