Skip to content

Commit 779a58b

Browse files
Breaking change: provide more context in error::Error
We'd like top provide the caller with more context to, say, print key portions of a response in debug mode or emit metrics. Unfortunatley, the key reqwest Response methods consume self and make the value largely useless for an error that wraps it. So let's drop the Response part of copy over the request URL, header map and body. The first two do not consume self.
1 parent f8f6300 commit 779a58b

File tree

3 files changed

+160
-147
lines changed

3 files changed

+160
-147
lines changed

src/api.rs

Lines changed: 77 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14+
#![allow(clippy::result_large_err)]
15+
1416
use backtrace::Backtrace;
1517
use reqwest::{
1618
header::{HeaderMap, HeaderValue},
@@ -21,9 +23,7 @@ use serde_json::{json, Map, Value};
2123
use std::fmt;
2224

2325
use crate::error::Error;
24-
use crate::error::Error::{
25-
ClientErrorResponse, InvalidHeaderValue, RequestError, ServerErrorResponse,
26-
};
26+
use crate::error::Error::{ClientErrorResponse, NotFound, ServerErrorResponse};
2727
use crate::responses::MessageList;
2828
use crate::{
2929
commons::{BindingDestinationType, UserLimitTarget, VirtualHostLimitTarget},
@@ -35,54 +35,11 @@ use crate::{
3535
responses::{self, BindingInfo, DefinitionSet},
3636
};
3737

38-
type HttpClientResponse = reqwest::Response;
39-
type HttpClientError = Error<HttpClientResponse, StatusCode, reqwest::Error, Backtrace>;
38+
pub type HttpClientResponse = reqwest::Response;
39+
pub type HttpClientError = crate::error::HttpClientError;
4040

4141
pub type Result<T> = std::result::Result<T, HttpClientError>;
4242

43-
impl From<reqwest::Error> for HttpClientError {
44-
fn from(req_err: reqwest::Error) -> Self {
45-
match req_err.status() {
46-
None => RequestError {
47-
error: req_err,
48-
backtrace: Backtrace::new(),
49-
},
50-
Some(status_code) => {
51-
if status_code.is_client_error() {
52-
return ClientErrorResponse {
53-
status_code,
54-
// reqwest::Error does not provide access to the associated
55-
// response, if any
56-
response: None,
57-
backtrace: Backtrace::new(),
58-
};
59-
};
60-
61-
if status_code.is_server_error() {
62-
return ServerErrorResponse {
63-
status_code,
64-
// reqwest::Error does not provide access to the associated
65-
// response, if any
66-
response: None,
67-
backtrace: Backtrace::new(),
68-
};
69-
};
70-
71-
RequestError {
72-
error: req_err,
73-
backtrace: Backtrace::new(),
74-
}
75-
}
76-
}
77-
}
78-
}
79-
80-
impl From<reqwest::header::InvalidHeaderValue> for HttpClientError {
81-
fn from(err: reqwest::header::InvalidHeaderValue) -> Self {
82-
InvalidHeaderValue { error: err }
83-
}
84-
}
85-
8643
/// A `ClientBuilder` can be used to create a `Client` with custom configuration.
8744
///
8845
/// Example
@@ -1229,8 +1186,8 @@ where
12291186
async fn http_get<S>(
12301187
&self,
12311188
path: S,
1232-
client_expect_code_error: Option<StatusCode>,
1233-
server_expect_code_error: Option<StatusCode>,
1189+
client_code_to_accept_or_ignore: Option<StatusCode>,
1190+
server_code_to_accept_or_ignore: Option<StatusCode>,
12341191
) -> Result<HttpClientResponse>
12351192
where
12361193
S: AsRef<str>,
@@ -1241,20 +1198,22 @@ where
12411198
.basic_auth(&self.username, Some(&self.password))
12421199
.send()
12431200
.await?;
1244-
let response = self.ok_or_status_code_error(
1245-
response,
1246-
client_expect_code_error,
1247-
server_expect_code_error,
1248-
)?;
1201+
let response = self
1202+
.ok_or_status_code_error(
1203+
response,
1204+
client_code_to_accept_or_ignore,
1205+
server_code_to_accept_or_ignore,
1206+
)
1207+
.await?;
12491208
Ok(response)
12501209
}
12511210

12521211
async fn http_put<S, T>(
12531212
&self,
12541213
path: S,
12551214
payload: &T,
1256-
client_expect_code_error: Option<StatusCode>,
1257-
server_expect_code_error: Option<StatusCode>,
1215+
client_code_to_accept_or_ignore: Option<StatusCode>,
1216+
server_code_to_accept_or_ignore: Option<StatusCode>,
12581217
) -> Result<HttpClientResponse>
12591218
where
12601219
S: AsRef<str>,
@@ -1267,20 +1226,22 @@ where
12671226
.basic_auth(&self.username, Some(&self.password))
12681227
.send()
12691228
.await?;
1270-
let response = self.ok_or_status_code_error(
1271-
response,
1272-
client_expect_code_error,
1273-
server_expect_code_error,
1274-
)?;
1229+
let response = self
1230+
.ok_or_status_code_error(
1231+
response,
1232+
client_code_to_accept_or_ignore,
1233+
server_code_to_accept_or_ignore,
1234+
)
1235+
.await?;
12751236
Ok(response)
12761237
}
12771238

12781239
async fn http_post<S, T>(
12791240
&self,
12801241
path: S,
12811242
payload: &T,
1282-
client_expect_code_error: Option<StatusCode>,
1283-
server_expect_code_error: Option<StatusCode>,
1243+
client_code_to_accept_or_ignore: Option<StatusCode>,
1244+
server_code_to_accept_or_ignore: Option<StatusCode>,
12841245
) -> Result<HttpClientResponse>
12851246
where
12861247
S: AsRef<str>,
@@ -1293,19 +1254,21 @@ where
12931254
.basic_auth(&self.username, Some(&self.password))
12941255
.send()
12951256
.await?;
1296-
let response = self.ok_or_status_code_error(
1297-
response,
1298-
client_expect_code_error,
1299-
server_expect_code_error,
1300-
)?;
1257+
let response = self
1258+
.ok_or_status_code_error(
1259+
response,
1260+
client_code_to_accept_or_ignore,
1261+
server_code_to_accept_or_ignore,
1262+
)
1263+
.await?;
13011264
Ok(response)
13021265
}
13031266

13041267
async fn http_delete<S>(
13051268
&self,
13061269
path: S,
1307-
client_expect_code_error: Option<StatusCode>,
1308-
server_expect_code_error: Option<StatusCode>,
1270+
client_code_to_accept_or_ignore: Option<StatusCode>,
1271+
server_code_to_accept_or_ignore: Option<StatusCode>,
13091272
) -> Result<HttpClientResponse>
13101273
where
13111274
S: AsRef<str>,
@@ -1316,20 +1279,22 @@ where
13161279
.basic_auth(&self.username, Some(&self.password))
13171280
.send()
13181281
.await?;
1319-
let response = self.ok_or_status_code_error(
1320-
response,
1321-
client_expect_code_error,
1322-
server_expect_code_error,
1323-
)?;
1282+
let response = self
1283+
.ok_or_status_code_error(
1284+
response,
1285+
client_code_to_accept_or_ignore,
1286+
server_code_to_accept_or_ignore,
1287+
)
1288+
.await?;
13241289
Ok(response)
13251290
}
13261291

13271292
async fn http_delete_with_headers<S>(
13281293
&self,
13291294
path: S,
13301295
headers: HeaderMap,
1331-
client_expect_code_error: Option<StatusCode>,
1332-
server_expect_code_error: Option<StatusCode>,
1296+
client_code_to_accept_or_ignore: Option<StatusCode>,
1297+
server_code_to_accept_or_ignore: Option<StatusCode>,
13331298
) -> Result<HttpClientResponse>
13341299
where
13351300
S: AsRef<str>,
@@ -1341,43 +1306,63 @@ where
13411306
.headers(headers)
13421307
.send()
13431308
.await?;
1344-
let response = self.ok_or_status_code_error(
1345-
response,
1346-
client_expect_code_error,
1347-
server_expect_code_error,
1348-
)?;
1309+
let response = self
1310+
.ok_or_status_code_error(
1311+
response,
1312+
client_code_to_accept_or_ignore,
1313+
server_code_to_accept_or_ignore,
1314+
)
1315+
.await?;
13491316
Ok(response)
13501317
}
13511318

1352-
fn ok_or_status_code_error(
1319+
async fn ok_or_status_code_error(
13531320
&self,
13541321
response: HttpClientResponse,
1355-
client_expect_code_error: Option<StatusCode>,
1356-
server_expect_code_error: Option<StatusCode>,
1322+
client_code_to_accept_or_ignore: Option<StatusCode>,
1323+
server_code_to_accept_or_ignore: Option<StatusCode>,
13571324
) -> Result<HttpClientResponse> {
13581325
let status = response.status();
1326+
if status == StatusCode::NOT_FOUND {
1327+
return Err(NotFound);
1328+
}
1329+
13591330
if status.is_client_error() {
1360-
match client_expect_code_error {
1331+
match client_code_to_accept_or_ignore {
13611332
Some(expect) if status == expect => {}
13621333
_ => {
1334+
let url = response.url().clone();
1335+
let headers = response.headers().clone();
1336+
// this consumes `self` and makes the response largely useless to the caller,
1337+
// so we copy the key parts into the error first
1338+
let body = response.text().await?;
13631339
return Err(ClientErrorResponse {
1364-
response: Some(response),
1340+
url: Some(url),
1341+
body: Some(body),
1342+
headers: Some(headers),
13651343
status_code: status,
13661344
backtrace: Backtrace::new(),
1367-
})
1345+
});
13681346
}
13691347
}
13701348
}
13711349

13721350
if status.is_server_error() {
1373-
match server_expect_code_error {
1351+
match server_code_to_accept_or_ignore {
13741352
Some(expect) if status == expect => {}
13751353
_ => {
1354+
let url = response.url().clone();
1355+
let headers = response.headers().clone();
1356+
// this consumes `self` and makes the response largely useless to the caller,
1357+
// so we copy the key parts into the error first
1358+
let body = response.text().await?;
13761359
return Err(ServerErrorResponse {
1377-
response: Some(response),
1360+
url: Some(url),
1361+
body: Some(body),
1362+
headers: Some(headers),
13781363
status_code: status,
13791364
backtrace: Backtrace::new(),
1380-
})
1365+
});
13811366
}
13821367
}
13831368
}

src/blocking_api.rs

Lines changed: 22 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@
1111
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14+
#![allow(clippy::result_large_err)]
15+
1416
use crate::error::Error;
15-
use crate::error::Error::{
16-
ClientErrorResponse, InvalidHeaderValue, NotFound, RequestError, ServerErrorResponse,
17-
};
17+
use crate::error::Error::{ClientErrorResponse, NotFound, ServerErrorResponse};
1818
use crate::{
1919
commons::{BindingDestinationType, UserLimitTarget, VirtualHostLimitTarget},
2020
path,
@@ -35,53 +35,10 @@ use serde_json::{json, Map, Value};
3535
use std::fmt;
3636

3737
pub type HttpClientResponse = reqwest::blocking::Response;
38-
pub type HttpClientError = Error<HttpClientResponse, StatusCode, reqwest::Error, Backtrace>;
38+
pub type HttpClientError = crate::error::HttpClientError;
3939

4040
pub type Result<T> = std::result::Result<T, HttpClientError>;
4141

42-
impl From<reqwest::Error> for HttpClientError {
43-
fn from(req_err: reqwest::Error) -> Self {
44-
match req_err.status() {
45-
None => RequestError {
46-
error: req_err,
47-
backtrace: Backtrace::new(),
48-
},
49-
Some(status_code) => {
50-
if status_code.is_client_error() {
51-
return ClientErrorResponse {
52-
status_code,
53-
// reqwest::Error does not provide access to the associated
54-
// response, if any
55-
response: None,
56-
backtrace: Backtrace::new(),
57-
};
58-
};
59-
60-
if status_code.is_server_error() {
61-
return ServerErrorResponse {
62-
status_code,
63-
// reqwest::Error does not provide access to the associated
64-
// response, if any
65-
response: None,
66-
backtrace: Backtrace::new(),
67-
};
68-
};
69-
70-
RequestError {
71-
error: req_err,
72-
backtrace: Backtrace::new(),
73-
}
74-
}
75-
}
76-
}
77-
}
78-
79-
impl From<reqwest::header::InvalidHeaderValue> for HttpClientError {
80-
fn from(err: reqwest::header::InvalidHeaderValue) -> Self {
81-
InvalidHeaderValue { error: err }
82-
}
83-
}
84-
8542
/// A `ClientBuilder` can be used to create a `Client` with custom configuration.
8643
///
8744
/// Example
@@ -1220,11 +1177,18 @@ where
12201177
match client_code_to_accept_or_ignore {
12211178
Some(expect) if status == expect => {}
12221179
_ => {
1180+
let url = response.url().clone();
1181+
let headers = response.headers().clone();
1182+
// this consumes `self` and makes the response largely useless to the caller,
1183+
// so we copy the key parts into the error first
1184+
let body = response.text()?;
12231185
return Err(ClientErrorResponse {
1224-
response: Some(response),
1186+
url: Some(url),
1187+
body: Some(body),
1188+
headers: Some(headers),
12251189
status_code: status,
12261190
backtrace: Backtrace::new(),
1227-
})
1191+
});
12281192
}
12291193
}
12301194
}
@@ -1233,11 +1197,18 @@ where
12331197
match server_code_to_accept_or_ignore {
12341198
Some(expect) if status == expect => {}
12351199
_ => {
1200+
let url = response.url().clone();
1201+
let headers = response.headers().clone();
1202+
// this consumes `self` and makes the response largely useless to the caller,
1203+
// so we copy the key parts into the error first
1204+
let body = response.text()?;
12361205
return Err(ServerErrorResponse {
1237-
response: Some(response),
1206+
url: Some(url),
1207+
body: Some(body),
1208+
headers: Some(headers),
12381209
status_code: status,
12391210
backtrace: Backtrace::new(),
1240-
})
1211+
});
12411212
}
12421213
}
12431214
}

0 commit comments

Comments
 (0)