mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-27 17:52:56 +01:00
Fix body propagation in Response::from_error. #760
This commit is contained in:
parent
9bb40c249f
commit
9d82d4dfb9
@ -18,6 +18,10 @@
|
|||||||
|
|
||||||
* Move multipart support to actix-multipart crate
|
* Move multipart support to actix-multipart crate
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Fix body propagation in Response::from_error. #760
|
||||||
|
|
||||||
|
|
||||||
## [1.0.0-alpha.3] - 2019-04-02
|
## [1.0.0-alpha.3] - 2019-04-02
|
||||||
|
|
||||||
|
@ -29,6 +29,7 @@ members = [
|
|||||||
"awc",
|
"awc",
|
||||||
"actix-http",
|
"actix-http",
|
||||||
"actix-files",
|
"actix-files",
|
||||||
|
"actix-framed",
|
||||||
"actix-session",
|
"actix-session",
|
||||||
"actix-multipart",
|
"actix-multipart",
|
||||||
"actix-web-actors",
|
"actix-web-actors",
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
//! Error and Result module
|
//! Error and Result module
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::io::Write;
|
||||||
use std::str::Utf8Error;
|
use std::str::Utf8Error;
|
||||||
use std::string::FromUtf8Error;
|
use std::string::FromUtf8Error;
|
||||||
use std::{fmt, io, result};
|
use std::{fmt, io, result};
|
||||||
|
|
||||||
pub use actix_threadpool::BlockingError;
|
pub use actix_threadpool::BlockingError;
|
||||||
use actix_utils::timeout::TimeoutError;
|
use actix_utils::timeout::TimeoutError;
|
||||||
|
use bytes::BytesMut;
|
||||||
use derive_more::{Display, From};
|
use derive_more::{Display, From};
|
||||||
use futures::Canceled;
|
use futures::Canceled;
|
||||||
use http::uri::InvalidUri;
|
use http::uri::InvalidUri;
|
||||||
@ -17,7 +19,9 @@ use serde_urlencoded::ser::Error as FormError;
|
|||||||
use tokio_timer::Error as TimerError;
|
use tokio_timer::Error as TimerError;
|
||||||
|
|
||||||
// re-export for convinience
|
// re-export for convinience
|
||||||
|
use crate::body::Body;
|
||||||
pub use crate::cookie::ParseError as CookieParseError;
|
pub use crate::cookie::ParseError as CookieParseError;
|
||||||
|
use crate::helpers::Writer;
|
||||||
use crate::response::Response;
|
use crate::response::Response;
|
||||||
|
|
||||||
/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
|
/// A specialized [`Result`](https://doc.rust-lang.org/std/result/enum.Result.html)
|
||||||
@ -57,6 +61,18 @@ pub trait ResponseError: fmt::Debug + fmt::Display {
|
|||||||
fn error_response(&self) -> Response {
|
fn error_response(&self) -> Response {
|
||||||
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
|
Response::new(StatusCode::INTERNAL_SERVER_ERROR)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs an error response
|
||||||
|
fn render_response(&self) -> Response {
|
||||||
|
let mut resp = self.error_response();
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
let _ = write!(Writer(&mut buf), "{}", self);
|
||||||
|
resp.headers_mut().insert(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("text/plain"),
|
||||||
|
);
|
||||||
|
resp.set_body(Body::from(buf))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
@ -477,7 +493,16 @@ where
|
|||||||
{
|
{
|
||||||
fn error_response(&self) -> Response {
|
fn error_response(&self) -> Response {
|
||||||
match self.status {
|
match self.status {
|
||||||
InternalErrorType::Status(st) => Response::new(st),
|
InternalErrorType::Status(st) => {
|
||||||
|
let mut res = Response::new(st);
|
||||||
|
let mut buf = BytesMut::new();
|
||||||
|
let _ = write!(Writer(&mut buf), "{}", self);
|
||||||
|
res.headers_mut().insert(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("text/plain"),
|
||||||
|
);
|
||||||
|
res.set_body(Body::from(buf))
|
||||||
|
}
|
||||||
InternalErrorType::Response(ref resp) => {
|
InternalErrorType::Response(ref resp) => {
|
||||||
if let Some(resp) = resp.borrow_mut().take() {
|
if let Some(resp) = resp.borrow_mut().take() {
|
||||||
resp
|
resp
|
||||||
@ -487,6 +512,11 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Constructs an error response
|
||||||
|
fn render_response(&self) -> Response {
|
||||||
|
self.error_response()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert Response to a Error
|
/// Convert Response to a Error
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
use std::{io, mem, ptr, slice};
|
||||||
|
|
||||||
use bytes::{BufMut, BytesMut};
|
use bytes::{BufMut, BytesMut};
|
||||||
use http::Version;
|
use http::Version;
|
||||||
use std::{mem, ptr, slice};
|
|
||||||
|
|
||||||
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
|
||||||
2021222324252627282930313233343536373839\
|
2021222324252627282930313233343536373839\
|
||||||
@ -167,6 +168,18 @@ pub(crate) fn convert_usize(mut n: usize, bytes: &mut BytesMut) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct Writer<'a>(pub &'a mut BytesMut);
|
||||||
|
|
||||||
|
impl<'a> io::Write for Writer<'a> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.0.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Http response
|
//! Http response
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{fmt, io, str};
|
use std::{fmt, str};
|
||||||
|
|
||||||
use bytes::{BufMut, Bytes, BytesMut};
|
use bytes::{BufMut, Bytes, BytesMut};
|
||||||
use futures::future::{ok, FutureResult, IntoFuture};
|
use futures::future::{ok, FutureResult, IntoFuture};
|
||||||
@ -51,13 +51,9 @@ impl Response<Body> {
|
|||||||
/// Constructs an error response
|
/// Constructs an error response
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_error(error: Error) -> Response {
|
pub fn from_error(error: Error) -> Response {
|
||||||
let mut resp = error.as_response_error().error_response();
|
let mut resp = error.as_response_error().render_response();
|
||||||
let mut buf = BytesMut::new();
|
|
||||||
let _ = write!(Writer(&mut buf), "{}", error);
|
|
||||||
resp.headers_mut()
|
|
||||||
.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain"));
|
|
||||||
resp.error = Some(error);
|
resp.error = Some(error);
|
||||||
resp.set_body(Body::from(buf))
|
resp
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert response to response with body
|
/// Convert response to response with body
|
||||||
@ -309,18 +305,6 @@ impl<'a> Iterator for CookieIter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Writer<'a>(pub &'a mut BytesMut);
|
|
||||||
|
|
||||||
impl<'a> io::Write for Writer<'a> {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.extend_from_slice(buf);
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An HTTP response builder
|
/// An HTTP response builder
|
||||||
///
|
///
|
||||||
/// This type can be used to construct an instance of `Response` through a
|
/// This type can be used to construct an instance of `Response` through a
|
||||||
|
@ -64,7 +64,7 @@ where
|
|||||||
InitError = (),
|
InitError = (),
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
/// Set application data. Applicatin data could be accessed
|
/// Set application data. Application data could be accessed
|
||||||
/// by using `Data<T>` extractor where `T` is data type.
|
/// by using `Data<T>` extractor where `T` is data type.
|
||||||
///
|
///
|
||||||
/// **Note**: http server accepts an application factory rather than
|
/// **Note**: http server accepts an application factory rather than
|
||||||
|
@ -25,7 +25,7 @@ pub(crate) trait DataFactoryResult {
|
|||||||
/// during application configuration process
|
/// during application configuration process
|
||||||
/// with `App::data()` method.
|
/// with `App::data()` method.
|
||||||
///
|
///
|
||||||
/// Applicatin data could be accessed by using `Data<T>`
|
/// Application data could be accessed by using `Data<T>`
|
||||||
/// extractor where `T` is data type.
|
/// extractor where `T` is data type.
|
||||||
///
|
///
|
||||||
/// **Note**: http server accepts an application factory rather than
|
/// **Note**: http server accepts an application factory rather than
|
||||||
|
@ -31,7 +31,7 @@ pub enum UrlencodedError {
|
|||||||
#[display(fmt = "Can not decode chunked transfer encoding")]
|
#[display(fmt = "Can not decode chunked transfer encoding")]
|
||||||
Chunked,
|
Chunked,
|
||||||
/// Payload size is bigger than allowed. (default: 256kB)
|
/// Payload size is bigger than allowed. (default: 256kB)
|
||||||
#[display(fmt = "Urlencoded payload size is bigger than allowed. (default: 256kB)")]
|
#[display(fmt = "Urlencoded payload size is bigger than allowed (default: 256kB)")]
|
||||||
Overflow,
|
Overflow,
|
||||||
/// Payload size is now known
|
/// Payload size is now known
|
||||||
#[display(fmt = "Payload size is now known")]
|
#[display(fmt = "Payload size is now known")]
|
||||||
@ -66,7 +66,7 @@ impl ResponseError for UrlencodedError {
|
|||||||
#[derive(Debug, Display, From)]
|
#[derive(Debug, Display, From)]
|
||||||
pub enum JsonPayloadError {
|
pub enum JsonPayloadError {
|
||||||
/// Payload size is bigger than allowed. (default: 32kB)
|
/// Payload size is bigger than allowed. (default: 32kB)
|
||||||
#[display(fmt = "Json payload size is bigger than allowed.")]
|
#[display(fmt = "Json payload size is bigger than allowed")]
|
||||||
Overflow,
|
Overflow,
|
||||||
/// Content type error
|
/// Content type error
|
||||||
#[display(fmt = "Content type error")]
|
#[display(fmt = "Content type error")]
|
||||||
|
@ -365,8 +365,10 @@ mod tests {
|
|||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::error::InternalError;
|
||||||
use crate::http::header;
|
use crate::http::header;
|
||||||
use crate::test::{block_on, TestRequest};
|
use crate::test::{block_on, TestRequest};
|
||||||
|
use crate::HttpResponse;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
struct MyObject {
|
struct MyObject {
|
||||||
@ -405,6 +407,37 @@ mod tests {
|
|||||||
assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}");
|
assert_eq!(resp.body().bin_ref(), b"{\"name\":\"test\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_custom_error_responder() {
|
||||||
|
let (req, mut pl) = TestRequest::default()
|
||||||
|
.header(
|
||||||
|
header::CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/json"),
|
||||||
|
)
|
||||||
|
.header(
|
||||||
|
header::CONTENT_LENGTH,
|
||||||
|
header::HeaderValue::from_static("16"),
|
||||||
|
)
|
||||||
|
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
|
||||||
|
.route_data(JsonConfig::default().limit(10).error_handler(|err, _| {
|
||||||
|
let msg = MyObject {
|
||||||
|
name: "invalid request".to_string(),
|
||||||
|
};
|
||||||
|
let resp = HttpResponse::BadRequest()
|
||||||
|
.body(serde_json::to_string(&msg).unwrap());
|
||||||
|
InternalError::from_response(err, resp).into()
|
||||||
|
}))
|
||||||
|
.to_http_parts();
|
||||||
|
|
||||||
|
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
|
||||||
|
let mut resp = Response::from_error(s.err().unwrap().into());
|
||||||
|
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
|
||||||
|
|
||||||
|
let body = block_on(resp.take_body().concat2()).unwrap();
|
||||||
|
let msg: MyObject = serde_json::from_slice(&body).unwrap();
|
||||||
|
assert_eq!(msg.name, "invalid request");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract() {
|
fn test_extract() {
|
||||||
let (req, mut pl) = TestRequest::default()
|
let (req, mut pl) = TestRequest::default()
|
||||||
@ -443,7 +476,7 @@ mod tests {
|
|||||||
|
|
||||||
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
|
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
|
||||||
assert!(format!("{}", s.err().unwrap())
|
assert!(format!("{}", s.err().unwrap())
|
||||||
.contains("Json payload size is bigger than allowed."));
|
.contains("Json payload size is bigger than allowed"));
|
||||||
|
|
||||||
let (req, mut pl) = TestRequest::default()
|
let (req, mut pl) = TestRequest::default()
|
||||||
.header(
|
.header(
|
||||||
|
@ -107,7 +107,7 @@ impl TestServer {
|
|||||||
TestServerRuntime { addr, rt, client }
|
TestServerRuntime { addr, rt, client }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get firat available unused address
|
/// Get first available unused address
|
||||||
pub fn unused_addr() -> net::SocketAddr {
|
pub fn unused_addr() -> net::SocketAddr {
|
||||||
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||||
let socket = TcpBuilder::new_v4().unwrap();
|
let socket = TcpBuilder::new_v4().unwrap();
|
||||||
|
Loading…
Reference in New Issue
Block a user