1
0
mirror of https://github.com/fafhrd91/actix-web synced 2025-07-02 01:05:08 +02:00

remove json and url encoded form support from -http (#2148)

This commit is contained in:
Rob Ede
2021-04-12 10:30:28 +01:00
committed by GitHub
parent 44c55dd036
commit 981c54432c
15 changed files with 137 additions and 258 deletions

View File

@ -3,12 +3,15 @@
pub use actix_http::error::*;
use derive_more::{Display, Error, From};
use serde_json::error::Error as JsonError;
use serde_urlencoded::de::Error as FormDeError;
use serde_urlencoded::ser::Error as FormError;
use url::ParseError as UrlParseError;
use crate::http::StatusCode;
/// Errors which can occur when attempting to generate resource uri.
#[derive(Debug, PartialEq, Display, From)]
#[derive(Debug, PartialEq, Display, Error, From)]
#[non_exhaustive]
pub enum UrlGenerationError {
/// Resource not found
#[display(fmt = "Resource not found")]
@ -23,13 +26,12 @@ pub enum UrlGenerationError {
ParseError(UrlParseError),
}
impl std::error::Error for UrlGenerationError {}
/// `InternalServerError` for `UrlGeneratorError`
impl ResponseError for UrlGenerationError {}
/// A set of errors that can occur during parsing urlencoded payloads
#[derive(Debug, Display, Error, From)]
#[non_exhaustive]
pub enum UrlencodedError {
/// Can not decode chunked transfer encoding.
#[display(fmt = "Can not decode chunked transfer encoding.")]
@ -52,8 +54,16 @@ pub enum UrlencodedError {
ContentType,
/// Parse error.
#[display(fmt = "Parse error.")]
Parse,
#[display(fmt = "Parse error: {}.", _0)]
Parse(FormDeError),
/// Encoding error.
#[display(fmt = "Encoding error.")]
Encoding,
/// Serialize error.
#[display(fmt = "Serialize error: {}.", _0)]
Serialize(FormError),
/// Payload error.
#[display(fmt = "Error that occur during reading payload: {}.", _0)]
@ -63,52 +73,66 @@ pub enum UrlencodedError {
/// Return `BadRequest` for `UrlencodedError`
impl ResponseError for UrlencodedError {
fn status_code(&self) -> StatusCode {
match *self {
UrlencodedError::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
UrlencodedError::UnknownLength => StatusCode::LENGTH_REQUIRED,
match self {
Self::Overflow { .. } => StatusCode::PAYLOAD_TOO_LARGE,
Self::UnknownLength => StatusCode::LENGTH_REQUIRED,
Self::Payload(err) => err.status_code(),
_ => StatusCode::BAD_REQUEST,
}
}
}
/// A set of errors that can occur during parsing json payloads
#[derive(Debug, Display, From)]
#[derive(Debug, Display, Error)]
#[non_exhaustive]
pub enum JsonPayloadError {
/// Payload size is bigger than allowed. (default: 32kB)
#[display(fmt = "Json payload size is bigger than allowed")]
Overflow,
/// Content type error
#[display(fmt = "Content type error")]
ContentType,
/// Deserialize error
#[display(fmt = "Json deserialize error: {}", _0)]
Deserialize(JsonError),
/// Serialize error
#[display(fmt = "Json serialize error: {}", _0)]
Serialize(JsonError),
/// Payload error
#[display(fmt = "Error that occur during reading payload: {}", _0)]
Payload(PayloadError),
}
impl std::error::Error for JsonPayloadError {}
impl From<PayloadError> for JsonPayloadError {
fn from(err: PayloadError) -> Self {
Self::Payload(err)
}
}
impl ResponseError for JsonPayloadError {
fn status_code(&self) -> StatusCode {
match *self {
JsonPayloadError::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
match self {
Self::Overflow => StatusCode::PAYLOAD_TOO_LARGE,
Self::Serialize(_) => StatusCode::INTERNAL_SERVER_ERROR,
Self::Payload(err) => err.status_code(),
_ => StatusCode::BAD_REQUEST,
}
}
}
/// A set of errors that can occur during parsing request paths
#[derive(Debug, Display, From)]
#[derive(Debug, Display, Error)]
#[non_exhaustive]
pub enum PathError {
/// Deserialize error
#[display(fmt = "Path deserialize error: {}", _0)]
Deserialize(serde::de::value::Error),
}
impl std::error::Error for PathError {}
/// Return `BadRequest` for `PathError`
impl ResponseError for PathError {
fn status_code(&self) -> StatusCode {
@ -118,6 +142,7 @@ impl ResponseError for PathError {
/// A set of errors that can occur during parsing query strings.
#[derive(Debug, Display, Error, From)]
#[non_exhaustive]
pub enum QueryPayloadError {
/// Query deserialize error.
#[display(fmt = "Query deserialize error: {}", _0)]
@ -132,25 +157,26 @@ impl ResponseError for QueryPayloadError {
}
/// Error type returned when reading body as lines.
#[derive(From, Display, Debug)]
#[derive(Debug, Display, Error, From)]
#[non_exhaustive]
pub enum ReadlinesError {
/// Error when decoding a line.
#[display(fmt = "Encoding error")]
/// Payload size is bigger than allowed. (default: 256kB)
EncodingError,
/// Payload error.
#[display(fmt = "Error that occur during reading payload: {}", _0)]
Payload(PayloadError),
/// Line limit exceeded.
#[display(fmt = "Line limit exceeded")]
LimitOverflow,
/// ContentType error.
#[display(fmt = "Content-type error")]
ContentTypeError(ContentTypeError),
}
impl std::error::Error for ReadlinesError {}
/// Return `BadRequest` for `ReadlinesError`
impl ResponseError for ReadlinesError {
fn status_code(&self) -> StatusCode {

View File

@ -24,7 +24,7 @@ use actix_http::http::header::HeaderValue;
#[cfg(feature = "cookies")]
use cookie::{Cookie, CookieJar};
use crate::error::Error;
use crate::error::{Error, JsonPayloadError};
/// An HTTP Response
pub struct HttpResponse<B = Body> {
@ -385,7 +385,10 @@ impl HttpResponseBuilder {
}
/// Replaced with [`Self::insert_header()`].
#[deprecated = "Replaced with `insert_header((key, value))`."]
#[deprecated(
since = "4.0.0",
note = "Replaced with `insert_header((key, value))`. Will be removed in v5."
)]
pub fn set_header<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: TryInto<HeaderName>,
@ -406,7 +409,10 @@ impl HttpResponseBuilder {
}
/// Replaced with [`Self::append_header()`].
#[deprecated = "Replaced with `append_header((key, value))`."]
#[deprecated(
since = "4.0.0",
note = "Replaced with `append_header((key, value))`. Will be removed in v5."
)]
pub fn header<K, V>(&mut self, key: K, value: V) -> &mut Self
where
K: TryInto<HeaderName>,
@ -572,7 +578,7 @@ impl HttpResponseBuilder {
/// Set a body and generate `Response`.
///
/// `ResponseBuilder` can not be used after this call.
/// `HttpResponseBuilder` can not be used after this call.
#[inline]
pub fn body<B: Into<Body>>(&mut self, body: B) -> HttpResponse {
self.message_body(body.into())
@ -580,7 +586,7 @@ impl HttpResponseBuilder {
/// Set a body and generate `Response`.
///
/// `ResponseBuilder` can not be used after this call.
/// `HttpResponseBuilder` can not be used after this call.
pub fn message_body<B>(&mut self, body: B) -> HttpResponse<B> {
if let Some(err) = self.err.take() {
return HttpResponse::from_error(Error::from(err)).into_body();
@ -608,7 +614,7 @@ impl HttpResponseBuilder {
/// Set a streaming body and generate `Response`.
///
/// `ResponseBuilder` can not be used after this call.
/// `HttpResponseBuilder` can not be used after this call.
#[inline]
pub fn streaming<S, E>(&mut self, stream: S) -> HttpResponse
where
@ -620,7 +626,7 @@ impl HttpResponseBuilder {
/// Set a json body and generate `Response`
///
/// `ResponseBuilder` can not be used after this call.
/// `HttpResponseBuilder` can not be used after this call.
pub fn json(&mut self, value: impl Serialize) -> HttpResponse {
match serde_json::to_string(&value) {
Ok(body) => {
@ -636,19 +642,19 @@ impl HttpResponseBuilder {
self.body(Body::from(body))
}
Err(e) => HttpResponse::from_error(Error::from(e)),
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
}
}
/// Set an empty body and generate `Response`
///
/// `ResponseBuilder` can not be used after this call.
/// `HttpResponseBuilder` can not be used after this call.
#[inline]
pub fn finish(&mut self) -> HttpResponse {
self.body(Body::Empty)
}
/// This method construct new `ResponseBuilder`
/// This method construct new `HttpResponseBuilder`
pub fn take(&mut self) -> Self {
Self {
head: self.head.take(),
@ -814,32 +820,33 @@ mod http_codes {
#[cfg(test)]
mod tests {
use bytes::{Bytes, BytesMut};
use serde_json::json;
use super::{HttpResponse as Response, HttpResponseBuilder as ResponseBuilder};
use super::{HttpResponse, HttpResponseBuilder};
use crate::dev::{Body, MessageBody, ResponseBody};
use crate::http::header::{self, HeaderValue, CONTENT_TYPE, COOKIE};
use crate::http::StatusCode;
#[test]
fn test_debug() {
let resp = Response::Ok()
let resp = HttpResponse::Ok()
.append_header((COOKIE, HeaderValue::from_static("cookie1=value1; ")))
.append_header((COOKIE, HeaderValue::from_static("cookie2=value2; ")))
.finish();
let dbg = format!("{:?}", resp);
assert!(dbg.contains("Response"));
assert!(dbg.contains("HttpResponse"));
}
#[test]
fn test_basic_builder() {
let resp = Response::Ok().insert_header(("X-TEST", "value")).finish();
let resp = HttpResponse::Ok()
.insert_header(("X-TEST", "value"))
.finish();
assert_eq!(resp.status(), StatusCode::OK);
}
#[test]
fn test_upgrade() {
let resp = ResponseBuilder::new(StatusCode::OK)
let resp = HttpResponseBuilder::new(StatusCode::OK)
.upgrade("websocket")
.finish();
assert!(resp.upgrade());
@ -851,13 +858,15 @@ mod tests {
#[test]
fn test_force_close() {
let resp = ResponseBuilder::new(StatusCode::OK).force_close().finish();
let resp = HttpResponseBuilder::new(StatusCode::OK)
.force_close()
.finish();
assert!(!resp.keep_alive())
}
#[test]
fn test_content_type() {
let resp = ResponseBuilder::new(StatusCode::OK)
let resp = HttpResponseBuilder::new(StatusCode::OK)
.content_type("text/plain")
.body(Body::Empty);
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "text/plain")
@ -878,7 +887,7 @@ mod tests {
#[actix_rt::test]
async fn test_json() {
let mut resp = Response::Ok().json(vec!["v1", "v2", "v3"]);
let mut resp = HttpResponse::Ok().json(vec!["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
assert_eq!(ct, HeaderValue::from_static("application/json"));
assert_eq!(
@ -886,7 +895,7 @@ mod tests {
br#"["v1","v2","v3"]"#
);
let mut resp = Response::Ok().json(&["v1", "v2", "v3"]);
let mut resp = HttpResponse::Ok().json(&["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
assert_eq!(ct, HeaderValue::from_static("application/json"));
assert_eq!(
@ -895,7 +904,7 @@ mod tests {
);
// content type override
let mut resp = Response::Ok()
let mut resp = HttpResponse::Ok()
.insert_header((CONTENT_TYPE, "text/json"))
.json(&vec!["v1", "v2", "v3"]);
let ct = resp.headers().get(CONTENT_TYPE).unwrap();
@ -908,8 +917,10 @@ mod tests {
#[actix_rt::test]
async fn test_serde_json_in_body() {
use serde_json::json;
let mut resp = Response::Ok().body(json!({"test-key":"test-value"}));
let mut resp = HttpResponse::Ok().body(
serde_json::to_vec(&serde_json::json!({ "test-key": "test-value" })).unwrap(),
);
assert_eq!(
read_body(resp.take_body()).await.as_ref(),
br#"{"test-key":"test-value"}"#
@ -918,7 +929,7 @@ mod tests {
#[test]
fn response_builder_header_insert_kv() {
let mut res = Response::Ok();
let mut res = HttpResponse::Ok();
res.insert_header(("Content-Type", "application/octet-stream"));
let res = res.finish();
@ -930,7 +941,7 @@ mod tests {
#[test]
fn response_builder_header_insert_typed() {
let mut res = Response::Ok();
let mut res = HttpResponse::Ok();
res.insert_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
let res = res.finish();
@ -942,7 +953,7 @@ mod tests {
#[test]
fn response_builder_header_append_kv() {
let mut res = Response::Ok();
let mut res = HttpResponse::Ok();
res.append_header(("Content-Type", "application/octet-stream"));
res.append_header(("Content-Type", "application/json"));
let res = res.finish();
@ -955,7 +966,7 @@ mod tests {
#[test]
fn response_builder_header_append_typed() {
let mut res = Response::Ok();
let mut res = HttpResponse::Ok();
res.append_header((header::CONTENT_TYPE, mime::APPLICATION_OCTET_STREAM));
res.append_header((header::CONTENT_TYPE, mime::APPLICATION_JSON));
let res = res.finish();

View File

@ -163,7 +163,7 @@ impl<T: Serialize> Responder for Form<T> {
Ok(body) => HttpResponse::Ok()
.content_type(mime::APPLICATION_WWW_FORM_URLENCODED)
.body(body),
Err(err) => HttpResponse::from_error(err.into()),
Err(err) => HttpResponse::from_error(UrlencodedError::Serialize(err).into()),
}
}
}
@ -346,14 +346,14 @@ where
}
if encoding == UTF_8 {
serde_urlencoded::from_bytes::<T>(&body).map_err(|_| UrlencodedError::Parse)
serde_urlencoded::from_bytes::<T>(&body).map_err(UrlencodedError::Parse)
} else {
let body = encoding
.decode_without_bom_handling_and_without_replacement(&body)
.map(|s| s.into_owned())
.ok_or(UrlencodedError::Parse)?;
.ok_or(UrlencodedError::Encoding)?;
serde_urlencoded::from_str::<T>(&body).map_err(|_| UrlencodedError::Parse)
serde_urlencoded::from_str::<T>(&body).map_err(UrlencodedError::Parse)
}
}
.boxed_local(),

View File

@ -127,7 +127,7 @@ impl<T: Serialize> Responder for Json<T> {
Ok(body) => HttpResponse::Ok()
.content_type(mime::APPLICATION_JSON)
.body(body),
Err(err) => HttpResponse::from_error(err.into()),
Err(err) => HttpResponse::from_error(JsonPayloadError::Serialize(err).into()),
}
}
}
@ -412,7 +412,8 @@ where
}
}
None => {
let json = serde_json::from_slice::<T>(&buf)?;
let json = serde_json::from_slice::<T>(&buf)
.map_err(JsonPayloadError::Deserialize)?;
return Poll::Ready(Ok(json));
}
}

View File

@ -93,7 +93,7 @@ where
T: de::DeserializeOwned,
{
type Error = Error;
type Future = Ready<Result<Self, Error>>;
type Future = Ready<Result<Self, Self::Error>>;
type Config = PathConfig;
#[inline]
@ -106,17 +106,17 @@ where
ready(
de::Deserialize::deserialize(PathDeserializer::new(req.match_info()))
.map(Path)
.map_err(move |e| {
.map_err(move |err| {
log::debug!(
"Failed during Path extractor deserialization. \
Request path: {:?}",
req.path()
);
if let Some(error_handler) = error_handler {
let e = PathError::Deserialize(e);
let e = PathError::Deserialize(err);
(error_handler)(e, req)
} else {
ErrorNotFound(e)
ErrorNotFound(err)
}
}),
)
@ -303,7 +303,7 @@ mod tests {
let s = Path::<(usize,)>::from_request(&req, &mut pl)
.await
.unwrap_err();
let res = HttpResponse::from_error(s.into());
let res = HttpResponse::from_error(s);
assert_eq!(res.status(), http::StatusCode::CONFLICT);
}