Skip to content
This repository has been archived by the owner on Jan 18, 2020. It is now read-only.

Commit

Permalink
reform the error representation based on failure
Browse files Browse the repository at this point in the history
  • Loading branch information
ubnt-intrepid committed Jan 27, 2019
1 parent c0393c7 commit a9e8034
Show file tree
Hide file tree
Showing 10 changed files with 263 additions and 283 deletions.
6 changes: 5 additions & 1 deletion examples/logging/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,11 @@ mod logging {
//
let response = result
.map(|response| response.map(Into::into))
.unwrap_or_else(|e| e.into_response(input.request).map(Into::into));
.unwrap_or_else(|e| {
e.into_response(input.request)
.expect("never fails")
.map(Into::into)
});

let log_level = match response.status().as_u16() {
400...599 => log::Level::Error,
Expand Down
2 changes: 1 addition & 1 deletion examples/template-tera/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ mod support_tera {
.expect("should be a valid response")
.into()
})
.map_err(tsukuyomi::error::internal_server_error)
.map_err(|e| tsukuyomi::error::internal_server_error(e.to_string()))
}
}
}
9 changes: 2 additions & 7 deletions tsukuyomi-cors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,13 +579,8 @@ impl From<CORSErrorKind> for CORSError {
}

impl HttpError for CORSError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::FORBIDDEN)
.body(self.to_string())
.expect("should be a valid response")
fn status_code(&self) -> StatusCode {
StatusCode::FORBIDDEN
}
}

Expand Down
1 change: 1 addition & 0 deletions tsukuyomi-juniper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ izanami-util = "0.1.0-preview.1"
juniper = "0.11.1"

bytes = "0.4"
failure = "0.1.5"
futures = "0.1"
http = "0.1"
mime = "0.3"
Expand Down
143 changes: 54 additions & 89 deletions tsukuyomi-juniper/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,37 @@
use {
http::{Request, Response, StatusCode},
http::{Response, StatusCode},
serde_json::json,
std::fmt,
tsukuyomi::{
error::{Error, HttpError}, //
future::{Poll, TryFuture},
future::{Async, Poll, TryFuture},
handler::{AllowedMethods, Handler, ModifyHandler},
input::Input,
output::ResponseBody,
output::IntoResponse,
util::Either,
},
};

#[derive(Debug)]
#[derive(Debug, failure::Fail)]
pub enum GraphQLParseError {
#[fail(display = "the request method is invalid")]
InvalidRequestMethod,
#[fail(display = "missing query")]
MissingQuery,
#[fail(display = "missing content-type")]
MissingMime,
#[fail(display = "the content type is invalid.")]
InvalidMime,
ParseJson(serde_json::Error),
ParseQuery(serde_urlencoded::de::Error),
DecodeUtf8(std::str::Utf8Error),
}

impl fmt::Display for GraphQLParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GraphQLParseError::InvalidRequestMethod => f.write_str("the request method is invalid"),
GraphQLParseError::MissingQuery => f.write_str("missing query"),
GraphQLParseError::MissingMime => f.write_str("missing content-type"),
GraphQLParseError::InvalidMime => f.write_str("the content type is invalid."),
GraphQLParseError::ParseJson(ref e) => e.fmt(f),
GraphQLParseError::ParseQuery(ref e) => e.fmt(f),
GraphQLParseError::DecodeUtf8(ref e) => e.fmt(f),
}
}
#[fail(display = "failed to parse input as a JSON object")]
ParseJson(#[fail(cause)] serde_json::Error),
#[fail(display = "failed to parse HTTP query")]
ParseQuery(#[fail(cause)] serde_urlencoded::de::Error),
#[fail(display = "failed to decode input as a UTF-8 sequence")]
DecodeUtf8(#[fail(cause)] std::str::Utf8Error),
}

impl HttpError for GraphQLParseError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
let body = json!({
"errors": [
{
"message": self.to_string(),
}
],
})
.to_string();
Response::builder()
.status(StatusCode::BAD_REQUEST)
.header("content-type", "application/json")
.body(body)
.expect("should be a valid response")
}
}

#[derive(Debug)]
pub struct GraphQLError(Error);

impl fmt::Display for GraphQLError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}

impl HttpError for GraphQLError {
type Body = ResponseBody;

fn into_response(self, request: &Request<()>) -> Response<Self::Body> {
let body = json!({
"errors": [
{
"message": self.to_string(),
}
],
})
.to_string();

let mut response = self.0.into_response(request).map(|_| body.into());

response.headers_mut().insert(
http::header::CONTENT_TYPE,
http::header::HeaderValue::from_static("application/json"),
);

response
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}

Expand All @@ -103,58 +49,77 @@ impl<H> ModifyHandler<H> for CaptureErrors
where
H: Handler,
{
type Output = H::Output;
type Handler = GraphQLHandler<H>; // private;
type Output = Either<Response<String>, H::Output>;
type Handler = CaptureErrorsHandler<H>; // private;

fn modify(&self, inner: H) -> Self::Handler {
GraphQLHandler { inner }
CaptureErrorsHandler { inner }
}
}

#[derive(Debug)]
pub struct GraphQLHandler<H> {
pub struct CaptureErrorsHandler<H> {
inner: H,
}

impl<H> Handler for GraphQLHandler<H>
impl<H> Handler for CaptureErrorsHandler<H>
where
H: Handler,
{
type Output = H::Output;
type Output = Either<Response<String>, H::Output>;
type Error = Error;
type Handle = GraphQLHandle<H::Handle>;
type Handle = CaptureErrorsHandle<H::Handle>;

fn allowed_methods(&self) -> Option<&AllowedMethods> {
self.inner.allowed_methods()
}

fn handle(&self) -> Self::Handle {
GraphQLHandle {
CaptureErrorsHandle {
inner: self.inner.handle(),
}
}
}

#[derive(Debug)]
pub struct GraphQLHandle<H> {
pub struct CaptureErrorsHandle<H> {
inner: H,
}

impl<H> TryFuture for GraphQLHandle<H>
impl<H> TryFuture for CaptureErrorsHandle<H>
where
H: TryFuture,
{
type Ok = H::Ok;
type Ok = Either<Response<String>, H::Ok>;
type Error = Error;

#[inline]
fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll<Self::Ok, Self::Error> {
self.inner.poll_ready(input).map_err(|err| {
let err = err.into();
if err.is::<GraphQLParseError>() || err.is::<GraphQLError>() {
err
} else {
GraphQLError(err).into()
match self.inner.poll_ready(input) {
Ok(Async::Ready(ok)) => Ok(Async::Ready(Either::Right(ok))),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(err) => {
let err = err.into();
let body = json!({
"errors": [
{
"message": err.to_string(),
}
],
})
.to_string();

let mut response = err
.into_response(input.request)
.expect("never fails")
.map(|_| body);
response.headers_mut().insert(
http::header::CONTENT_TYPE,
http::header::HeaderValue::from_static("application/json"),
);

Ok(Async::Ready(Either::Left(response)))
}
})
}
}
}
11 changes: 3 additions & 8 deletions tsukuyomi-tungstenite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mod imp {
SEC_WEBSOCKET_VERSION,
UPGRADE,
},
Request, Response, StatusCode,
Response, StatusCode,
},
izanami_util::rt::{DefaultExecutor, Executor},
sha1::{Digest, Sha1},
Expand Down Expand Up @@ -161,13 +161,8 @@ mod imp {
}

impl HttpError for HandshakeError {
type Body = String;

fn into_response(self, _: &Request<()>) -> Response<Self::Body> {
Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(self.to_string())
.expect("should be a valid response")
fn status_code(&self) -> StatusCode {
StatusCode::BAD_REQUEST
}
}

Expand Down
4 changes: 2 additions & 2 deletions tsukuyomi/src/app/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use {
param::Params,
Cookies, Input,
},
output::ResponseBody,
output::{IntoResponse, ResponseBody},
util::Never,
},
cookie::CookieJar,
Expand Down Expand Up @@ -184,7 +184,7 @@ impl<C: Concurrency> Future for AppFuture<C> {

let mut output = match polled {
Ok(output) => output,
Err(err) => err.into_response(&self.request),
Err(err) => err.into_response(&self.request)?,
};

self.process_before_reply(&mut output);
Expand Down
Loading

0 comments on commit a9e8034

Please sign in to comment.