From a9e803426325790332b5c2df71e3d994d05dbb30 Mon Sep 17 00:00:00 2001 From: Yusuke Sasaki Date: Fri, 25 Jan 2019 02:28:10 +0900 Subject: [PATCH] reform the error representation based on failure --- examples/logging/src/main.rs | 6 +- examples/template-tera/src/main.rs | 2 +- tsukuyomi-cors/src/lib.rs | 9 +- tsukuyomi-juniper/Cargo.toml | 1 + tsukuyomi-juniper/src/error.rs | 143 +++----- tsukuyomi-tungstenite/src/lib.rs | 11 +- tsukuyomi/src/app/service.rs | 4 +- tsukuyomi/src/error.rs | 359 ++++++++++--------- tsukuyomi/src/input/body.rs | 10 +- tsukuyomi/tests/integration_tests/extract.rs | 1 - 10 files changed, 263 insertions(+), 283 deletions(-) diff --git a/examples/logging/src/main.rs b/examples/logging/src/main.rs index e12164a74..0e5aac4f5 100644 --- a/examples/logging/src/main.rs +++ b/examples/logging/src/main.rs @@ -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, diff --git a/examples/template-tera/src/main.rs b/examples/template-tera/src/main.rs index 4d837b14d..c365e7d73 100644 --- a/examples/template-tera/src/main.rs +++ b/examples/template-tera/src/main.rs @@ -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())) } } } diff --git a/tsukuyomi-cors/src/lib.rs b/tsukuyomi-cors/src/lib.rs index 67f8067b5..f1f381a45 100644 --- a/tsukuyomi-cors/src/lib.rs +++ b/tsukuyomi-cors/src/lib.rs @@ -579,13 +579,8 @@ impl From for CORSError { } impl HttpError for CORSError { - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - Response::builder() - .status(StatusCode::FORBIDDEN) - .body(self.to_string()) - .expect("should be a valid response") + fn status_code(&self) -> StatusCode { + StatusCode::FORBIDDEN } } diff --git a/tsukuyomi-juniper/Cargo.toml b/tsukuyomi-juniper/Cargo.toml index 045d4ac87..5c1d30bb9 100644 --- a/tsukuyomi-juniper/Cargo.toml +++ b/tsukuyomi-juniper/Cargo.toml @@ -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" diff --git a/tsukuyomi-juniper/src/error.rs b/tsukuyomi-juniper/src/error.rs index 3b0da89bb..5b671d586 100644 --- a/tsukuyomi-juniper/src/error.rs +++ b/tsukuyomi-juniper/src/error.rs @@ -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 { - 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 { - 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 } } @@ -103,58 +49,77 @@ impl ModifyHandler for CaptureErrors where H: Handler, { - type Output = H::Output; - type Handler = GraphQLHandler; // private; + type Output = Either, H::Output>; + type Handler = CaptureErrorsHandler; // private; fn modify(&self, inner: H) -> Self::Handler { - GraphQLHandler { inner } + CaptureErrorsHandler { inner } } } #[derive(Debug)] -pub struct GraphQLHandler { +pub struct CaptureErrorsHandler { inner: H, } -impl Handler for GraphQLHandler +impl Handler for CaptureErrorsHandler where H: Handler, { - type Output = H::Output; + type Output = Either, H::Output>; type Error = Error; - type Handle = GraphQLHandle; + type Handle = CaptureErrorsHandle; 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 { +pub struct CaptureErrorsHandle { inner: H, } -impl TryFuture for GraphQLHandle +impl TryFuture for CaptureErrorsHandle where H: TryFuture, { - type Ok = H::Ok; + type Ok = Either, H::Ok>; type Error = Error; + #[inline] fn poll_ready(&mut self, input: &mut Input<'_>) -> Poll { - self.inner.poll_ready(input).map_err(|err| { - let err = err.into(); - if err.is::() || err.is::() { - 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))) } - }) + } } } diff --git a/tsukuyomi-tungstenite/src/lib.rs b/tsukuyomi-tungstenite/src/lib.rs index d96589c1d..c1902323c 100644 --- a/tsukuyomi-tungstenite/src/lib.rs +++ b/tsukuyomi-tungstenite/src/lib.rs @@ -81,7 +81,7 @@ mod imp { SEC_WEBSOCKET_VERSION, UPGRADE, }, - Request, Response, StatusCode, + Response, StatusCode, }, izanami_util::rt::{DefaultExecutor, Executor}, sha1::{Digest, Sha1}, @@ -161,13 +161,8 @@ mod imp { } impl HttpError for HandshakeError { - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - Response::builder() - .status(StatusCode::BAD_REQUEST) - .body(self.to_string()) - .expect("should be a valid response") + fn status_code(&self) -> StatusCode { + StatusCode::BAD_REQUEST } } diff --git a/tsukuyomi/src/app/service.rs b/tsukuyomi/src/app/service.rs index 02b28e47c..e378d4d12 100644 --- a/tsukuyomi/src/app/service.rs +++ b/tsukuyomi/src/app/service.rs @@ -7,7 +7,7 @@ use { param::Params, Cookies, Input, }, - output::ResponseBody, + output::{IntoResponse, ResponseBody}, util::Never, }, cookie::CookieJar, @@ -184,7 +184,7 @@ impl Future for AppFuture { 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); diff --git a/tsukuyomi/src/error.rs b/tsukuyomi/src/error.rs index a41e433f7..1fc150ee8 100644 --- a/tsukuyomi/src/error.rs +++ b/tsukuyomi/src/error.rs @@ -2,15 +2,17 @@ //! //! Tsukuyomi models the all errors generated during handling HTTP requests with a trait //! named [`HttpError`]. This trait is a method for converting itself into an HTTP response. -//! The design of this trait imitates the `failure` crate, but there are some specialization -//! considering of the HTTP context. //! //! [`HttpError`]: ./trait.HttpError.html use { - crate::{output::ResponseBody, util::Never}, + crate::{ + output::{IntoResponse, ResponseBody}, + util::Never, + }, + failure::{AsFail, Fail}, http::{Request, Response, StatusCode}, - std::{any::Any, fmt, io}, + std::{any::TypeId, fmt, io}, }; /// A type alias of `Result` with `error::Error` as error type. @@ -18,133 +20,115 @@ pub type Result = std::result::Result; /// A trait representing error values to be converted into an HTTP response. /// -/// The role of this trait is similar to `IntoResponse`, but there are the following -/// differences: +/// Roughly speaking, this trait extends `failure::Fail`, a nearly standard of +/// error abstraction in Rust, and provides the additional properties for handling +/// the value as an HTTP response. /// -/// * `HttpError::into_response` is infallible. -/// * The error values are stored as an object. -pub trait HttpError: fmt::Display + fmt::Debug + Send + 'static + Sized { - type Body: Into; - - /// Consumes itself and creates an HTTP response from its value. - fn into_response(self, request: &Request<()>) -> Response; -} - -impl HttpError for StatusCode { - type Body = (); - - fn into_response(self, _: &Request<()>) -> Response { +/// Note that this trait is defined as a sub trait of `AsFail` (not `Fail` itself), +/// due to some restrictions of the current trait system. +pub trait HttpError: AsFail + fmt::Debug + fmt::Display + Send + Sync + 'static { + /// Returns an HTTP status code associated with this error value. + fn status_code(&self) -> StatusCode; + + /// Creates an HTTP response from this error value. + fn to_response(&self) -> Response<()> { let mut response = Response::new(()); - *response.status_mut() = self; + *response.status_mut() = self.status_code(); response } -} -/// The implementation of `HttpError` for the standard I/O error. -impl HttpError for io::Error { - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - Response::builder() - .status(match self.kind() { - io::ErrorKind::NotFound => StatusCode::NOT_FOUND, - io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN, - _ => StatusCode::INTERNAL_SERVER_ERROR, - }) - .body(format!("I/O error: {}", self)) - .expect("should be a valid response") + // not a public API. + #[doc(hidden)] + fn __private_type_id__(&self) -> TypeId { + TypeId::of::() } } -/// The implementation of `HttpError` for the generic error provided by `failure`. -impl HttpError for failure::Error { - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(format!("generic error: {}", self)) - .expect("should be a valid response") +impl dyn HttpError { + /// Returns whether the concrete type of this object is the same as `T` or not. + pub fn is(&self) -> bool { + self.__private_type_id__() == TypeId::of::() } -} -impl HttpError for Never { - type Body = ResponseBody; - - fn into_response(self, _: &Request<()>) -> Response { - match self {} + /// Attempts to downcast this object into the specified concrete type as a reference. + pub fn downcast_ref(&self) -> Option<&T> { + if self.is::() { + unsafe { Some(&*(self as *const Self as *const T)) } + } else { + None + } } -} -/// An error type which wraps a `Display`able value. -#[derive(Debug)] -pub struct ErrorResponse { - inner: Response, -} - -#[allow(missing_docs)] -impl ErrorResponse -where - T: fmt::Debug + fmt::Display + Send + 'static, -{ - pub fn new(inner: Response) -> Self { - debug_assert!(inner.status().is_client_error() || inner.status().is_server_error()); - Self { inner } + /// Attempts to downcast this object into the specified concrete type as a mutable reference. + pub fn downcast_mut(&mut self) -> Option<&mut T> { + if self.is::() { + unsafe { Some(&mut *(self as *mut Self as *mut T)) } + } else { + None + } } - pub fn into_inner(self) -> Response { - self.inner + /// Attempts to downcast this object into the specified concrete type without unboxing. + pub fn downcast(self: Box) -> std::result::Result, Box> { + if self.is::() { + unsafe { Ok(Box::from_raw(Box::into_raw(self) as *mut T)) } + } else { + Err(self) + } } } -impl From> for ErrorResponse -where - D: fmt::Debug + fmt::Display + Send + 'static, -{ - fn from(response: Response) -> Self { - Self::new(response) +/// The implementation of `HttpError` for the standard I/O error. +impl HttpError for io::Error { + fn status_code(&self) -> StatusCode { + match self.kind() { + io::ErrorKind::NotFound => StatusCode::NOT_FOUND, + io::ErrorKind::PermissionDenied => StatusCode::FORBIDDEN, + _ => StatusCode::INTERNAL_SERVER_ERROR, + } } } -impl fmt::Display for ErrorResponse -where - D: fmt::Debug + fmt::Display + Send + 'static, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.inner.body(), f) +impl HttpError for failure::Error { + fn status_code(&self) -> StatusCode { + StatusCode::INTERNAL_SERVER_ERROR } } -impl HttpError for ErrorResponse -where - D: fmt::Debug + fmt::Display + Send + 'static, -{ - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - self.inner.map(|body| body.to_string()) +impl HttpError for Never { + fn status_code(&self) -> StatusCode { + match *self {} } } -#[allow(missing_docs)] -pub fn error_response(response: Response) -> Error +/// Creates an `Error` from the specified message and HTTP status code. +pub fn err_msg(status: StatusCode, msg: D) -> Error where - D: fmt::Debug + fmt::Display + Send + 'static, + D: fmt::Debug + fmt::Display + Send + Sync + 'static, { - ErrorResponse::new(response).into() -} + #[derive(Debug, failure::Fail)] + #[fail(display = "{}", msg)] + struct ErrorMessage + where + D: fmt::Debug + fmt::Display + Send + Sync + 'static, + { + status: StatusCode, + msg: D, + } -#[allow(missing_docs)] -pub fn custom(status: StatusCode, msg: D) -> Error -where - D: fmt::Debug + fmt::Display + Send + 'static, -{ - let mut response = Response::new(msg); - *response.status_mut() = status; - error_response(response) + impl HttpError for ErrorMessage + where + D: fmt::Debug + fmt::Display + Send + Sync + 'static, + { + fn status_code(&self) -> StatusCode { + self.status + } + } + + ErrorMessage { status, msg }.into() } -macro_rules! define_errors { +macro_rules! define_custom_errors { ($( $(#[$m:meta])* $name:ident => $STATUS:ident, @@ -153,124 +137,165 @@ macro_rules! define_errors { #[inline] pub fn $name(msg: D) -> Error where - D: fmt::Debug + fmt::Display + Send + 'static, + D: fmt::Debug + fmt::Display + Send + Sync + 'static, { - self::custom(StatusCode::$STATUS, msg) + self::err_msg(StatusCode::$STATUS, msg) } )*}; } -define_errors! { - /// Equivalent to `custom(StatusCode::BAD_REQUEST, msg)`. +define_custom_errors! { + /// Equivalent to `err_msg(StatusCode::BAD_REQUEST, msg)`. bad_request => BAD_REQUEST, - /// Equivalent to `custom(StatusCode::UNAUTHORIZED, msg)`. + /// Equivalent to `err_msg(StatusCode::UNAUTHORIZED, msg)`. unauthorized => UNAUTHORIZED, - /// Equivalent to `custom(StatusCode::FORBIDDEN, msg)`. + /// Equivalent to `err_msg(StatusCode::FORBIDDEN, msg)`. forbidden => FORBIDDEN, - /// Equivalent to `custom(StatusCode::NOT_FOUND, msg)`. + /// Equivalent to `err_msg(StatusCode::NOT_FOUND, msg)`. not_found => NOT_FOUND, - /// Equivalent to `custom(StatusCode::METHOD_NOT_ALLOWED, msg)`. + /// Equivalent to `err_msg(StatusCode::METHOD_NOT_ALLOWED, msg)`. method_not_allowed => METHOD_NOT_ALLOWED, - /// Equivalent to `custom(StatusCode::INTERNAL_SERVER_ERROR, msg)`. + /// Equivalent to `err_msg(StatusCode::INTERNAL_SERVER_ERROR, msg)`. internal_server_error => INTERNAL_SERVER_ERROR, } -// ==== Error ==== +type DynStdError = dyn std::error::Error + Send + Sync + 'static; -type AnyObj = dyn Any + Send + 'static; +/// A wrapper type for treating arbitrary error type as an `HttpError`. +/// +/// The value of this type are built at constructing `Error` from +/// `Box`. +#[derive(Debug, failure::Fail)] +#[fail(display = "{}", _0)] +pub struct BoxedStdCompat(Box); + +impl BoxedStdCompat { + pub fn into_inner(self) -> Box { + self.0 + } -/// A custom trait object which holds all kinds of errors occurring in handlers. -pub struct Error { - obj: Box, - fmt_debug_fn: fn(&AnyObj, &mut fmt::Formatter<'_>) -> fmt::Result, - fmt_display_fn: fn(&AnyObj, &mut fmt::Formatter<'_>) -> fmt::Result, - into_response_fn: fn(Box, &Request<()>) -> Response, -} + pub fn get_ref(&self) -> &DynStdError { + &*self.0 + } -impl fmt::Debug for Error { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.fmt_debug_fn)(&self.obj, formatter) + pub fn downcast(self) -> std::result::Result + where + T: std::error::Error + Send + Sync + 'static, + { + self.0.downcast::().map(|t| *t).map_err(BoxedStdCompat) + } + + pub fn downcast_ref(&self) -> Option<&T> + where + T: std::error::Error + Send + Sync + 'static, + { + self.0.downcast_ref::() } } -impl fmt::Display for Error { - fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - (self.fmt_display_fn)(&self.obj, formatter) +impl HttpError for BoxedStdCompat { + fn status_code(&self) -> StatusCode { + StatusCode::INTERNAL_SERVER_ERROR } } +// ==== Error ==== + +/// A type that contains arbitrary HTTP error values. +#[derive(Debug)] +pub struct Error { + inner: Box, +} + impl From for Error where E: HttpError, { fn from(err: E) -> Self { - Self::new(err) + Self { + inner: Box::new(err), + } } } -impl Error { - pub fn new(err: E) -> Self { - fn fmt_debug(this: &AnyObj, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let this = this.downcast_ref::().expect("the wrong type id"); - fmt::Debug::fmt(this, f) - } +impl From for Error { + fn from(status: StatusCode) -> Self { + #[derive(Debug, failure::Fail)] + #[fail(display = "{}", _0)] + struct CompatStatus(StatusCode); - fn fmt_display(this: &AnyObj, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let this = this.downcast_ref::().expect("the wrong type id"); - fmt::Display::fmt(this, f) + impl HttpError for CompatStatus { + fn status_code(&self) -> StatusCode { + self.0 + } } - fn into_response( - this: Box, - request: &Request<()>, - ) -> Response { - let this = *this.downcast::().expect("the wrong type id"); - HttpError::into_response(this, request).map(Into::into) + Self { + inner: Box::new(CompatStatus(status)), } + } +} - Error { - obj: Box::new(err), - fmt_debug_fn: fmt_debug::, - fmt_display_fn: fmt_display::, - into_response_fn: into_response::, +impl From> for Error { + fn from(e: Box) -> Self { + Self { + inner: Box::new(BoxedStdCompat(e)), } } +} - /// Returns `true` if the inner error value has the type of `T`. - #[inline] - pub fn is(&self) -> bool { - self.obj.is::() +impl Error { + /// Returns an HTTP status code associated with the underlying error value. + pub fn status_code(&self) -> StatusCode { + self.inner.status_code() + } + + /// Creates an HTTP response from the underlying error value. + pub fn to_response(&self) -> Response<()> { + self.inner.to_response() + } + + /// Attempts to downcast the underlying error value into the specified concrete type. + pub fn downcast(self) -> Result { + match self.inner.downcast::() { + Ok(t) => Ok(*t), + Err(inner) => Err(Self { inner }), + } } - /// Attempts to downcast this error value to the specified concrete type by reference. - #[inline] + /// Attempts to downcast the underlying error value into the specified concrete type as a reference. pub fn downcast_ref(&self) -> Option<&T> { - self.obj.downcast_ref() + self.inner.downcast_ref() } +} - /// Attempts to downcast this error value to the specified concrete type by reference. - #[inline] - pub fn downcast_mut(&mut self) -> Option<&mut T> { - self.obj.downcast_mut() +impl fmt::Display for Error { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self.inner, formatter) } +} - /// Attempts to downcast this error value into the specified concrete type. - #[inline] - pub fn downcast(self) -> std::result::Result { - if self.obj.is::() { - Ok(*self.obj.downcast().expect("never fails")) - } else { - Err(self) - } +impl AsFail for Error { + fn as_fail(&self) -> &dyn Fail { + self.inner.as_fail() } +} - /// Consumes itself and creates an HTTP response from its value. - pub fn into_response(self, request: &Request<()>) -> Response { - (self.into_response_fn)(self.obj, request) +impl IntoResponse for Error { + type Body = ResponseBody; + type Error = Never; + + fn into_response( + self, + _: &Request<()>, + ) -> std::result::Result, Self::Error> { + let (mut parts, ()) = self.inner.to_response().into_parts(); + parts.extensions.insert(self); + Ok(Response::from_parts(parts, ResponseBody::empty())) } } diff --git a/tsukuyomi/src/input/body.rs b/tsukuyomi/src/input/body.rs index 870794995..492c102e3 100644 --- a/tsukuyomi/src/input/body.rs +++ b/tsukuyomi/src/input/body.rs @@ -5,7 +5,7 @@ use { crate::error::HttpError, bytes::{Buf, BufMut, Bytes, BytesMut}, futures01::{Future, Poll, Stream}, - http::{Request, Response, StatusCode}, + http::StatusCode, izanami_util::{ buf_stream::{BufStream, SizeHint}, http::{Upgrade, Upgraded}, @@ -71,12 +71,8 @@ impl std::error::Error for Error { } impl HttpError for Error { - type Body = String; - - fn into_response(self, _: &Request<()>) -> Response { - let mut response = Response::new(self.to_string()); - *response.status_mut() = StatusCode::INTERNAL_SERVER_ERROR; - response + fn status_code(&self) -> StatusCode { + StatusCode::INTERNAL_SERVER_ERROR } } diff --git a/tsukuyomi/tests/integration_tests/extract.rs b/tsukuyomi/tests/integration_tests/extract.rs index 9f42441ea..6e4ef2f09 100644 --- a/tsukuyomi/tests/integration_tests/extract.rs +++ b/tsukuyomi/tests/integration_tests/extract.rs @@ -370,7 +370,6 @@ fn optional() -> izanami::Result<()> { .body(&b"id=23&name=bob"[..]), )?; assert_eq!(response.status(), 500); - assert_eq!(response.body().to_utf8()?, "####none####"); Ok(()) }