Skip to content

Commit

Permalink
Response: add Error storage, retrieval, conversion
Browse files Browse the repository at this point in the history
This allows for robust creation of Response-s directly from Error-s, with error
capture for future reference, and retrieval via `error() -> Option<&Error>`.

Refs: http-rs#169
Refs: http-rs/tide#546
Refs: http-rs/tide#532
Refs: http-rs/tide#452
  • Loading branch information
Fishrock123 committed Jun 5, 2020
1 parent 8090139 commit e05cd22
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 4 deletions.
81 changes: 78 additions & 3 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::headers::{
};
use crate::mime::Mime;
use crate::trailers::{self, Trailers};
use crate::{Body, Extensions, StatusCode, Version};
use crate::{Body, Error, Extensions, StatusCode, Version};

cfg_unstable! {
use crate::upgrade;
Expand Down Expand Up @@ -49,6 +49,7 @@ pin_project_lite::pin_project! {
ext: Extensions,
local_addr: Option<String>,
peer_addr: Option<String>,
error: Option<Error>,
}
}

Expand Down Expand Up @@ -83,6 +84,7 @@ pin_project_lite::pin_project! {
ext: Extensions,
local_addr: Option<String>,
peer_addr: Option<String>,
error: Option<Error>,
}
}

Expand All @@ -108,6 +110,7 @@ impl Response {
ext: Extensions::new(),
peer_addr: None,
local_addr: None,
error: None,
}
}

Expand Down Expand Up @@ -136,6 +139,61 @@ impl Response {
ext: Extensions::new(),
peer_addr: None,
local_addr: None,
error: None,
}
}

/// Create a new response from an `http_types::Error`.
///
/// This will store the error in the `Response`, allowing it to later be
/// checked via `Response::error()`.
///
/// If the `Error`'s status had a status code that was not a 5XX server
/// error code, the response body will be set to the error's message, and
/// the content-type header will be set to `http_types::mime::Plain`.
#[cfg(not(feature = "unstable"))]
pub fn from_error(error: Error) -> Self {
let (trailers_sender, trailers_receiver) = sync::channel(1);
Self {
status: error.status(),
headers: Headers::new(),
version: None,
body: Body::empty(),
trailers_sender: Some(trailers_sender),
trailers_receiver: Some(trailers_receiver),
ext: Extensions::new(),
peer_addr: None,
local_addr: None,
error: Some(error),
}
}

/// Create a new response from an `http_types::Error`.
///
/// This will store the error in the `Response`, allowing it to later be
/// checked via `Response::error()`.
///
/// If the `Error`'s status had a status code that was not a 5XX server
/// error code, the response body will be set to the error's message, and
/// the content-type header will be set to `http_types::mime::Plain`.
#[cfg(feature = "unstable")]
pub fn from_error(error: Error) -> Self {
let (trailers_sender, trailers_receiver) = sync::channel(1);
let (upgrade_sender, upgrade_receiver) = sync::channel(1);
Self {
status: error.status(),
headers: Headers::new(),
version: None,
body: Body::empty(),
trailers_sender: Some(trailers_sender),
trailers_receiver: Some(trailers_receiver),
upgrade_sender: Some(upgrade_sender),
upgrade_receiver: Some(upgrade_receiver),
has_upgrade: false,
ext: Extensions::new(),
peer_addr: None,
local_addr: None,
error: Some(error),
}
}

Expand Down Expand Up @@ -465,6 +523,16 @@ impl Response {
self.body.is_empty()
}

/// Returns an optional reference to the `Error` if the response was created from one, or else `None`.
pub fn error(&self) -> Option<&Error> {
self.error.as_ref()
}

/// Takes the `Error` from the response if one exists, replacing it with `None`.
pub fn take_error(&mut self) -> Option<Error> {
self.error.take()
}

/// Get the HTTP version, if one has been set.
///
/// # Examples
Expand Down Expand Up @@ -631,8 +699,8 @@ impl Response {
}

impl Clone for Response {
/// Clone the response, resolving the body to `Body::empty()` and removing
/// extensions.
/// Clone the response, resolving the body to `Body::empty()`, removing
/// extensions, and unsetting any `Error`.
fn clone(&self) -> Self {
Self {
status: self.status.clone(),
Expand All @@ -650,6 +718,7 @@ impl Clone for Response {
ext: Extensions::new(),
peer_addr: self.peer_addr.clone(),
local_addr: self.local_addr.clone(),
error: None,
}
}
}
Expand Down Expand Up @@ -722,6 +791,12 @@ impl Index<&str> for Response {
}
}

impl From<Error> for Response {
fn from(e: Error) -> Self {
Self::from_error(e)
}
}

impl From<StatusCode> for Response {
fn from(s: StatusCode) -> Self {
Response::new(s)
Expand Down
20 changes: 19 additions & 1 deletion tests/error.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use http_types::{bail, ensure, ensure_eq, Error, StatusCode};
use http_types::{bail, ensure, ensure_eq, Error, Response, StatusCode};
use std::io;

#[test]
Expand Down Expand Up @@ -71,3 +71,21 @@ fn option_ext() {
let err = res.unwrap_err();
assert_eq!(err.status(), StatusCode::NotFound);
}

#[test]
fn to_response() {
let msg = "This is an error";

let error = Error::from_str(StatusCode::NotFound, msg);
let mut res = Response::from_error(error);

assert!(res.error().is_some());
// Ensure we did not consume the error
assert!(res.error().is_some());

assert_eq!(res.error().unwrap().status(), StatusCode::NotFound);
assert_eq!(res.error().unwrap().to_string(), msg);

res.take_error();
assert!(res.error().is_none());
}

0 comments on commit e05cd22

Please sign in to comment.