[Rust Server] Allow configuration of multipart/form attachment size limit (#19371)

* [Rust Server] Allow configuration of multipart/form attachment size limit

multipart 0.14+ imposes a 8MB size limit on multipart/form bodies.

This allows that limit to be configured. The default is left as is.

This also improves error messages produced when handling multipart/form bodies.

* Update samples
This commit is contained in:
Richard Whitehouse 2024-08-17 09:02:20 +01:00 committed by GitHub
parent bb831dad9a
commit 0a5c99739a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 432 additions and 172 deletions

View File

@ -19,7 +19,7 @@ use mime_multipart::{read_multipart_body, Node, Part};
{{/apiUsesMultipartRelated}} {{/apiUsesMultipartRelated}}
{{#apiUsesMultipartFormData}} {{#apiUsesMultipartFormData}}
use multipart::server::Multipart; use multipart::server::Multipart;
use multipart::server::save::SaveResult; use multipart::server::save::{PartialReason, SaveResult};
{{/apiUsesMultipartFormData}} {{/apiUsesMultipartFormData}}
#[allow(unused_imports)] #[allow(unused_imports)]

View File

@ -4,6 +4,9 @@ pub struct MakeService<T, C> where
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
{{#apiUsesMultipartFormData}}
multipart_form_size_limit: Option<u64>,
{{/apiUsesMultipartFormData}}
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -14,11 +17,25 @@ impl<T, C> MakeService<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
api_impl, api_impl,
{{#apiUsesMultipartFormData}}
multipart_form_size_limit: Some(8 * 1024 * 1024),
{{/apiUsesMultipartFormData}}
marker: PhantomData marker: PhantomData
} }
} }
} {{#apiUsesMultipartFormData}}
/// Configure size limit when inspecting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
{{/apiUsesMultipartFormData}}
}
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
@ -33,8 +50,11 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone()){{^apiUsesMultipartFormData}};{{/apiUsesMultipartFormData}}
self.api_impl.clone(), {{#apiUsesMultipartFormData}}
)) .multipart_form_size_limit(self.multipart_form_size_limit);
{{/apiUsesMultipartFormData}}
future::ok(service)
} }
} }

View File

@ -9,15 +9,43 @@
use std::io::Read; use std::io::Read;
// Read Form Parameters from body // Read Form Parameters from body
let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary).save().temp() { let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary)
.save()
.size_limit(multipart_form_size_limit)
.temp()
{
SaveResult::Full(entries) => { SaveResult::Full(entries) => {
entries entries
}, },
_ => { SaveResult::Partial(_, PartialReason::CountLimit) => {
return Ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process all message parts".to_string())) .body(Body::from("Unable to process message part due to excessive parts".to_string()))
.expect("Unable to create Bad Request response due to failure to process all message")) .expect("Unable to create Bad Request response due to excessive parts"))
},
SaveResult::Partial(_, PartialReason::SizeLimit) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to excessive data".to_string()))
.expect("Unable to create Bad Request response due to excessive data"))
},
SaveResult::Partial(_, PartialReason::Utf8Error(_)) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to invalid data".to_string()))
.expect("Unable to create Bad Request response due to invalid data"))
},
SaveResult::Partial(_, PartialReason::IoError(_)) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process message part due an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal errror"))
},
SaveResult::Error(e) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process all message parts due to an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal error"))
}, },
}; };
{{#formParams}} {{#formParams}}

View File

@ -2,5 +2,13 @@
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
{{#apiUsesMultipartFormData}}
self.multipart_form_size_limit,
{{/apiUsesMultipartFormData}}
))
}
} }

View File

@ -11,6 +11,9 @@ pub struct Service<T, C> where
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
{{#apiUsesMultipart}}
multipart_form_size_limit: Option<u64>,
{{/apiUsesMultipart}}
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -21,9 +24,24 @@ impl<T, C> Service<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl, api_impl,
{{#apiUsesMultipart}}
multipart_form_size_limit: Some(8 * 1024 * 1024),
{{/apiUsesMultipart}}
marker: PhantomData marker: PhantomData
} }
} }
{{#apiUsesMultipart}}
/// Configure size limit when extracting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
{{/apiUsesMultipart}}
} }
impl<T, C> Clone for Service<T, C> where impl<T, C> Clone for Service<T, C> where
@ -33,6 +51,9 @@ impl<T, C> Clone for Service<T, C> where
fn clone(&self) -> Self { fn clone(&self) -> Self {
Service { Service {
api_impl: self.api_impl.clone(), api_impl: self.api_impl.clone(),
{{#apiUsesMultipart}}
multipart_form_size_limit: Some(8 * 1024 * 1024),
{{/apiUsesMultipart}}
marker: self.marker, marker: self.marker,
} }
} }
@ -50,7 +71,14 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
{{#apiUsesMultipartFormData}}
multipart_form_size_limit: Option<u64>,
{{/apiUsesMultipartFormData}}
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static C: Has<XSpanIdString> {{#hasAuthMethods}}+ Has<Option<Authorization>>{{/hasAuthMethods}} + Send + Sync + 'static
{ {

View File

@ -16,7 +16,7 @@ use hyper_0_10::header::{Headers, ContentType};
use mime_0_2::{TopLevel, SubLevel, Mime as Mime2}; use mime_0_2::{TopLevel, SubLevel, Mime as Mime2};
use mime_multipart::{read_multipart_body, Node, Part}; use mime_multipart::{read_multipart_body, Node, Part};
use multipart::server::Multipart; use multipart::server::Multipart;
use multipart::server::save::SaveResult; use multipart::server::save::{PartialReason, SaveResult};
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::{models, header, AuthenticationApi}; use crate::{models, header, AuthenticationApi};
@ -55,6 +55,7 @@ pub struct MakeService<T, C> where
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
multipart_form_size_limit: Option<u64>,
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -65,11 +66,21 @@ impl<T, C> MakeService<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
api_impl, api_impl,
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: PhantomData marker: PhantomData
} }
} }
}
/// Configure size limit when inspecting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
}
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
@ -84,9 +95,10 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone())
self.api_impl.clone(), .multipart_form_size_limit(self.multipart_form_size_limit);
))
future::ok(service)
} }
} }
@ -103,6 +115,7 @@ pub struct Service<T, C> where
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
multipart_form_size_limit: Option<u64>,
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -113,9 +126,20 @@ impl<T, C> Service<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl, api_impl,
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: PhantomData marker: PhantomData
} }
} }
/// Configure size limit when extracting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
} }
impl<T, C> Clone for Service<T, C> where impl<T, C> Clone for Service<T, C> where
@ -125,6 +149,7 @@ impl<T, C> Clone for Service<T, C> where
fn clone(&self) -> Self { fn clone(&self) -> Self {
Service { Service {
api_impl: self.api_impl.clone(), api_impl: self.api_impl.clone(),
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: self.marker, marker: self.marker,
} }
} }
@ -142,7 +167,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
multipart_form_size_limit: Option<u64>,
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
@ -303,15 +333,43 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
use std::io::Read; use std::io::Read;
// Read Form Parameters from body // Read Form Parameters from body
let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary).save().temp() { let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary)
.save()
.size_limit(multipart_form_size_limit)
.temp()
{
SaveResult::Full(entries) => { SaveResult::Full(entries) => {
entries entries
}, },
_ => { SaveResult::Partial(_, PartialReason::CountLimit) => {
return Ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process all message parts".to_string())) .body(Body::from("Unable to process message part due to excessive parts".to_string()))
.expect("Unable to create Bad Request response due to failure to process all message")) .expect("Unable to create Bad Request response due to excessive parts"))
},
SaveResult::Partial(_, PartialReason::SizeLimit) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to excessive data".to_string()))
.expect("Unable to create Bad Request response due to excessive data"))
},
SaveResult::Partial(_, PartialReason::Utf8Error(_)) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to invalid data".to_string()))
.expect("Unable to create Bad Request response due to invalid data"))
},
SaveResult::Partial(_, PartialReason::IoError(_)) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process message part due an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal errror"))
},
SaveResult::Error(e) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process all message parts due to an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal error"))
}, },
}; };
let field_string_field = entries.fields.remove("string_field"); let field_string_field = entries.fields.remove("string_field");
@ -557,7 +615,13 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
self.multipart_form_size_limit,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -59,7 +59,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
@ -73,9 +72,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -131,7 +130,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
@ -221,7 +224,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -72,7 +72,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
@ -86,9 +85,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -145,7 +144,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
@ -265,7 +268,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -144,7 +144,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
@ -158,9 +157,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -216,7 +215,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
@ -1935,7 +1938,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -167,7 +167,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
@ -181,9 +180,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -239,7 +238,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
@ -1401,7 +1404,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -13,7 +13,7 @@ pub use swagger::auth::Authorization;
use swagger::auth::Scopes; use swagger::auth::Scopes;
use url::form_urlencoded; use url::form_urlencoded;
use multipart::server::Multipart; use multipart::server::Multipart;
use multipart::server::save::SaveResult; use multipart::server::save::{PartialReason, SaveResult};
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::{models, header, AuthenticationApi}; use crate::{models, header, AuthenticationApi};
@ -162,6 +162,7 @@ pub struct MakeService<T, C> where
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
multipart_form_size_limit: Option<u64>,
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -172,11 +173,21 @@ impl<T, C> MakeService<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
MakeService { MakeService {
api_impl, api_impl,
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: PhantomData marker: PhantomData
} }
} }
}
/// Configure size limit when inspecting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
}
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
@ -191,9 +202,10 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone())
self.api_impl.clone(), .multipart_form_size_limit(self.multipart_form_size_limit);
))
future::ok(service)
} }
} }
@ -210,6 +222,7 @@ pub struct Service<T, C> where
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
api_impl: T, api_impl: T,
multipart_form_size_limit: Option<u64>,
marker: PhantomData<C>, marker: PhantomData<C>,
} }
@ -220,9 +233,20 @@ impl<T, C> Service<T, C> where
pub fn new(api_impl: T) -> Self { pub fn new(api_impl: T) -> Self {
Service { Service {
api_impl, api_impl,
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: PhantomData marker: PhantomData
} }
} }
/// Configure size limit when extracting a multipart/form body.
///
/// Default is 8 MiB.
///
/// Set to None for no size limit, which presents a Denial of Service attack risk.
pub fn multipart_form_size_limit(mut self, multipart_form_size_limit: Option<u64>) -> Self {
self.multipart_form_size_limit = multipart_form_size_limit;
self
}
} }
impl<T, C> Clone for Service<T, C> where impl<T, C> Clone for Service<T, C> where
@ -232,6 +256,7 @@ impl<T, C> Clone for Service<T, C> where
fn clone(&self) -> Self { fn clone(&self) -> Self {
Service { Service {
api_impl: self.api_impl.clone(), api_impl: self.api_impl.clone(),
multipart_form_size_limit: Some(8 * 1024 * 1024),
marker: self.marker, marker: self.marker,
} }
} }
@ -249,7 +274,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
multipart_form_size_limit: Option<u64>,
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
@ -2148,15 +2178,43 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
use std::io::Read; use std::io::Read;
// Read Form Parameters from body // Read Form Parameters from body
let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary).save().temp() { let mut entries = match Multipart::with_body(&body.to_vec()[..], boundary)
.save()
.size_limit(multipart_form_size_limit)
.temp()
{
SaveResult::Full(entries) => { SaveResult::Full(entries) => {
entries entries
}, },
_ => { SaveResult::Partial(_, PartialReason::CountLimit) => {
return Ok(Response::builder() return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST) .status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process all message parts".to_string())) .body(Body::from("Unable to process message part due to excessive parts".to_string()))
.expect("Unable to create Bad Request response due to failure to process all message")) .expect("Unable to create Bad Request response due to excessive parts"))
},
SaveResult::Partial(_, PartialReason::SizeLimit) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to excessive data".to_string()))
.expect("Unable to create Bad Request response due to excessive data"))
},
SaveResult::Partial(_, PartialReason::Utf8Error(_)) => {
return Ok(Response::builder()
.status(StatusCode::BAD_REQUEST)
.body(Body::from("Unable to process message part due to invalid data".to_string()))
.expect("Unable to create Bad Request response due to invalid data"))
},
SaveResult::Partial(_, PartialReason::IoError(_)) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process message part due an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal errror"))
},
SaveResult::Error(e) => {
return Ok(Response::builder()
.status(StatusCode::INTERNAL_SERVER_ERROR)
.body(Body::from("Failed to process all message parts due to an internal error".to_string()))
.expect("Unable to create Internal Server Error response due to an internal error"))
}, },
}; };
let field_additional_metadata = entries.fields.remove("additional_metadata"); let field_additional_metadata = entries.fields.remove("additional_metadata");
@ -3171,7 +3229,13 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
self.multipart_form_size_limit,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -59,7 +59,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
@ -73,9 +72,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -131,7 +130,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static C: Has<XSpanIdString> + Has<Option<Authorization>> + Send + Sync + 'static
{ {
@ -187,7 +190,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.

View File

@ -81,7 +81,6 @@ impl<T, C> MakeService<T, C> where
} }
} }
impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
@ -95,9 +94,9 @@ impl<T, C, Target> hyper::service::Service<Target> for MakeService<T, C> where
} }
fn call(&mut self, target: Target) -> Self::Future { fn call(&mut self, target: Target) -> Self::Future {
future::ok(Service::new( let service = Service::new(self.api_impl.clone());
self.api_impl.clone(),
)) future::ok(service)
} }
} }
@ -153,7 +152,11 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
self.api_impl.poll_ready(cx) self.api_impl.poll_ready(cx)
} }
fn call(&mut self, req: (Request<Body>, C)) -> Self::Future { async fn run<T, C>(mut api_impl: T, req: (Request<Body>, C)) -> Result<Response<Body>, crate::ServiceError> where fn call(&mut self, req: (Request<Body>, C)) -> Self::Future {
async fn run<T, C>(
mut api_impl: T,
req: (Request<Body>, C),
) -> Result<Response<Body>, crate::ServiceError> where
T: Api<C> + Clone + Send + 'static, T: Api<C> + Clone + Send + 'static,
C: Has<XSpanIdString> + Send + Sync + 'static C: Has<XSpanIdString> + Send + Sync + 'static
{ {
@ -640,7 +643,12 @@ impl<T, C> hyper::service::Service<(Request<Body>, C)> for Service<T, C> where
.body(Body::empty()) .body(Body::empty())
.expect("Unable to create Not Found response")) .expect("Unable to create Not Found response"))
} }
} Box::pin(run(self.api_impl.clone(), req)) } }
Box::pin(run(
self.api_impl.clone(),
req,
))
}
} }
/// Request parser for `Api`. /// Request parser for `Api`.