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:
64
src/error.rs
64
src/error.rs
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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(),
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user