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,
581 http::StatusCode,
582 post,
583 test::{call_service, init_service, TestRequest},
584 web, App, HttpRequest, HttpResponse, Responder,
585 };
586 use awc::{Client, ClientResponse};
587 use futures_core::future::LocalBoxFuture;
588 use futures_util::TryStreamExt as _;
589
590 use super::MultipartForm;
591 use crate::{
592 form::{
593 bytes::Bytes, tempfile::TempFile, text::Text, FieldReader, Limits, MultipartFormConfig,
594 },
595 test::create_form_data_payload_and_headers,
596 Field, MultipartError,
597 };
598
599 pub async fn send_form(
600 srv: &TestServer,
601 form: multipart::Form<'static>,
602 uri: &'static str,
603 ) -> ClientResponse<Decoder<Payload>> {
604 Client::default()
605 .post(srv.url(uri))
606 .content_type(form.content_type())
607 .send_body(multipart::Body::from(form))
608 .await
609 .unwrap()
610 }
611
612 #[derive(MultipartForm)]
614 struct TestOptions {
615 field1: Option<Text<String>>,
616 field2: Option<Text<String>>,
617 }
618
619 async fn test_options_route(form: MultipartForm<TestOptions>) -> impl Responder {
620 assert!(form.field1.is_some());
621 assert!(form.field2.is_none());
622 HttpResponse::Ok().finish()
623 }
624
625 #[actix_rt::test]
626 async fn test_options() {
627 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_options_route)));
628
629 let mut form = multipart::Form::default();
630 form.add_text("field1", "value");
631
632 let response = send_form(&srv, form, "/").await;
633 assert_eq!(response.status(), StatusCode::OK);
634 }
635
636 #[derive(MultipartForm)]
638 struct TestVec {
639 list1: Vec<Text<String>>,
640 list2: Vec<Text<String>>,
641 }
642
643 async fn test_vec_route(form: MultipartForm<TestVec>) -> impl Responder {
644 let form = form.into_inner();
645 let strings = form
646 .list1
647 .into_iter()
648 .map(|s| s.into_inner())
649 .collect::<Vec<_>>();
650 assert_eq!(strings, vec!["value1", "value2", "value3"]);
651 assert_eq!(form.list2.len(), 0);
652 HttpResponse::Ok().finish()
653 }
654
655 #[actix_rt::test]
656 async fn test_vec() {
657 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_vec_route)));
658
659 let mut form = multipart::Form::default();
660 form.add_text("list1", "value1");
661 form.add_text("list1", "value2");
662 form.add_text("list1", "value3");
663
664 let response = send_form(&srv, form, "/").await;
665 assert_eq!(response.status(), StatusCode::OK);
666 }
667
668 #[derive(MultipartForm)]
670 struct TestOptionVec {
671 list1: Option<Vec<Text<String>>>,
672 list2: Option<Vec<Text<String>>>,
673 }
674
675 async fn test_option_vec_route(form: MultipartForm<TestOptionVec>) -> impl Responder {
676 let form = form.into_inner();
677 let strings = form
678 .list1
679 .unwrap()
680 .into_iter()
681 .map(|s| s.into_inner())
682 .collect::<Vec<_>>();
683 assert_eq!(strings, vec!["value1", "value2", "value3"]);
684 assert!(form.list2.is_none());
685 HttpResponse::Ok().finish()
686 }
687
688 #[actix_rt::test]
689 async fn test_option_vec() {
690 let srv =
691 actix_test::start(|| App::new().route("/", web::post().to(test_option_vec_route)));
692
693 let mut form = multipart::Form::default();
694 form.add_text("list1", "value1");
695 form.add_text("list1", "value2");
696 form.add_text("list1", "value3");
697
698 let response = send_form(&srv, form, "/").await;
699 assert_eq!(response.status(), StatusCode::OK);
700 }
701
702 #[derive(MultipartForm)]
704 struct TestFieldRenaming {
705 #[multipart(rename = "renamed")]
706 field1: Text<String>,
707 #[multipart(rename = "field1")]
708 field2: Text<String>,
709 field3: Text<String>,
710 }
711
712 async fn test_field_renaming_route(form: MultipartForm<TestFieldRenaming>) -> impl Responder {
713 assert_eq!(&*form.field1, "renamed");
714 assert_eq!(&*form.field2, "field1");
715 assert_eq!(&*form.field3, "field3");
716 HttpResponse::Ok().finish()
717 }
718
719 #[actix_rt::test]
720 async fn test_field_renaming() {
721 let srv =
722 actix_test::start(|| App::new().route("/", web::post().to(test_field_renaming_route)));
723
724 let mut form = multipart::Form::default();
725 form.add_text("renamed", "renamed");
726 form.add_text("field1", "field1");
727 form.add_text("field3", "field3");
728
729 let response = send_form(&srv, form, "/").await;
730 assert_eq!(response.status(), StatusCode::OK);
731 }
732
733 #[derive(MultipartForm)]
735 #[multipart(deny_unknown_fields)]
736 struct TestDenyUnknown {}
737
738 #[derive(MultipartForm)]
739 struct TestAllowUnknown {}
740
741 async fn test_deny_unknown_route(_: MultipartForm<TestDenyUnknown>) -> impl Responder {
742 HttpResponse::Ok().finish()
743 }
744
745 async fn test_allow_unknown_route(_: MultipartForm<TestAllowUnknown>) -> impl Responder {
746 HttpResponse::Ok().finish()
747 }
748
749 #[actix_rt::test]
750 async fn test_deny_unknown() {
751 let srv = actix_test::start(|| {
752 App::new()
753 .route("/deny", web::post().to(test_deny_unknown_route))
754 .route("/allow", web::post().to(test_allow_unknown_route))
755 });
756
757 let mut form = multipart::Form::default();
758 form.add_text("unknown", "value");
759 let response = send_form(&srv, form, "/deny").await;
760 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
761
762 let mut form = multipart::Form::default();
763 form.add_text("unknown", "value");
764 let response = send_form(&srv, form, "/allow").await;
765 assert_eq!(response.status(), StatusCode::OK);
766 }
767
768 #[derive(MultipartForm)]
770 #[multipart(duplicate_field = "deny")]
771 struct TestDuplicateDeny {
772 _field: Text<String>,
773 }
774
775 #[derive(MultipartForm)]
776 #[multipart(duplicate_field = "replace")]
777 struct TestDuplicateReplace {
778 field: Text<String>,
779 }
780
781 #[derive(MultipartForm)]
782 #[multipart(duplicate_field = "ignore")]
783 struct TestDuplicateIgnore {
784 field: Text<String>,
785 }
786
787 async fn test_duplicate_deny_route(_: MultipartForm<TestDuplicateDeny>) -> impl Responder {
788 HttpResponse::Ok().finish()
789 }
790
791 async fn test_duplicate_replace_route(
792 form: MultipartForm<TestDuplicateReplace>,
793 ) -> impl Responder {
794 assert_eq!(&*form.field, "second_value");
795 HttpResponse::Ok().finish()
796 }
797
798 async fn test_duplicate_ignore_route(
799 form: MultipartForm<TestDuplicateIgnore>,
800 ) -> impl Responder {
801 assert_eq!(&*form.field, "first_value");
802 HttpResponse::Ok().finish()
803 }
804
805 #[actix_rt::test]
806 async fn test_duplicate_field() {
807 let srv = actix_test::start(|| {
808 App::new()
809 .route("/deny", web::post().to(test_duplicate_deny_route))
810 .route("/replace", web::post().to(test_duplicate_replace_route))
811 .route("/ignore", web::post().to(test_duplicate_ignore_route))
812 });
813
814 let mut form = multipart::Form::default();
815 form.add_text("_field", "first_value");
816 form.add_text("_field", "second_value");
817 let response = send_form(&srv, form, "/deny").await;
818 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
819
820 let mut form = multipart::Form::default();
821 form.add_text("field", "first_value");
822 form.add_text("field", "second_value");
823 let response = send_form(&srv, form, "/replace").await;
824 assert_eq!(response.status(), StatusCode::OK);
825
826 let mut form = multipart::Form::default();
827 form.add_text("field", "first_value");
828 form.add_text("field", "second_value");
829 let response = send_form(&srv, form, "/ignore").await;
830 assert_eq!(response.status(), StatusCode::OK);
831 }
832
833 #[actix_rt::test]
834 async fn test_discarded_fields_count_towards_total_limit() {
835 let srv = actix_test::start(|| {
836 App::new()
837 .route("/unknown", web::post().to(test_upload_limits_memory))
838 .route("/duplicate", web::post().to(test_duplicate_ignore_route))
839 .app_data(
840 MultipartFormConfig::default()
841 .memory_limit(usize::MAX)
842 .total_limit(20),
843 )
844 });
845
846 let mut form = multipart::Form::default();
847 form.add_text("field", "7 bytes");
848 form.add_text("unknown", "this string is 28 bytes long");
849 let response = send_form(&srv, form, "/unknown").await;
850 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
851
852 let mut form = multipart::Form::default();
853 form.add_text("field", "first_value");
854 form.add_text("field", "this string is 28 bytes long");
855 let response = send_form(&srv, form, "/duplicate").await;
856 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
857 }
858
859 #[derive(MultipartForm)]
861 struct TestMemoryUploadLimits {
862 field: Bytes,
863 }
864
865 #[derive(MultipartForm)]
866 struct TestFileUploadLimits {
867 field: TempFile,
868 }
869
870 async fn test_upload_limits_memory(
871 form: MultipartForm<TestMemoryUploadLimits>,
872 ) -> impl Responder {
873 assert!(!form.field.data.is_empty());
874 HttpResponse::Ok().finish()
875 }
876
877 async fn test_upload_limits_file(form: MultipartForm<TestFileUploadLimits>) -> impl Responder {
878 assert!(form.field.size > 0);
879 HttpResponse::Ok().finish()
880 }
881
882 #[actix_rt::test]
883 async fn test_memory_limits() {
884 let srv = actix_test::start(|| {
885 App::new()
886 .route("/text", web::post().to(test_upload_limits_memory))
887 .route("/file", web::post().to(test_upload_limits_file))
888 .app_data(
889 MultipartFormConfig::default()
890 .memory_limit(20)
891 .total_limit(usize::MAX),
892 )
893 });
894
895 let mut form = multipart::Form::default();
897 form.add_text("field", "this string is 28 bytes long");
898 let response = send_form(&srv, form, "/text").await;
899 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
900
901 let mut form = multipart::Form::default();
903 form.add_text("field", "this string is 28 bytes long");
904 let response = send_form(&srv, form, "/file").await;
905 assert_eq!(response.status(), StatusCode::OK);
906 }
907
908 #[actix_rt::test]
909 async fn test_total_limit() {
910 let srv = actix_test::start(|| {
911 App::new()
912 .route("/text", web::post().to(test_upload_limits_memory))
913 .route("/file", web::post().to(test_upload_limits_file))
914 .app_data(
915 MultipartFormConfig::default()
916 .memory_limit(usize::MAX)
917 .total_limit(20),
918 )
919 });
920
921 let mut form = multipart::Form::default();
923 form.add_text("field", "7 bytes");
924 let response = send_form(&srv, form, "/text").await;
925 assert_eq!(response.status(), StatusCode::OK);
926
927 let mut form = multipart::Form::default();
929 form.add_text("field", "this string is 28 bytes long");
930 let response = send_form(&srv, form, "/text").await;
931 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
932
933 let mut form = multipart::Form::default();
935 form.add_text("field", "this string is 28 bytes long");
936 let response = send_form(&srv, form, "/file").await;
937 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
938 }
939
940 #[derive(MultipartForm)]
941 struct TestFieldLevelLimits {
942 #[multipart(limit = "30B")]
943 field: Vec<Bytes>,
944 }
945
946 async fn test_field_level_limits_route(
947 form: MultipartForm<TestFieldLevelLimits>,
948 ) -> impl Responder {
949 assert!(!form.field.is_empty());
950 HttpResponse::Ok().finish()
951 }
952
953 #[actix_rt::test]
954 async fn test_field_level_limits() {
955 let srv = actix_test::start(|| {
956 App::new()
957 .route("/", web::post().to(test_field_level_limits_route))
958 .app_data(
959 MultipartFormConfig::default()
960 .memory_limit(usize::MAX)
961 .total_limit(usize::MAX),
962 )
963 });
964
965 let mut form = multipart::Form::default();
967 form.add_text("field", "this string is 28 bytes long");
968 let response = send_form(&srv, form, "/").await;
969 assert_eq!(response.status(), StatusCode::OK);
970
971 let mut form = multipart::Form::default();
973 form.add_text("field", "this string is more than 30 bytes long");
974 let response = send_form(&srv, form, "/").await;
975 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
976
977 let mut form = multipart::Form::default();
979 form.add_text("field", "7 bytes");
980 form.add_text("field", "7 bytes");
981 let response = send_form(&srv, form, "/").await;
982 assert_eq!(response.status(), StatusCode::OK);
983
984 let mut form = multipart::Form::default();
986 form.add_text("field", "this string is 28 bytes long");
987 form.add_text("field", "this string is 28 bytes long");
988 let response = send_form(&srv, form, "/").await;
989 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
990 }
991
992 #[actix_rt::test]
993 async fn non_multipart_form_data() {
994 #[derive(MultipartForm)]
995 struct TestNonMultipartFormData {
996 #[allow(unused)]
997 #[multipart(limit = "30B")]
998 foo: Text<String>,
999 }
1000
1001 async fn non_multipart_form_data_route(
1002 _form: MultipartForm<TestNonMultipartFormData>,
1003 ) -> String {
1004 unreachable!("request is sent with multipart/mixed");
1005 }
1006
1007 let srv = actix_test::start(|| {
1008 App::new().route("/", web::post().to(non_multipart_form_data_route))
1009 });
1010
1011 let mut form = multipart::Form::default();
1012 form.add_text("foo", "foo");
1013
1014 let ct = form.content_type().replacen("/form-data", "/mixed", 1);
1016
1017 let res = Client::default()
1018 .post(srv.url("/"))
1019 .content_type(ct)
1020 .send_body(multipart::Body::from(form))
1021 .await
1022 .unwrap();
1023
1024 assert_eq!(res.status(), StatusCode::UNSUPPORTED_MEDIA_TYPE);
1025 }
1026
1027 #[should_panic(expected = "Field should not be polled after completion")]
1028 #[actix_web::test]
1029 async fn field_try_next_panic() {
1030 #[derive(Debug)]
1031 struct NullSink;
1032
1033 impl<'t> FieldReader<'t> for NullSink {
1034 type Future = LocalBoxFuture<'t, Result<Self, MultipartError>>;
1035
1036 fn read_field(
1037 _: &'t HttpRequest,
1038 mut field: Field,
1039 _limits: &'t mut Limits,
1040 ) -> Self::Future {
1041 Box::pin(async move {
1042 while let Some(_chunk) = field.try_next().await? {}
1044
1045 let _post = field.try_next().await;
1047
1048 Ok(Self)
1049 })
1050 }
1051 }
1052
1053 #[allow(dead_code)]
1054 #[derive(MultipartForm)]
1055 struct NullSinkForm {
1056 foo: NullSink,
1057 }
1058
1059 #[post("/")]
1060 async fn null_sink(_form: MultipartForm<NullSinkForm>) -> HttpResponse {
1061 unreachable!("form should panic before reaching this point");
1062 }
1063
1064 let app = init_service(App::new().service(null_sink)).await;
1065
1066 let (body, headers) = create_form_data_payload_and_headers(
1067 "foo",
1068 None,
1069 None,
1070 web::Bytes::from_static(b"test data"),
1071 );
1072
1073 let req = headers
1074 .into_iter()
1075 .fold(TestRequest::post(), |req, header| req.insert_header(header))
1076 .set_payload(body)
1077 .to_request();
1078
1079 let _res = call_service(&app, req).await;
1081 }
1082}