Skip to content

Commit 720be07

Browse files
authored
Make request errors more useful (#12)
By making the HttpRequest error type only yield status code type failures, we can more easily take action on those errors in the SDK. For other configuration related errors specific to request building, we have introduced a new enum variant -- InvalidParameter.
1 parent 6e8b751 commit 720be07

File tree

3 files changed

+48
-28
lines changed

3 files changed

+48
-28
lines changed

src/client.rs

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ pub struct ClientBuilder {
4242
impl ClientBuilder {
4343
/// Set a HTTP header on the SSE request.
4444
pub fn header(mut self, key: &'static str, value: &str) -> Result<ClientBuilder> {
45-
let value = value.parse().map_err(|e| Error::HttpRequest(Box::new(e)))?;
45+
let value = value
46+
.parse()
47+
.map_err(|e| Error::InvalidParameter(Box::new(e)))?;
4648
self.headers.insert(key, value);
4749
Ok(self)
4850
}
@@ -113,7 +115,9 @@ impl Client<()> {
113115
/// [`ClientBuilder`]: struct.ClientBuilder.html
114116
/// [`.stream()`]: #method.stream
115117
pub fn for_url(url: &str) -> Result<ClientBuilder> {
116-
let url = url.parse().map_err(|e| Error::HttpRequest(Box::new(e)))?;
118+
let url = url
119+
.parse()
120+
.map_err(|e| Error::InvalidParameter(Box::new(e)))?;
117121
Ok(ClientBuilder {
118122
url,
119123
headers: HeaderMap::new(),
@@ -205,7 +209,7 @@ impl<C> ReconnectingRequest<C> {
205209
*request.headers_mut().unwrap() = self.props.headers.clone();
206210
let request = request
207211
.body(Body::empty())
208-
.map_err(|e| Error::HttpRequest(Box::new(e)))?;
212+
.map_err(|e| Error::InvalidParameter(Box::new(e)))?;
209213
Ok(self.http.request(request))
210214
}
211215

@@ -257,40 +261,50 @@ where
257261
loop {
258262
trace!("ReconnectingRequest::poll loop({:?})", &self.state);
259263

260-
let state = self.as_mut().project().state.project();
261-
let new_state = match state {
264+
let this = self.as_mut().project();
265+
let state = this.state.project();
266+
267+
match state {
262268
// New immediately transitions to Connecting, and exists only
263269
// to ensure that we only connect when polled.
264270
StateProj::New => {
265271
let resp = match self.send_request() {
266272
Err(e) => return Poll::Ready(Some(Err(e))),
267273
Ok(r) => r,
268274
};
269-
State::Connecting {
270-
resp,
271-
retry: self.props.reconnect_opts.retry_initial,
272-
}
275+
let retry = self.props.reconnect_opts.retry_initial;
276+
self.as_mut()
277+
.project()
278+
.state
279+
.set(State::Connecting { resp, retry })
273280
}
274281
StateProj::Connecting { retry, resp } => match ready!(resp.poll(cx)) {
275282
Ok(resp) => {
276283
debug!("HTTP response: {:#?}", resp);
277284

278285
if !resp.status().is_success() {
279-
let e = StatusError {
280-
status: resp.status(),
281-
};
282-
return Poll::Ready(Some(Err(Error::HttpRequest(Box::new(e)))));
286+
self.as_mut().project().state.set(State::New);
287+
return Poll::Ready(Some(Err(Error::HttpRequest(resp.status()))));
283288
}
284289

285290
self.as_mut().reset_backoff();
286-
State::Connected(resp.into_body())
291+
self.as_mut()
292+
.project()
293+
.state
294+
.set(State::Connected(resp.into_body()))
287295
}
288296
Err(e) => {
289297
warn!("request returned an error: {}", e);
290298
if !*retry {
299+
self.as_mut().project().state.set(State::New);
291300
return Poll::Ready(Some(Err(Error::HttpStream(Box::new(e)))));
292301
}
293-
State::WaitingToReconnect(delay(self.as_mut().backoff(), "retrying"))
302+
303+
let duration = self.as_mut().backoff();
304+
self.as_mut()
305+
.project()
306+
.state
307+
.set(State::WaitingToReconnect(delay(duration, "retrying")))
294308
}
295309
},
296310
StateProj::Connected(body) => match ready!(body.poll_data(cx)) {
@@ -300,10 +314,11 @@ where
300314
res => {
301315
// reconnect
302316
if self.props.reconnect_opts.reconnect {
303-
State::WaitingToReconnect(delay(
304-
self.as_mut().backoff(),
305-
"reconnecting",
306-
))
317+
let duration = self.as_mut().backoff();
318+
self.as_mut()
319+
.project()
320+
.state
321+
.set(State::WaitingToReconnect(delay(duration, "reconnecting")))
307322
} else {
308323
return Poll::Ready(
309324
res.map(|r| r.map_err(|e| Error::HttpStream(Box::new(e)))),
@@ -315,10 +330,12 @@ where
315330
ready!(delay.poll(cx));
316331
info!("Reconnecting");
317332
let resp = self.send_request()?;
318-
State::Connecting { retry: true, resp }
333+
self.as_mut()
334+
.project()
335+
.state
336+
.set(State::Connecting { retry: true, resp })
319337
}
320338
};
321-
self.as_mut().project().state.set(new_state);
322339
}
323340
}
324341
}

src/decode.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,7 +569,7 @@ mod tests {
569569
Ready(Some(Err(err))) => {
570570
assert!(err.is_http_stream_error());
571571
let description = format!("{}", err.source().unwrap());
572-
assert!(description.contains("read error"), description);
572+
assert!(description.contains("read error"), "{}", description);
573573
}
574574
res => panic!("expected HttpStream error, got {:?}", res),
575575
}
@@ -582,7 +582,7 @@ mod tests {
582582
Ready(Some(Err(err))) => {
583583
assert!(err.is_http_stream_error());
584584
let description = format!("{}", err.source().unwrap());
585-
assert!(description.contains("read error"), description);
585+
assert!(description.contains("read error"), "{}", description);
586586
}
587587
res => panic!("expected HttpStream error, got {:?}", res),
588588
}
@@ -600,7 +600,7 @@ mod tests {
600600
Ready(Some(Err(err))) => {
601601
assert!(err.is_http_stream_error());
602602
let description = format!("{}", err.source().unwrap());
603-
assert!(description.contains("read error"), description);
603+
assert!(description.contains("read error"), "{}", description);
604604
}
605605
res => panic!("expected HttpStream error, got {:?}", res),
606606
}
@@ -687,7 +687,7 @@ mod tests {
687687
let mut cx = Context::from_waker(&waker);
688688
match s.poll_next_unpin(&mut cx) {
689689
Poll::Ready(None) => (),
690-
Poll::Ready(Some(event)) => panic!(format!("expected eof, got {:?}", event)),
690+
Poll::Ready(Some(event)) => panic!("expected eof, got {:?}", event),
691691
Poll::Pending => panic!("expected eof, got Pending"),
692692
}
693693
}
@@ -700,7 +700,7 @@ mod tests {
700700
let mut cx = Context::from_waker(&waker);
701701
match s.poll_next_unpin(&mut cx) {
702702
Poll::Ready(Some(Ok(event))) => event,
703-
Poll::Ready(Some(Err(e))) => panic!(format!("expected eof, got error: {:?}", e)),
703+
Poll::Ready(Some(Err(e))) => panic!("expected eof, got error: {:?}", e),
704704
Poll::Ready(None) => panic!("expected event, got eof"),
705705
Poll::Pending => panic!("expected event, got Pending"),
706706
}

src/error.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use hyper::StatusCode;
2+
13
/// Error type returned from this library's functions.
24
#[derive(Debug)]
35
pub enum Error {
6+
/// An invalid request parameter
7+
InvalidParameter(Box<dyn std::error::Error + Send + 'static>),
48
/// The HTTP request failed.
5-
HttpRequest(Box<dyn std::error::Error + Send + 'static>),
9+
HttpRequest(StatusCode),
610
/// An error reading from the HTTP response body.
711
HttpStream(Box<dyn std::error::Error + Send + 'static>),
812
/// The HTTP response stream ended unexpectedly (e.g. in the
@@ -38,7 +42,6 @@ impl Error {
3842

3943
pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
4044
match self {
41-
Error::HttpRequest(err) => Some(err.as_ref()),
4245
Error::HttpStream(err) => Some(err.as_ref()),
4346
Error::Unexpected(err) => Some(err.as_ref()),
4447
_ => None,

0 commit comments

Comments
 (0)