1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-01-18 05:41:50 +01:00

Log request processing errors

This commit is contained in:
Nikolay Kim 2018-01-20 20:12:24 -08:00
parent 98931a8623
commit 7cf221f767
6 changed files with 75 additions and 24 deletions

View File

@ -4,10 +4,13 @@
* Fix HEAD requests handling * Fix HEAD requests handling
* Can't have multiple Applications on a single server with different state #49 * Log request processing errors
* Allow multiple Applications on a single server with different state #49
* CORS middleware: allowed_headers is defaulting to None #50 * CORS middleware: allowed_headers is defaulting to None #50
## 0.3.1 (2018-01-13) ## 0.3.1 (2018-01-13)
* Fix directory entry path #47 * Fix directory entry path #47

View File

@ -81,7 +81,7 @@ version = "0.9"
optional = true optional = true
[dev-dependencies] [dev-dependencies]
env_logger = "0.4" env_logger = "0.5"
reqwest = "0.8" reqwest = "0.8"
skeptic = "0.13" skeptic = "0.13"
serde_derive = "1.0" serde_derive = "1.0"

View File

@ -6,6 +6,6 @@ workspace = "../.."
[dependencies] [dependencies]
futures = "*" futures = "*"
env_logger = "0.4" env_logger = "0.5"
actix = "0.4" actix = "0.4"
actix-web = { path="../.." } actix-web = { path="../.." }

View File

@ -7,6 +7,7 @@ extern crate env_logger;
extern crate futures; extern crate futures;
use futures::Stream; use futures::Stream;
use std::{io, env};
use actix_web::*; use actix_web::*;
use actix_web::middleware::RequestSession; use actix_web::middleware::RequestSession;
use futures::future::{FutureResult, result}; use futures::future::{FutureResult, result};
@ -56,17 +57,17 @@ fn index(mut req: HttpRequest) -> Result<HttpResponse> {
fn p404(req: HttpRequest) -> Result<HttpResponse> { fn p404(req: HttpRequest) -> Result<HttpResponse> {
// html // html
let html = format!(r#"<!DOCTYPE html><html><head><title>actix - basics</title><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /></head> let html = r#"<!DOCTYPE html><html><head><title>actix - basics</title><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /></head>
<body> <body>
<a href="index.html">back to home</a> <a href="index.html">back to home</a>
<h1>404</h1> <h1>404</h1>
</body> </body>
</html>"#); </html>"#;
// response // response
Ok(HttpResponse::build(StatusCode::NOT_FOUND) Ok(HttpResponse::build(StatusCode::NOT_FOUND)
.content_type("text/html; charset=utf-8") .content_type("text/html; charset=utf-8")
.body(&html).unwrap()) .body(html).unwrap())
} }
@ -92,8 +93,9 @@ fn with_param(req: HttpRequest) -> Result<HttpResponse>
} }
fn main() { fn main() {
::std::env::set_var("RUST_LOG", "actix_web=info"); env::set_var("RUST_LOG", "actix_web=debug");
let _ = env_logger::init(); env::set_var("RUST_BACKTRACE", "1");
env_logger::init();
let sys = actix::System::new("basic-example"); let sys = actix::System::new("basic-example");
let addr = HttpServer::new( let addr = HttpServer::new(
@ -121,6 +123,9 @@ fn main() {
_ => httpcodes::HTTPNotFound, _ => httpcodes::HTTPNotFound,
} }
})) }))
.resource("/error.html", |r| r.f(|req| {
error::ErrorBadRequest(io::Error::new(io::ErrorKind::Other, "test"))
}))
// static files // static files
.handler("/static/", fs::StaticFiles::new("../static/", true)) .handler("/static/", fs::StaticFiles::new("../static/", true))
// redirect // redirect

View File

@ -9,8 +9,8 @@ use std::error::Error as StdError;
use cookie; use cookie;
use httparse; use httparse;
use failure::Fail;
use futures::Canceled; use futures::Canceled;
use failure::{Fail, Backtrace};
use http2::Error as Http2Error; use http2::Error as Http2Error;
use http::{header, StatusCode, Error as HttpError}; use http::{header, StatusCode, Error as HttpError};
use http::uri::InvalidUriBytes; use http::uri::InvalidUriBytes;
@ -22,6 +22,8 @@ use url::ParseError as UrlParseError;
pub use cookie::{ParseError as CookieParseError}; pub use cookie::{ParseError as CookieParseError};
use body::Body; use body::Body;
use handler::Responder;
use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed, HTTPExpectationFailed}; use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed, HTTPExpectationFailed};
@ -33,9 +35,9 @@ use httpcodes::{HTTPBadRequest, HTTPMethodNotAllowed, HTTPExpectationFailed};
pub type Result<T, E=Error> = result::Result<T, E>; pub type Result<T, E=Error> = result::Result<T, E>;
/// General purpose actix web error /// General purpose actix web error
#[derive(Fail, Debug)]
pub struct Error { pub struct Error {
cause: Box<ResponseError>, cause: Box<ResponseError>,
backtrace: Option<Backtrace>,
} }
impl Error { impl Error {
@ -64,6 +66,16 @@ impl fmt::Display for Error {
} }
} }
impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.backtrace.is_none() {
fmt::Debug::fmt(&self.cause, f)
} else {
write!(f, "{:?}\n\n{:?}", &self.cause, self.backtrace.as_ref().unwrap())
}
}
}
/// `HttpResponse` for `Error` /// `HttpResponse` for `Error`
impl From<Error> for HttpResponse { impl From<Error> for HttpResponse {
fn from(err: Error) -> Self { fn from(err: Error) -> Self {
@ -74,7 +86,12 @@ impl From<Error> for HttpResponse {
/// `Error` for any error that implements `ResponseError` /// `Error` for any error that implements `ResponseError`
impl<T: ResponseError> From<T> for Error { impl<T: ResponseError> From<T> for Error {
fn from(err: T) -> Error { fn from(err: T) -> Error {
Error { cause: Box::new(err) } let backtrace = if err.backtrace().is_none() {
Some(Backtrace::new())
} else {
None
};
Error { cause: Box::new(err), backtrace: backtrace }
} }
} }
@ -489,21 +506,37 @@ macro_rules! ERROR_WRAP {
} }
} }
impl<T: fmt::Debug + 'static> Fail for $type {} impl<T: fmt::Display + fmt::Debug + 'static> Fail for $type {}
impl<T: fmt::Debug + 'static> fmt::Display for $type { impl<T: Fail> Fail for $type {
fn backtrace(&self) -> Option<&Backtrace> {
self.cause().backtrace()
}
}
impl<T: fmt::Display + 'static> fmt::Display for $type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.0) fmt::Display::fmt(&self.0, f)
} }
} }
impl<T> ResponseError for $type impl<T> ResponseError for $type
where T: Send + Sync + fmt::Debug + 'static, where T: Send + Sync + fmt::Debug + fmt::Display + 'static
{ {
fn error_response(&self) -> HttpResponse { fn error_response(&self) -> HttpResponse {
HttpResponse::new($status, Body::Empty) HttpResponse::new($status, Body::Empty)
} }
} }
impl<T> Responder for $type
where T: Send + Sync + fmt::Debug + fmt::Display + 'static
{
type Item = HttpResponse;
type Error = Error;
fn respond_to(self, _: HttpRequest) -> Result<HttpResponse, Error> {
Err(self.into())
}
}
} }
} }

View File

@ -3,6 +3,7 @@ use std::rc::Rc;
use std::cell::RefCell; use std::cell::RefCell;
use std::marker::PhantomData; use std::marker::PhantomData;
use log::Level::Debug;
use futures::{Async, Poll, Future, Stream}; use futures::{Async, Poll, Future, Stream};
use futures::unsync::oneshot; use futures::unsync::oneshot;
@ -56,7 +57,7 @@ impl<S: 'static, H: PipelineHandler<S>> PipelineState<S, H> {
struct PipelineInfo<S> { struct PipelineInfo<S> {
req: HttpRequest<S>, req: HttpRequest<S>,
count: usize, count: u16,
mws: Rc<Vec<Box<Middleware<S>>>>, mws: Rc<Vec<Box<Middleware<S>>>>,
context: Option<Box<ActorHttpContext>>, context: Option<Box<ActorHttpContext>>,
error: Option<Error>, error: Option<Error>,
@ -211,13 +212,13 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
fn init(info: &mut PipelineInfo<S>, handler: Rc<RefCell<H>>) -> PipelineState<S, H> { fn init(info: &mut PipelineInfo<S>, handler: Rc<RefCell<H>>) -> PipelineState<S, H> {
// execute middlewares, we need this stage because middlewares could be non-async // execute middlewares, we need this stage because middlewares could be non-async
// and we can move to next state immediately // and we can move to next state immediately
let len = info.mws.len(); let len = info.mws.len() as u16;
loop { loop {
if info.count == len { if info.count == len {
let reply = handler.borrow_mut().handle(info.req.clone()); let reply = handler.borrow_mut().handle(info.req.clone());
return WaitingResponse::init(info, reply) return WaitingResponse::init(info, reply)
} else { } else {
match info.mws[info.count].start(&mut info.req) { match info.mws[info.count as usize].start(&mut info.req) {
Ok(Started::Done) => Ok(Started::Done) =>
info.count += 1, info.count += 1,
Ok(Started::Response(resp)) => Ok(Started::Response(resp)) =>
@ -246,7 +247,7 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
} }
fn poll(&mut self, info: &mut PipelineInfo<S>) -> Option<PipelineState<S, H>> { fn poll(&mut self, info: &mut PipelineInfo<S>) -> Option<PipelineState<S, H>> {
let len = info.mws.len(); let len = info.mws.len() as u16;
'outer: loop { 'outer: loop {
match self.fut.as_mut().unwrap().poll() { match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None, Ok(Async::NotReady) => return None,
@ -260,7 +261,7 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
return Some(WaitingResponse::init(info, reply)); return Some(WaitingResponse::init(info, reply));
} else { } else {
loop { loop {
match info.mws[info.count].start(info.req_mut()) { match info.mws[info.count as usize].start(info.req_mut()) {
Ok(Started::Done) => Ok(Started::Done) =>
info.count += 1, info.count += 1,
Ok(Started::Response(resp)) => { Ok(Started::Response(resp)) => {
@ -334,7 +335,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
loop { loop {
resp = match info.mws[curr].response(info.req_mut(), resp) { resp = match info.mws[curr].response(info.req_mut(), resp) {
Err(err) => { Err(err) => {
info.count = curr + 1; info.count = (curr + 1) as u16;
return ProcessResponse::init(err.into()) return ProcessResponse::init(err.into())
} }
Ok(Response::Done(r)) => { Ok(Response::Done(r)) => {
@ -458,6 +459,13 @@ impl<S: 'static, H> ProcessResponse<S, H> {
} }
}; };
if let Some(err) = self.resp.error() {
warn!("Error occured during request handling: {}", err);
if log_enabled!(Debug) {
debug!("{:?}", err);
}
}
match self.resp.replace_body(Body::Empty) { match self.resp.replace_body(Body::Empty) {
Body::Streaming(stream) => Body::Streaming(stream) =>
self.iostate = IOState::Payload(stream), self.iostate = IOState::Payload(stream),
@ -586,7 +594,6 @@ impl<S: 'static, H> ProcessResponse<S, H> {
}, },
Ok(Async::NotReady) => return Err(PipelineState::Response(self)), Ok(Async::NotReady) => return Err(PipelineState::Response(self)),
Err(err) => { Err(err) => {
debug!("Error sending data: {}", err);
info.error = Some(err.into()); info.error = Some(err.into());
return Ok(FinishingMiddlewares::init(info, self.resp)) return Ok(FinishingMiddlewares::init(info, self.resp))
} }
@ -599,7 +606,6 @@ impl<S: 'static, H> ProcessResponse<S, H> {
match io.write_eof() { match io.write_eof() {
Ok(_) => (), Ok(_) => (),
Err(err) => { Err(err) => {
debug!("Error sending data: {}", err);
info.error = Some(err.into()); info.error = Some(err.into());
return Ok(FinishingMiddlewares::init(info, self.resp)) return Ok(FinishingMiddlewares::init(info, self.resp))
} }
@ -661,7 +667,7 @@ impl<S: 'static, H> FinishingMiddlewares<S, H> {
self.fut = None; self.fut = None;
info.count -= 1; info.count -= 1;
match info.mws[info.count].finish(info.req_mut(), &self.resp) { match info.mws[info.count as usize].finish(info.req_mut(), &self.resp) {
Finished::Done => { Finished::Done => {
if info.count == 0 { if info.count == 0 {
return Some(Completed::init(info)) return Some(Completed::init(info))
@ -682,6 +688,10 @@ impl<S, H> Completed<S, H> {
#[inline] #[inline]
fn init(info: &mut PipelineInfo<S>) -> PipelineState<S, H> { fn init(info: &mut PipelineInfo<S>) -> PipelineState<S, H> {
if let Some(ref err) = info.error {
error!("Error occured during request handling: {}", err);
}
if info.context.is_none() { if info.context.is_none() {
PipelineState::None PipelineState::None
} else { } else {