diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-imports.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-imports.mustache index be6effa720c..1b39e6808be 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-imports.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-imports.mustache @@ -19,7 +19,7 @@ use mime_multipart::{read_multipart_body, Node, Part}; {{/apiUsesMultipartRelated}} {{#apiUsesMultipartFormData}} use multipart::server::Multipart; -use multipart::server::save::SaveResult; +use multipart::server::save::{PartialReason, SaveResult}; {{/apiUsesMultipartFormData}} #[allow(unused_imports)] diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-make-service.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-make-service.mustache index 5f5359de68a..7471c9c2278 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-make-service.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-make-service.mustache @@ -4,6 +4,9 @@ pub struct MakeService where C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Send + Sync + 'static { api_impl: T, +{{#apiUsesMultipartFormData}} + multipart_form_size_limit: Option, +{{/apiUsesMultipartFormData}} marker: PhantomData, } @@ -14,11 +17,25 @@ impl MakeService where pub fn new(api_impl: T) -> Self { MakeService { api_impl, +{{#apiUsesMultipartFormData}} + multipart_form_size_limit: Some(8 * 1024 * 1024), +{{/apiUsesMultipartFormData}} 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } +{{/apiUsesMultipartFormData}} +} impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, @@ -33,8 +50,11 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()){{^apiUsesMultipartFormData}};{{/apiUsesMultipartFormData}} +{{#apiUsesMultipartFormData}} + .multipart_form_size_limit(self.multipart_form_size_limit); +{{/apiUsesMultipartFormData}} + + future::ok(service) } } diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-request-body-multipart-form.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-request-body-multipart-form.mustache index 4c594df5627..c5022acf84b 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-request-body-multipart-form.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-request-body-multipart-form.mustache @@ -9,15 +9,43 @@ use std::io::Read; // 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) => { entries }, - _ => { + SaveResult::Partial(_, PartialReason::CountLimit) => { return Ok(Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from("Unable to process all message parts".to_string())) - .expect("Unable to create Bad Request response due to failure to process all message")) + .body(Body::from("Unable to process message part due to excessive parts".to_string())) + .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}} diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-service-footer.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-service-footer.mustache index 25e9c12a5b3..793cd99e5cc 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-service-footer.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-service-footer.mustache @@ -1,6 +1,14 @@ - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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}} + )) + } } diff --git a/modules/openapi-generator/src/main/resources/rust-server/server-service-header.mustache b/modules/openapi-generator/src/main/resources/rust-server/server-service-header.mustache index 01fde31fa1e..997b5b5c9f0 100644 --- a/modules/openapi-generator/src/main/resources/rust-server/server-service-header.mustache +++ b/modules/openapi-generator/src/main/resources/rust-server/server-service-header.mustache @@ -11,6 +11,9 @@ pub struct Service where C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Send + Sync + 'static { api_impl: T, +{{#apiUsesMultipart}} + multipart_form_size_limit: Option, +{{/apiUsesMultipart}} marker: PhantomData, } @@ -21,9 +24,24 @@ impl Service where pub fn new(api_impl: T) -> Self { Service { api_impl, +{{#apiUsesMultipart}} + multipart_form_size_limit: Some(8 * 1024 * 1024), +{{/apiUsesMultipart}} 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } +{{/apiUsesMultipart}} } impl Clone for Service where @@ -33,6 +51,9 @@ impl Clone for Service where fn clone(&self) -> Self { Service { api_impl: self.api_impl.clone(), +{{#apiUsesMultipart}} + multipart_form_size_limit: Some(8 * 1024 * 1024), +{{/apiUsesMultipart}} marker: self.marker, } } @@ -50,17 +71,24 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), +{{#apiUsesMultipartFormData}} + multipart_form_size_limit: Option, +{{/apiUsesMultipartFormData}} + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has {{#hasAuthMethods}}+ Has>{{/hasAuthMethods}} + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - {{! - This match statement is duplicated below in `parse_operation_id()`. - Please update both places if changing how this code is autogenerated. - }} - match method { + {{! + This match statement is duplicated below in `parse_operation_id()`. + Please update both places if changing how this code is autogenerated. + }} + match method { diff --git a/samples/server/petstore/rust-server/output/multipart-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/multipart-v3/src/server/mod.rs index a87d3ba9074..e9296a9524a 100644 --- a/samples/server/petstore/rust-server/output/multipart-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/multipart-v3/src/server/mod.rs @@ -16,7 +16,7 @@ use hyper_0_10::header::{Headers, ContentType}; use mime_0_2::{TopLevel, SubLevel, Mime as Mime2}; use mime_multipart::{read_multipart_body, Node, Part}; use multipart::server::Multipart; -use multipart::server::save::SaveResult; +use multipart::server::save::{PartialReason, SaveResult}; #[allow(unused_imports)] use crate::{models, header, AuthenticationApi}; @@ -55,6 +55,7 @@ pub struct MakeService where C: Has + Send + Sync + 'static { api_impl: T, + multipart_form_size_limit: Option, marker: PhantomData, } @@ -65,11 +66,21 @@ impl MakeService where pub fn new(api_impl: T) -> Self { MakeService { api_impl, + multipart_form_size_limit: Some(8 * 1024 * 1024), 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } +} impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, @@ -84,9 +95,10 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()) + .multipart_form_size_limit(self.multipart_form_size_limit); + + future::ok(service) } } @@ -103,6 +115,7 @@ pub struct Service where C: Has + Send + Sync + 'static { api_impl: T, + multipart_form_size_limit: Option, marker: PhantomData, } @@ -113,9 +126,20 @@ impl Service where pub fn new(api_impl: T) -> Self { Service { api_impl, + multipart_form_size_limit: Some(8 * 1024 * 1024), 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } } impl Clone for Service where @@ -125,6 +149,7 @@ impl Clone for Service where fn clone(&self) -> Self { Service { api_impl: self.api_impl.clone(), + multipart_form_size_limit: Some(8 * 1024 * 1024), marker: self.marker, } } @@ -142,16 +167,21 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + multipart_form_size_limit: Option, + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // MultipartRelatedRequestPost - POST /multipart_related_request hyper::Method::POST if path.matched(paths::ID_MULTIPART_RELATED_REQUEST) => { @@ -303,15 +333,43 @@ impl hyper::service::Service<(Request, C)> for Service where use std::io::Read; // 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) => { entries }, - _ => { + SaveResult::Partial(_, PartialReason::CountLimit) => { return Ok(Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from("Unable to process all message parts".to_string())) - .expect("Unable to create Bad Request response due to failure to process all message")) + .body(Body::from("Unable to process message part due to excessive parts".to_string())) + .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"); @@ -553,11 +611,17 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_MULTIPART_RELATED_REQUEST) => method_not_allowed(), _ if path.matched(paths::ID_MULTIPART_REQUEST) => method_not_allowed(), _ if path.matched(paths::ID_MULTIPLE_IDENTICAL_MIME_TYPES) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/no-example-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/no-example-v3/src/server/mod.rs index 6146bbb51ad..c9632d351d2 100644 --- a/samples/server/petstore/rust-server/output/no-example-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/no-example-v3/src/server/mod.rs @@ -59,7 +59,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Send + Sync + 'static @@ -73,9 +72,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -131,16 +130,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // OpGet - GET /op hyper::Method::GET if path.matched(paths::ID_OP) => { @@ -217,11 +220,16 @@ impl hyper::service::Service<(Request, C)> for Service where }, _ if path.matched(paths::ID_OP) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/client/callbacks.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/client/callbacks.rs index 71ba30387a5..97198a90b7e 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/client/callbacks.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/client/callbacks.rs @@ -72,7 +72,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Has> + Send + Sync + 'static @@ -86,9 +85,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -145,16 +144,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Has> + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Has> + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // CallbackCallbackWithHeaderPost - POST /{$request.query.url}/callback-with-header hyper::Method::POST if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK_WITH_HEADER) => { @@ -261,11 +264,16 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK) => method_not_allowed(), _ if path.matched(paths::ID_REQUEST_QUERY_URL_CALLBACK_WITH_HEADER) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs index b0de9a68581..af72c843ad0 100644 --- a/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/openapi-v3/src/server/mod.rs @@ -144,7 +144,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Has> + Send + Sync + 'static @@ -158,9 +157,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -216,16 +215,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Has> + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Has> + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // AnyOfGet - GET /any-of hyper::Method::GET if path.matched(paths::ID_ANY_OF) => { @@ -1931,11 +1934,16 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_XML) => method_not_allowed(), _ if path.matched(paths::ID_XML_EXTRA) => method_not_allowed(), _ if path.matched(paths::ID_XML_OTHER) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/ops-v3/src/server/mod.rs b/samples/server/petstore/rust-server/output/ops-v3/src/server/mod.rs index c4c97c93de3..47846655bfb 100644 --- a/samples/server/petstore/rust-server/output/ops-v3/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/ops-v3/src/server/mod.rs @@ -167,7 +167,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Send + Sync + 'static @@ -181,9 +180,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -239,16 +238,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // Op10Get - GET /op10 hyper::Method::GET if path.matched(paths::ID_OP10) => { @@ -1397,11 +1400,16 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_OP7) => method_not_allowed(), _ if path.matched(paths::ID_OP8) => method_not_allowed(), _ if path.matched(paths::ID_OP9) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs index ca9745ecf37..95092bccc4a 100644 --- a/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/petstore-with-fake-endpoints-models-for-testing/src/server/mod.rs @@ -13,7 +13,7 @@ pub use swagger::auth::Authorization; use swagger::auth::Scopes; use url::form_urlencoded; use multipart::server::Multipart; -use multipart::server::save::SaveResult; +use multipart::server::save::{PartialReason, SaveResult}; #[allow(unused_imports)] use crate::{models, header, AuthenticationApi}; @@ -162,6 +162,7 @@ pub struct MakeService where C: Has + Has> + Send + Sync + 'static { api_impl: T, + multipart_form_size_limit: Option, marker: PhantomData, } @@ -172,11 +173,21 @@ impl MakeService where pub fn new(api_impl: T) -> Self { MakeService { api_impl, + multipart_form_size_limit: Some(8 * 1024 * 1024), 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } +} impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, @@ -191,9 +202,10 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()) + .multipart_form_size_limit(self.multipart_form_size_limit); + + future::ok(service) } } @@ -210,6 +222,7 @@ pub struct Service where C: Has + Has> + Send + Sync + 'static { api_impl: T, + multipart_form_size_limit: Option, marker: PhantomData, } @@ -220,9 +233,20 @@ impl Service where pub fn new(api_impl: T) -> Self { Service { api_impl, + multipart_form_size_limit: Some(8 * 1024 * 1024), 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) -> Self { + self.multipart_form_size_limit = multipart_form_size_limit; + self + } } impl Clone for Service where @@ -232,6 +256,7 @@ impl Clone for Service where fn clone(&self) -> Self { Service { api_impl: self.api_impl.clone(), + multipart_form_size_limit: Some(8 * 1024 * 1024), marker: self.marker, } } @@ -249,16 +274,21 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Has> + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + multipart_form_size_limit: Option, + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Has> + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // TestSpecialTags - PATCH /another-fake/dummy hyper::Method::PATCH if path.matched(paths::ID_ANOTHER_FAKE_DUMMY) => { @@ -2148,15 +2178,43 @@ impl hyper::service::Service<(Request, C)> for Service where use std::io::Read; // 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) => { entries }, - _ => { + SaveResult::Partial(_, PartialReason::CountLimit) => { return Ok(Response::builder() .status(StatusCode::BAD_REQUEST) - .body(Body::from("Unable to process all message parts".to_string())) - .expect("Unable to create Bad Request response due to failure to process all message")) + .body(Body::from("Unable to process message part due to excessive parts".to_string())) + .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"); @@ -3167,11 +3225,17 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_USER_LOGIN) => method_not_allowed(), _ if path.matched(paths::ID_USER_LOGOUT) => method_not_allowed(), _ if path.matched(paths::ID_USER_USERNAME) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/server/mod.rs b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/server/mod.rs index 09cb3242c61..04b60fe0454 100644 --- a/samples/server/petstore/rust-server/output/ping-bearer-auth/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/ping-bearer-auth/src/server/mod.rs @@ -59,7 +59,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Has> + Send + Sync + 'static @@ -73,9 +72,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -131,16 +130,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Has> + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Has> + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // PingGet - GET /ping hyper::Method::GET if path.matched(paths::ID_PING) => { @@ -183,11 +186,16 @@ impl hyper::service::Service<(Request, C)> for Service where }, _ if path.matched(paths::ID_PING) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`. diff --git a/samples/server/petstore/rust-server/output/rust-server-test/src/server/mod.rs b/samples/server/petstore/rust-server/output/rust-server-test/src/server/mod.rs index 2ff7ad28aeb..d2e153e4ac5 100644 --- a/samples/server/petstore/rust-server/output/rust-server-test/src/server/mod.rs +++ b/samples/server/petstore/rust-server/output/rust-server-test/src/server/mod.rs @@ -81,7 +81,6 @@ impl MakeService where } } - impl hyper::service::Service for MakeService where T: Api + Clone + Send + 'static, C: Has + Send + Sync + 'static @@ -95,9 +94,9 @@ impl hyper::service::Service for MakeService where } fn call(&mut self, target: Target) -> Self::Future { - future::ok(Service::new( - self.api_impl.clone(), - )) + let service = Service::new(self.api_impl.clone()); + + future::ok(service) } } @@ -153,16 +152,20 @@ impl hyper::service::Service<(Request, C)> for Service where self.api_impl.poll_ready(cx) } - fn call(&mut self, req: (Request, C)) -> Self::Future { async fn run(mut api_impl: T, req: (Request, C)) -> Result, crate::ServiceError> where - T: Api + Clone + Send + 'static, - C: Has + Send + Sync + 'static - { - let (request, context) = req; - let (parts, body) = request.into_parts(); - let (method, uri, headers) = (parts.method, parts.uri, parts.headers); - let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); + fn call(&mut self, req: (Request, C)) -> Self::Future { + async fn run( + mut api_impl: T, + req: (Request, C), + ) -> Result, crate::ServiceError> where + T: Api + Clone + Send + 'static, + C: Has + Send + Sync + 'static + { + let (request, context) = req; + let (parts, body) = request.into_parts(); + let (method, uri, headers) = (parts.method, parts.uri, parts.headers); + let path = paths::GLOBAL_REGEX_SET.matches(uri.path()); - match method { + match method { // AllOfGet - GET /allOf hyper::Method::GET if path.matched(paths::ID_ALLOF) => { @@ -636,11 +639,16 @@ impl hyper::service::Service<(Request, C)> for Service where _ if path.matched(paths::ID_POST_YAML) => method_not_allowed(), _ if path.matched(paths::ID_RAW_JSON) => method_not_allowed(), _ if path.matched(paths::ID_SOLO_OBJECT) => method_not_allowed(), - _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Unable to create Not Found response")) + _ => Ok(Response::builder().status(StatusCode::NOT_FOUND) + .body(Body::empty()) + .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`.