1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

Re-apply patch from #637 #894

This commit is contained in:
Nikolay Kim 2019-06-12 16:45:05 +06:00
parent 2ffda29f9b
commit 7450ae37a7
3 changed files with 93 additions and 10 deletions

View File

@ -14,6 +14,8 @@
* Allow to test an app that uses async actors #897 * Allow to test an app that uses async actors #897
* Re-apply patch from #637 #894
## [1.0.0] - 2019-06-05 ## [1.0.0] - 2019-06-05

View File

@ -27,3 +27,4 @@ time = "0.1.42"
[dev-dependencies] [dev-dependencies]
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-http = "0.2.3" actix-http = "0.2.3"
bytes = "0.4"

View File

@ -175,15 +175,15 @@ where
#[inline] #[inline]
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
let req2 = req.clone(); let req2 = req.clone();
let (limit, err) = req let (limit, err, ctype) = req
.app_data::<Self::Config>() .app_data::<Self::Config>()
.map(|c| (c.limit, c.ehandler.clone())) .map(|c| (c.limit, c.ehandler.clone(), c.content_type.clone()))
.unwrap_or((32768, None)); .unwrap_or((32768, None, None));
let path = req.path().to_string(); let path = req.path().to_string();
Box::new( Box::new(
JsonBody::new(req, payload) JsonBody::new(req, payload, ctype)
.limit(limit) .limit(limit)
.map_err(move |e| { .map_err(move |e| {
log::debug!( log::debug!(
@ -224,6 +224,9 @@ where
/// // change json extractor configuration /// // change json extractor configuration
/// web::Json::<Info>::configure(|cfg| { /// web::Json::<Info>::configure(|cfg| {
/// cfg.limit(4096) /// cfg.limit(4096)
/// .content_type(|mime| { // <- accept text/plain content type
/// mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
/// })
/// .error_handler(|err, req| { // <- create custom error response /// .error_handler(|err, req| { // <- create custom error response
/// error::InternalError::from_response( /// error::InternalError::from_response(
/// err, HttpResponse::Conflict().finish()).into() /// err, HttpResponse::Conflict().finish()).into()
@ -237,6 +240,7 @@ where
pub struct JsonConfig { pub struct JsonConfig {
limit: usize, limit: usize,
ehandler: Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>, ehandler: Option<Arc<dyn Fn(JsonPayloadError, &HttpRequest) -> Error + Send + Sync>>,
content_type: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
} }
impl JsonConfig { impl JsonConfig {
@ -254,6 +258,15 @@ impl JsonConfig {
self.ehandler = Some(Arc::new(f)); self.ehandler = Some(Arc::new(f));
self self
} }
/// Set predicate for allowed content types
pub fn content_type<F>(mut self, predicate: F) -> Self
where
F: Fn(mime::Mime) -> bool + Send + Sync + 'static,
{
self.content_type = Some(Arc::new(predicate));
self
}
} }
impl Default for JsonConfig { impl Default for JsonConfig {
@ -261,6 +274,7 @@ impl Default for JsonConfig {
JsonConfig { JsonConfig {
limit: 32768, limit: 32768,
ehandler: None, ehandler: None,
content_type: None,
} }
} }
} }
@ -271,6 +285,7 @@ impl Default for JsonConfig {
/// Returns error: /// Returns error:
/// ///
/// * content type is not `application/json` /// * content type is not `application/json`
/// (unless specified in [`JsonConfig`](struct.JsonConfig.html))
/// * content length is greater than 256k /// * content length is greater than 256k
pub struct JsonBody<U> { pub struct JsonBody<U> {
limit: usize, limit: usize,
@ -285,13 +300,20 @@ where
U: DeserializeOwned + 'static, U: DeserializeOwned + 'static,
{ {
/// Create `JsonBody` for request. /// Create `JsonBody` for request.
pub fn new(req: &HttpRequest, payload: &mut Payload) -> Self { pub fn new(
req: &HttpRequest,
payload: &mut Payload,
ctype: Option<Arc<dyn Fn(mime::Mime) -> bool + Send + Sync>>,
) -> Self {
// check content-type // check content-type
let json = if let Ok(Some(mime)) = req.mime_type() { let json = if let Ok(Some(mime)) = req.mime_type() {
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON) mime.subtype() == mime::JSON
|| mime.suffix() == Some(mime::JSON)
|| ctype.as_ref().map_or(false, |predicate| predicate(mime))
} else { } else {
false false
}; };
if !json { if !json {
return JsonBody { return JsonBody {
limit: 262_144, limit: 262_144,
@ -512,7 +534,7 @@ mod tests {
#[test] #[test]
fn test_json_body() { fn test_json_body() {
let (req, mut pl) = TestRequest::default().to_http_parts(); let (req, mut pl) = TestRequest::default().to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl)); let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -521,7 +543,7 @@ mod tests {
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
) )
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl)); let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::ContentType));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -535,7 +557,7 @@ mod tests {
) )
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl).limit(100)); let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None).limit(100));
assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow)); assert!(json_eq(json.err().unwrap(), JsonPayloadError::Overflow));
let (req, mut pl) = TestRequest::default() let (req, mut pl) = TestRequest::default()
@ -550,7 +572,7 @@ mod tests {
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.to_http_parts(); .to_http_parts();
let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl)); let json = block_on(JsonBody::<MyObject>::new(&req, &mut pl, None));
assert_eq!( assert_eq!(
json.ok().unwrap(), json.ok().unwrap(),
MyObject { MyObject {
@ -558,4 +580,62 @@ mod tests {
} }
); );
} }
#[test]
fn test_with_json_and_bad_content_type() {
let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
)
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data(JsonConfig::default().limit(4096))
.to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
assert!(s.is_err())
}
#[test]
fn test_with_json_and_good_custom_content_type() {
let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/plain"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
)
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data(JsonConfig::default().content_type(|mime: mime::Mime| {
mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
}))
.to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
assert!(s.is_ok())
}
#[test]
fn test_with_json_and_bad_custom_content_type() {
let (req, mut pl) = TestRequest::with_header(
header::CONTENT_TYPE,
header::HeaderValue::from_static("text/html"),
)
.header(
header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"),
)
.set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.data(JsonConfig::default().content_type(|mime: mime::Mime| {
mime.type_() == mime::TEXT && mime.subtype() == mime::PLAIN
}))
.to_http_parts();
let s = block_on(Json::<MyObject>::from_request(&req, &mut pl));
assert!(s.is_err())
}
} }