1use std::{
4 any::Any,
5 collections::HashMap,
6 future::{ready, Future},
7 sync::Arc,
8};
9
10use actix_web::{dev, error::PayloadError, web, Error, FromRequest, HttpRequest};
11use derive_more::{Deref, DerefMut};
12use futures_core::future::LocalBoxFuture;
13use futures_util::{TryFutureExt as _, TryStreamExt as _};
14
15use crate::{Field, Multipart, MultipartError};
16
17pub mod bytes;
18pub mod json;
19#[cfg(feature = "tempfile")]
20pub mod tempfile;
21pub mod text;
22
23#[cfg(feature = "derive")]
24pub use actix_multipart_derive::MultipartForm;
25
26type FieldErrorHandler<T> = Option<Arc<dyn Fn(T, &HttpRequest) -> Error + Send + Sync>>;
27
28pub trait FieldReader<'t>: Sized + Any {
32 type Future: Future<Output = Result<Self, MultipartError>>;
34
35 fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future;
45}
46
47#[doc(hidden)]
49#[derive(Default, Deref, DerefMut)]
50pub struct State(pub HashMap<String, Box<dyn Any>>);
51
52#[doc(hidden)]
54pub trait FieldGroupReader<'t>: Sized + Any {
55 type Future: Future<Output = Result<(), MultipartError>>;
56
57 fn handle_field(
59 req: &'t HttpRequest,
60 field: Field,
61 limits: &'t mut Limits,
62 state: &'t mut State,
63 duplicate_field: DuplicateField,
64 ) -> Self::Future;
65
66 fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError>;
68}
69
70impl<'t, T> FieldGroupReader<'t> for Option<T>
71where
72 T: FieldReader<'t>,
73{
74 type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;
75
76 fn handle_field(
77 req: &'t HttpRequest,
78 field: Field,
79 limits: &'t mut Limits,
80 state: &'t mut State,
81 duplicate_field: DuplicateField,
82 ) -> Self::Future {
83 if state.contains_key(&field.form_field_name) {
84 match duplicate_field {
85 DuplicateField::Ignore => {
86 return Box::pin(async move { discard_field(field, limits).await });
87 }
88
89 DuplicateField::Deny => {
90 return Box::pin(ready(Err(MultipartError::DuplicateField(
91 field.form_field_name,
92 ))))
93 }
94
95 DuplicateField::Replace => {}
96 }
97 }
98
99 Box::pin(async move {
100 let field_name = field.form_field_name.clone();
101 let t = T::read_field(req, field, limits).await?;
102 state.insert(field_name, Box::new(t));
103 Ok(())
104 })
105 }
106
107 fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {
108 Ok(state.remove(name).map(|m| *m.downcast::<T>().unwrap()))
109 }
110}
111
112impl<'t, T> FieldGroupReader<'t> for Vec<T>
113where
114 T: FieldReader<'t>,
115{
116 type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;
117
118 fn handle_field(
119 req: &'t HttpRequest,
120 field: Field,
121 limits: &'t mut Limits,
122 state: &'t mut State,
123 _duplicate_field: DuplicateField,
124 ) -> Self::Future {
125 Box::pin(async move {
126 let vec = state
129 .entry(field.form_field_name.clone())
130 .or_insert_with(|| Box::<Vec<T>>::default())
131 .downcast_mut::<Vec<T>>()
132 .unwrap();
133
134 let item = T::read_field(req, field, limits).await?;
135 vec.push(item);
136
137 Ok(())
138 })
139 }
140
141 fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {
142 Ok(state
143 .remove(name)
144 .map(|m| *m.downcast::<Vec<T>>().unwrap())
145 .unwrap_or_default())
146 }
147}
148
149impl<'t, T> FieldGroupReader<'t> for T
150where
151 T: FieldReader<'t>,
152{
153 type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;
154
155 fn handle_field(
156 req: &'t HttpRequest,
157 field: Field,
158 limits: &'t mut Limits,
159 state: &'t mut State,
160 duplicate_field: DuplicateField,
161 ) -> Self::Future {
162 if state.contains_key(&field.form_field_name) {
163 match duplicate_field {
164 DuplicateField::Ignore => {
165 return Box::pin(async move { discard_field(field, limits).await });
166 }
167
168 DuplicateField::Deny => {
169 return Box::pin(ready(Err(MultipartError::DuplicateField(
170 field.form_field_name,
171 ))))
172 }
173
174 DuplicateField::Replace => {}
175 }
176 }
177
178 Box::pin(async move {
179 let field_name = field.form_field_name.clone();
180 let t = T::read_field(req, field, limits).await?;
181 state.insert(field_name, Box::new(t));
182 Ok(())
183 })
184 }
185
186 fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {
187 state
188 .remove(name)
189 .map(|m| *m.downcast::<T>().unwrap())
190 .ok_or_else(|| MultipartError::MissingField(name.to_owned()))
191 }
192}
193
194impl<'t, T> FieldGroupReader<'t> for Option<Vec<T>>
195where
196 T: FieldReader<'t>,
197{
198 type Future = LocalBoxFuture<'t, Result<(), MultipartError>>;
199
200 fn handle_field(
201 req: &'t HttpRequest,
202 field: Field,
203 limits: &'t mut Limits,
204 state: &'t mut State,
205 _duplicate_field: DuplicateField,
206 ) -> Self::Future {
207 let field_name = field.name().unwrap().to_string();
208
209 Box::pin(async move {
210 let vec = state
211 .entry(field_name)
212 .or_insert_with(|| Box::<Vec<T>>::default())
213 .downcast_mut::<Vec<T>>()
214 .unwrap();
215
216 let item = T::read_field(req, field, limits).await?;
217 vec.push(item);
218
219 Ok(())
220 })
221 }
222
223 fn from_state(name: &str, state: &'t mut State) -> Result<Self, MultipartError> {
224 if let Some(boxed_vec) = state.remove(name) {
225 let vec = *boxed_vec.downcast::<Vec<T>>().unwrap();
226 Ok(Some(vec))
227 } else {
228 Ok(None)
229 }
230 }
231}
232
233pub trait MultipartCollect: Sized {
237 fn limit(field_name: &str) -> Option<usize>;
240
241 fn handle_field<'t>(
244 req: &'t HttpRequest,
245 field: Field,
246 limits: &'t mut Limits,
247 state: &'t mut State,
248 ) -> LocalBoxFuture<'t, Result<(), MultipartError>>;
249
250 fn from_state(state: State) -> Result<Self, MultipartError>;
253}
254
255#[doc(hidden)]
256pub enum DuplicateField {
257 Ignore,
259
260 Deny,
262
263 Replace,
265}
266
267pub struct Limits {
269 pub total_limit_remaining: usize,
270 pub memory_limit_remaining: usize,
271 pub field_limit_remaining: Option<usize>,
272}
273
274impl Limits {
275 pub fn new(total_limit: usize, memory_limit: usize) -> Self {
276 Self {
277 total_limit_remaining: total_limit,
278 memory_limit_remaining: memory_limit,
279 field_limit_remaining: None,
280 }
281 }
282
283 pub fn try_consume_limits(
291 &mut self,
292 bytes: usize,
293 in_memory: bool,
294 ) -> Result<(), MultipartError> {
295 self.total_limit_remaining = self
296 .total_limit_remaining
297 .checked_sub(bytes)
298 .ok_or(MultipartError::Payload(PayloadError::Overflow))?;
299
300 if in_memory {
301 self.memory_limit_remaining = self
302 .memory_limit_remaining
303 .checked_sub(bytes)
304 .ok_or(MultipartError::Payload(PayloadError::Overflow))?;
305 }
306
307 if let Some(field_limit) = self.field_limit_remaining {
308 self.field_limit_remaining = Some(
309 field_limit
310 .checked_sub(bytes)
311 .ok_or(MultipartError::Payload(PayloadError::Overflow))?,
312 );
313 }
314
315 Ok(())
316 }
317}
318
319#[doc(hidden)]
321pub async fn discard_field(mut field: Field, limits: &mut Limits) -> Result<(), MultipartError> {
322 while let Some(chunk) = field.try_next().await? {
323 limits.try_consume_limits(chunk.len(), false)?;
324 }
325
326 Ok(())
327}
328
329#[derive(Deref, DerefMut)]
441pub struct MultipartForm<T: MultipartCollect>(pub T);
442
443impl<T: MultipartCollect> MultipartForm<T> {
444 pub fn into_inner(self) -> T {
446 self.0
447 }
448}
449
450impl<T> FromRequest for MultipartForm<T>
451where
452 T: MultipartCollect + 'static,
453{
454 type Error = Error;
455 type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
456
457 #[inline]
458 fn from_request(req: &HttpRequest, payload: &mut dev::Payload) -> Self::Future {
459 let mut multipart = Multipart::from_req(req, payload);
460
461 let content_type = match multipart.content_type_or_bail() {
462 Ok(content_type) => content_type,
463 Err(err) => return Box::pin(ready(Err(err.into()))),
464 };
465
466 if content_type.subtype() != mime::FORM_DATA {
467 return Box::pin(ready(Err(MultipartError::ContentTypeIncompatible.into())));
469 };
470
471 let config = MultipartFormConfig::from_req(req);
472 let mut limits = Limits::new(config.total_limit, config.memory_limit);
473
474 let req = req.clone();
475 let req2 = req.clone();
476 let err_handler = config.err_handler.clone();
477
478 Box::pin(
479 async move {
480 let mut state = State::default();
481
482 let mut field_limits = HashMap::<String, Option<usize>>::new();
484
485 while let Some(field) = multipart.try_next().await? {
486 debug_assert!(
487 !field.form_field_name.is_empty(),
488 "multipart form fields should have names",
489 );
490
491 let entry = field_limits
493 .entry(field.form_field_name.clone())
494 .or_insert_with(|| T::limit(&field.form_field_name));
495
496 limits.field_limit_remaining.clone_from(entry);
497
498 T::handle_field(&req, field, &mut limits, &mut state).await?;
499
500 *entry = limits.field_limit_remaining;
502 }
503
504 let inner = T::from_state(state)?;
505 Ok(MultipartForm(inner))
506 }
507 .map_err(move |err| {
508 if let Some(handler) = err_handler {
509 (*handler)(err, &req2)
510 } else {
511 err.into()
512 }
513 }),
514 )
515 }
516}
517
518type MultipartFormErrorHandler =
519 Option<Arc<dyn Fn(MultipartError, &HttpRequest) -> Error + Send + Sync>>;
520
521#[derive(Clone)]
525pub struct MultipartFormConfig {
526 total_limit: usize,
527 memory_limit: usize,
528 err_handler: MultipartFormErrorHandler,
529}
530
531impl MultipartFormConfig {
532 pub fn total_limit(mut self, total_limit: usize) -> Self {
534 self.total_limit = total_limit;
535 self
536 }
537
538 pub fn memory_limit(mut self, memory_limit: usize) -> Self {
540 self.memory_limit = memory_limit;
541 self
542 }
543
544 pub fn error_handler<F>(mut self, f: F) -> Self
546 where
547 F: Fn(MultipartError, &HttpRequest) -> Error + Send + Sync + 'static,
548 {
549 self.err_handler = Some(Arc::new(f));
550 self
551 }
552
553 fn from_req(req: &HttpRequest) -> &Self {
556 req.app_data::<Self>()
557 .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
558 .unwrap_or(&DEFAULT_CONFIG)
559 }
560}
561
562const DEFAULT_CONFIG: MultipartFormConfig = MultipartFormConfig {
563 total_limit: 52_428_800, memory_limit: 2_097_152, err_handler: None,
566};
567
568impl Default for MultipartFormConfig {
569 fn default() -> Self {
570 DEFAULT_CONFIG
571 }
572}
573
574#[cfg(test)]
575mod tests {
576 use actix_http::encoding::Decoder;
577 use actix_multipart_rfc7578::client::multipart;
578 use actix_test::TestServer;
579 use actix_web::{
580 dev::Payload, http::StatusCode, web, App, HttpRequest, HttpResponse, Resource, Responder,
581 };
582 use awc::{Client, ClientResponse};
583 use futures_core::future::LocalBoxFuture;
584 use futures_util::TryStreamExt as _;
585
586 use super::MultipartForm;
587 use crate::{
588 form::{
589 bytes::Bytes, tempfile::TempFile, text::Text, FieldReader, Limits, MultipartFormConfig,
590 },
591 Field, MultipartError,
592 };
593
594 pub async fn send_form(
595 srv: &TestServer,
596 form: multipart::Form<'static>,
597 uri: &'static str,
598 ) -> ClientResponse<Decoder<Payload>> {
599 Client::default()
600 .post(srv.url(uri))
601 .content_type(form.content_type())
602 .send_body(multipart::Body::from(form))
603 .await
604 .unwrap()
605 }
606
607 #[derive(MultipartForm)]
609 struct TestOptions {
610 field1: Option<Text<String>>,
611 field2: Option<Text<String>>,
612 }
613
614 async fn test_options_route(form: MultipartForm<TestOptions>) -> impl Responder {
615 assert!(form.field1.is_some());
616 assert!(form.field2.is_none());
617 HttpResponse::Ok().finish()
618 }
619
620 #[actix_rt::test]
621 async fn test_options() {
622 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_options_route)));
623
624 let mut form = multipart::Form::default();
625 form.add_text("field1", "value");
626
627 let response = send_form(&srv, form, "/").await;
628 assert_eq!(response.status(), StatusCode::OK);
629 }
630
631 #[derive(MultipartForm)]
633 struct TestVec {
634 list1: Vec<Text<String>>,
635 list2: Vec<Text<String>>,
636 }
637
638 async fn test_vec_route(form: MultipartForm<TestVec>) -> impl Responder {
639 let form = form.into_inner();
640 let strings = form
641 .list1
642 .into_iter()
643 .map(|s| s.into_inner())
644 .collect::<Vec<_>>();
645 assert_eq!(strings, vec!["value1", "value2", "value3"]);
646 assert_eq!(form.list2.len(), 0);
647 HttpResponse::Ok().finish()
648 }
649
650 #[actix_rt::test]
651 async fn test_vec() {
652 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_vec_route)));
653
654 let mut form = multipart::Form::default();
655 form.add_text("list1", "value1");
656 form.add_text("list1", "value2");
657 form.add_text("list1", "value3");
658
659 let response = send_form(&srv, form, "/").await;
660 assert_eq!(response.status(), StatusCode::OK);
661 }
662
663 #[derive(MultipartForm)]
665 struct TestOptionVec {
666 list1: Option<Vec<Text<String>>>,
667 list2: Option<Vec<Text<String>>>,
668 }
669
670 async fn test_option_vec_route(form: MultipartForm<TestOptionVec>) -> impl Responder {
671 let form = form.into_inner();
672 let strings = form
673 .list1
674 .unwrap()
675 .into_iter()
676 .map(|s| s.into_inner())
677 .collect::<Vec<_>>();
678 assert_eq!(strings, vec!["value1", "value2", "value3"]);
679 assert!(form.list2.is_none());
680 HttpResponse::Ok().finish()
681 }
682
683 #[actix_rt::test]
684 async fn test_option_vec() {
685 let srv =
686 actix_test::start(|| App::new().route("/", web::post().to(test_option_vec_route)));
687
688 let mut form = multipart::Form::default();
689 form.add_text("list1", "value1");
690 form.add_text("list1", "value2");
691 form.add_text("list1", "value3");
692
693 let response = send_form(&srv, form, "/").await;
694 assert_eq!(response.status(), StatusCode::OK);
695 }
696
697 #[derive(MultipartForm)]
699 struct TestFieldRenaming {
700 #[multipart(rename = "renamed")]
701 field1: Text<String>,
702 #[multipart(rename = "field1")]
703 field2: Text<String>,
704 field3: Text<String>,
705 }
706
707 async fn test_field_renaming_route(form: MultipartForm<TestFieldRenaming>) -> impl Responder {
708 assert_eq!(&*form.field1, "renamed");
709 assert_eq!(&*form.field2, "field1");
710 assert_eq!(&*form.field3, "field3");
711 HttpResponse::Ok().finish()
712 }
713
714 #[actix_rt::test]
715 async fn test_field_renaming() {
716 let srv =
717 actix_test::start(|| App::new().route("/", web::post().to(test_field_renaming_route)));
718
719 let mut form = multipart::Form::default();
720 form.add_text("renamed", "renamed");
721 form.add_text("field1", "field1");
722 form.add_text("field3", "field3");
723
724 let response = send_form(&srv, form, "/").await;
725 assert_eq!(response.status(), StatusCode::OK);
726 }
727
728 #[derive(MultipartForm)]
730 #[multipart(deny_unknown_fields)]
731 struct TestDenyUnknown {}
732
733 #[derive(MultipartForm)]
734 struct TestAllowUnknown {}
735
736 async fn test_deny_unknown_route(_: MultipartForm<TestDenyUnknown>) -> impl Responder {
737 HttpResponse::Ok().finish()
738 }
739
740 async fn test_allow_unknown_route(_: MultipartForm<TestAllowUnknown>) -> impl Responder {
741 HttpResponse::Ok().finish()
742 }
743
744 #[actix_rt::test]
745 async fn test_deny_unknown() {
746 let srv = actix_test::start(|| {
747 App::new()
748 .route("/deny", web::post().to(test_deny_unknown_route))
749 .route("/allow", web::post().to(test_allow_unknown_route))
750 });
751
752 let mut form = multipart::Form::default();
753 form.add_text("unknown", "value");
754 let response = send_form(&srv, form, "/deny").await;
755 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
756
757 let mut form = multipart::Form::default();
758 form.add_text("unknown", "value");
759 let response = send_form(&srv, form, "/allow").await;
760 assert_eq!(response.status(), StatusCode::OK);
761 }
762
763 #[derive(MultipartForm)]
765 #[multipart(duplicate_field = "deny")]
766 struct TestDuplicateDeny {
767 _field: Text<String>,
768 }
769
770 #[derive(MultipartForm)]
771 #[multipart(duplicate_field = "replace")]
772 struct TestDuplicateReplace {
773 field: Text<String>,
774 }
775
776 #[derive(MultipartForm)]
777 #[multipart(duplicate_field = "ignore")]
778 struct TestDuplicateIgnore {
779 field: Text<String>,
780 }
781
782 async fn test_duplicate_deny_route(_: MultipartForm<TestDuplicateDeny>) -> impl Responder {
783 HttpResponse::Ok().finish()
784 }
785
786 async fn test_duplicate_replace_route(
787 form: MultipartForm<TestDuplicateReplace>,
788 ) -> impl Responder {
789 assert_eq!(&*form.field, "second_value");
790 HttpResponse::Ok().finish()
791 }
792
793 async fn test_duplicate_ignore_route(
794 form: MultipartForm<TestDuplicateIgnore>,
795 ) -> impl Responder {
796 assert_eq!(&*form.field, "first_value");
797 HttpResponse::Ok().finish()
798 }
799
800 #[actix_rt::test]
801 async fn test_duplicate_field() {
802 let srv = actix_test::start(|| {
803 App::new()
804 .route("/deny", web::post().to(test_duplicate_deny_route))
805 .route("/replace", web::post().to(test_duplicate_replace_route))
806 .route("/ignore", web::post().to(test_duplicate_ignore_route))
807 });
808
809 let mut form = multipart::Form::default();
810 form.add_text("_field", "first_value");
811 form.add_text("_field", "second_value");
812 let response = send_form(&srv, form, "/deny").await;
813 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
814
815 let mut form = multipart::Form::default();
816 form.add_text("field", "first_value");
817 form.add_text("field", "second_value");
818 let response = send_form(&srv, form, "/replace").await;
819 assert_eq!(response.status(), StatusCode::OK);
820
821 let mut form = multipart::Form::default();
822 form.add_text("field", "first_value");
823 form.add_text("field", "second_value");
824 let response = send_form(&srv, form, "/ignore").await;
825 assert_eq!(response.status(), StatusCode::OK);
826 }
827
828 #[actix_rt::test]
829 async fn test_discarded_fields_count_towards_total_limit() {
830 let srv = actix_test::start(|| {
831 App::new()
832 .route("/unknown", web::post().to(test_upload_limits_memory))
833 .route("/duplicate", web::post().to(test_duplicate_ignore_route))
834 .app_data(
835 MultipartFormConfig::default()
836 .memory_limit(usize::MAX)
837 .total_limit(20),
838 )
839 });
840
841 let mut form = multipart::Form::default();
842 form.add_text("field", "7 bytes");
843 form.add_text("unknown", "this string is 28 bytes long");
844 let response = send_form(&srv, form, "/unknown").await;
845 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
846
847 let mut form = multipart::Form::default();
848 form.add_text("field", "first_value");
849 form.add_text("field", "this string is 28 bytes long");
850 let response = send_form(&srv, form, "/duplicate").await;
851 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
852 }
853
854 #[derive(MultipartForm)]
856 struct TestMemoryUploadLimits {
857 field: Bytes,
858 }
859
860 #[derive(MultipartForm)]
861 struct TestFileUploadLimits {
862 field: TempFile,
863 }
864
865 async fn test_upload_limits_memory(
866 form: MultipartForm<TestMemoryUploadLimits>,
867 ) -> impl Responder {
868 assert!(!form.field.data.is_empty());
869 HttpResponse::Ok().finish()
870 }
871
872 async fn test_upload_limits_file(form: MultipartForm<TestFileUploadLimits>) -> impl Responder {
873 assert!(form.field.size > 0);
874 HttpResponse::Ok().finish()
875 }
876
877 #[actix_rt::test]
878 async fn test_memory_limits() {
879 let srv = actix_test::start(|| {
880 App::new()
881 .route("/text", web::post().to(test_upload_limits_memory))
882 .route("/file", web::post().to(test_upload_limits_file))
883 .app_data(
884 MultipartFormConfig::default()
885 .memory_limit(20)
886 .total_limit(usize::MAX),
887 )
888 });
889
890 let mut form = multipart::Form::default();
892 form.add_text("field", "this string is 28 bytes long");
893 let response = send_form(&srv, form, "/text").await;
894 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
895
896 let mut form = multipart::Form::default();
898 form.add_text("field", "this string is 28 bytes long");
899 let response = send_form(&srv, form, "/file").await;
900 assert_eq!(response.status(), StatusCode::OK);
901 }
902
903 #[actix_rt::test]
904 async fn test_total_limit() {
905 let srv = actix_test::start(|| {
906 App::new()
907 .route("/text", web::post().to(test_upload_limits_memory))
908 .route("/file", web::post().to(test_upload_limits_file))
909 .app_data(
910 MultipartFormConfig::default()
911 .memory_limit(usize::MAX)
912 .total_limit(20),
913 )
914 });
915
916 let mut form = multipart::Form::default();
918 form.add_text("field", "7 bytes");
919 let response = send_form(&srv, form, "/text").await;
920 assert_eq!(response.status(), StatusCode::OK);
921
922 let mut form = multipart::Form::default();
924 form.add_text("field", "this string is 28 bytes long");
925 let response = send_form(&srv, form, "/text").await;
926 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
927
928 let mut form = multipart::Form::default();
930 form.add_text("field", "this string is 28 bytes long");
931 let response = send_form(&srv, form, "/file").await;
932 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
933 }
934
935 #[derive(MultipartForm)]
936 struct TestFieldLevelLimits {
937 #[multipart(limit = "30B")]
938 field: Vec<Bytes>,
939 }
940
941 async fn test_field_level_limits_route(
942 form: MultipartForm<TestFieldLevelLimits>,
943 ) -> impl Responder {
944 assert!(!form.field.is_empty());
945 HttpResponse::Ok().finish()
946 }
947
948 #[actix_rt::test]
949 async fn test_field_level_limits() {
950 let srv = actix_test::start(|| {
951 App::new()
952 .route("/", web::post().to(test_field_level_limits_route))
953 .app_data(
954 MultipartFormConfig::default()
955 .memory_limit(usize::MAX)
956 .total_limit(usize::MAX),
957 )
958 });
959
960 let mut form = multipart::Form::default();
962 form.add_text("field", "this string is 28 bytes long");
963 let response = send_form(&srv, form, "/").await;
964 assert_eq!(response.status(), StatusCode::OK);
965
966 let mut form = multipart::Form::default();
968 form.add_text("field", "this string is more than 30 bytes long");
969 let response = send_form(&srv, form, "/").await;
970 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
971
972 let mut form = multipart::Form::default();
974 form.add_text("field", "7 bytes");
975 form.add_text("field", "7 bytes");
976 let response = send_form(&srv, form, "/").await;
977 assert_eq!(response.status(), StatusCode::OK);
978
979 let mut form = multipart::Form::default();
981 form.add_text("field", "this string is 28 bytes long");
982 form.add_text("field", "this string is 28 bytes long");
983 let response = send_form(&srv, form, "/").await;
984 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
985 }
986
987 #[actix_rt::test]
988 async fn non_multipart_form_data() {
989 #[derive(MultipartForm)]
990 struct TestNonMultipartFormData {
991 #[allow(unused)]
992 #[multipart(limit = "30B")]
993 foo: Text<String>,
994 }
995
996 async fn non_multipart_form_data_route(
997 _form: MultipartForm<TestNonMultipartFormData>,
998 ) -> String {
999 unreachable!("request is sent with multipart/mixed");
1000 }
1001
1002 let srv = actix_test::start(|| {
1003 App::new().route("/", web::post().to(non_multipart_form_data_route))
1004 });
1005
1006 let mut form = multipart::Form::default();
1007 form.add_text("foo", "foo");
1008
1009 let ct = form.content_type().replacen("/form-data", "/mixed", 1);
1011
1012 let res = Client::default()
1013 .post(srv.url("/"))
1014 .content_type(ct)
1015 .send_body(multipart::Body::from(form))
1016 .await
1017 .unwrap();
1018
1019 assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
1020 }
1021
1022 #[should_panic(expected = "called `Result::unwrap()` on an `Err` value: Connect(Disconnected)")]
1023 #[actix_web::test]
1024 async fn field_try_next_panic() {
1025 #[derive(Debug)]
1026 struct NullSink;
1027
1028 impl<'t> FieldReader<'t> for NullSink {
1029 type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;
1030
1031 fn read_field(
1032 _: &'t HttpRequest,
1033 mut field: Field,
1034 _limits: &'t mut Limits,
1035 ) -> Self::Future {
1036 Box::pin(async move {
1037 while let Some(_chunk) = field.try_next().await? {}
1039
1040 let _post = field.try_next().await;
1042
1043 Ok(Self)
1044 })
1045 }
1046 }
1047
1048 #[allow(dead_code)]
1049 #[derive(MultipartForm)]
1050 struct NullSinkForm {
1051 foo: NullSink,
1052 }
1053
1054 async fn null_sink(_form: MultipartForm<NullSinkForm>) -> impl Responder {
1055 "unreachable"
1056 }
1057
1058 let srv = actix_test::start(|| App::new().service(Resource::new("/").post(null_sink)));
1059
1060 let mut form = multipart::Form::default();
1061 form.add_text("foo", "data is not important to this test");
1062
1063 let _res = send_form(&srv, form, "/").await;
1065 }
1066}