Skip to main content

actix_multipart/form/
mod.rs

1//! Extract and process typed data from fields of a `multipart/form-data` request.
2
3use 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
28/// Trait that data types to be used in a multipart form struct should implement.
29///
30/// It represents an asynchronous handler that processes a multipart field to produce `Self`.
31pub trait FieldReader<'t>: Sized + Any {
32    /// Future that resolves to a `Self`.
33    type Future: Future<Output = Result<Self, MultipartError>>;
34
35    /// The form will call this function to handle the field.
36    ///
37    /// # Panics
38    ///
39    /// When reading the `field` payload using its `Stream` implementation, polling (manually or via
40    /// `next()`/`try_next()`) may panic after the payload is exhausted. If this is a problem for
41    /// your implementation of this method, you should [`fuse()`] the `Field` first.
42    ///
43    /// [`fuse()`]: futures_util::stream::StreamExt::fuse()
44    fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future;
45}
46
47/// Used to accumulate the state of the loaded fields.
48#[doc(hidden)]
49#[derive(Default, Deref, DerefMut)]
50pub struct State(pub HashMap<String, Box<dyn Any>>);
51
52/// Trait that the field collection types implement, i.e. `Vec<T>`, `Option<T>`, or `T` itself.
53#[doc(hidden)]
54pub trait FieldGroupReader<'t>: Sized + Any {
55    type Future: Future<Output = Result<(), MultipartError>>;
56
57    /// The form will call this function for each matching field.
58    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    /// Construct `Self` from the group of processed fields.
67    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            // Note: Vec GroupReader always allows duplicates
127
128            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
233/// Trait that allows a type to be used in the [`struct@MultipartForm`] extractor.
234///
235/// You should use the [`macro@MultipartForm`] macro to derive this for your struct.
236pub trait MultipartCollect: Sized {
237    /// An optional limit in bytes to be applied a given field name. Note this limit will be shared
238    /// across all fields sharing the same name.
239    fn limit(field_name: &str) -> Option<usize>;
240
241    /// The extractor will call this function for each incoming field, the state can be updated
242    /// with the processed field data.
243    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    /// Once all the fields have been processed and stored in the state, this is called
251    /// to convert into the struct representation.
252    fn from_state(state: State) -> Result<Self, MultipartError>;
253}
254
255#[doc(hidden)]
256pub enum DuplicateField {
257    /// Additional fields are not processed.
258    Ignore,
259
260    /// An error will be raised.
261    Deny,
262
263    /// All fields will be processed, the last one will replace all previous.
264    Replace,
265}
266
267/// Used to keep track of the remaining limits for the form and current field.
268pub 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    /// This function should be called within a [`FieldReader`] when reading each chunk of a field
284    /// to ensure that the form limits are not exceeded.
285    ///
286    /// # Arguments
287    ///
288    /// * `bytes` - The number of bytes being read from this chunk
289    /// * `in_memory` - Whether to consume from the memory limits
290    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/// Drain a field that will not be retained while still accounting for form limits.
320#[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/// Typed `multipart/form-data` extractor.
330///
331/// To extract typed data from a multipart stream, the inner type `T` must implement the
332/// [`MultipartCollect`] trait. You should use the [`macro@MultipartForm`] macro to derive this
333/// for your struct.
334///
335/// Note that this extractor rejects requests with any other Content-Type such as `multipart/mixed`,
336/// `multipart/related`, or non-multipart media types.
337///
338/// Add a [`MultipartFormConfig`] to your app data to configure extraction.
339///
340/// # Basic Use
341///
342/// Each field type should implement the [`FieldReader`] trait:
343///
344/// ```rust
345/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
346///
347/// #[derive(MultipartForm)]
348/// struct ImageUpload {
349///     description: Text<String>,
350///     timestamp: Text<i64>,
351///     image: TempFile,
352/// }
353/// ```
354///
355/// # Optional and List Fields
356///
357/// You can also use [`Vec<T>`](Vec) and [`Option<T>`](Option) provided that `T: FieldReader`.
358///
359/// A [`Vec`] field corresponds to an upload with multiple parts under the [same field
360/// name](https://www.rfc-editor.org/rfc/rfc7578#section-4.3).
361///
362/// ```rust
363/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
364///
365/// #[derive(MultipartForm)]
366/// struct Form {
367///     category: Option<Text<String>>,
368///     files: Vec<TempFile>,
369/// }
370/// ```
371///
372/// # Field Renaming
373///
374/// You can use the `#[multipart(rename = "foo")]` attribute to receive a field by a different name.
375///
376/// ```rust
377/// use actix_multipart::form::{tempfile::TempFile, MultipartForm};
378///
379/// #[derive(MultipartForm)]
380/// struct Form {
381///     #[multipart(rename = "files[]")]
382///     files: Vec<TempFile>,
383/// }
384/// ```
385///
386/// # Field Limits
387///
388/// You can use the `#[multipart(limit = "<size>")]` attribute to set field level limits. The limit
389/// string is parsed using [`bytesize`].
390///
391/// Note: the form is also subject to the global limits configured using [`MultipartFormConfig`].
392///
393/// ```rust
394/// use actix_multipart::form::{tempfile::TempFile, text::Text, MultipartForm};
395///
396/// #[derive(MultipartForm)]
397/// struct Form {
398///     #[multipart(limit = "2 KiB")]
399///     description: Text<String>,
400///
401///     #[multipart(limit = "512 MiB")]
402///     files: Vec<TempFile>,
403/// }
404/// ```
405///
406/// # Unknown Fields
407///
408/// By default fields with an unknown name are ignored. They can be rejected using the
409/// `#[multipart(deny_unknown_fields)]` attribute:
410///
411/// ```rust
412/// use actix_multipart::form::MultipartForm;
413///
414/// #[derive(MultipartForm)]
415/// #[multipart(deny_unknown_fields)]
416/// struct Form {}
417/// ```
418///
419/// # Duplicate Fields
420///
421/// The behaviour for when multiple fields with the same name are received can be changed using the
422/// `#[multipart(duplicate_field = "<behavior>")]` attribute:
423///
424/// - "ignore": (default) Extra fields are ignored. I.e., the first one is persisted.
425/// - "deny": A [`MultipartError::DuplicateField`] error response is returned.
426/// - "replace": Each field is processed, but only the last one is persisted.
427///
428/// Note that [`Vec`] fields will ignore this option.
429///
430/// ```rust
431/// use actix_multipart::form::MultipartForm;
432///
433/// #[derive(MultipartForm)]
434/// #[multipart(duplicate_field = "deny")]
435/// struct Form {}
436/// ```
437///
438/// [`bytesize`]: https://docs.rs/bytesize/2
439/// [`MultipartError::DuplicateField`]: crate::MultipartError::DuplicateField
440#[derive(Deref, DerefMut)]
441pub struct MultipartForm<T: MultipartCollect>(pub T);
442
443impl<T: MultipartCollect> MultipartForm<T> {
444    /// Unwrap into inner `T` value.
445    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            // this extractor only supports multipart/form-data
468            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                // ensure limits are shared for all fields with this name
483                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                    // Retrieve the limit for this field
492                    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                    // Update the stored limit
501                    *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/// [`struct@MultipartForm`] extractor configuration.
522///
523/// Add to your app data to have it picked up by [`struct@MultipartForm`] extractors.
524#[derive(Clone)]
525pub struct MultipartFormConfig {
526    total_limit: usize,
527    memory_limit: usize,
528    err_handler: MultipartFormErrorHandler,
529}
530
531impl MultipartFormConfig {
532    /// Sets maximum accepted payload size for the entire form. By default this limit is 50MiB.
533    pub fn total_limit(mut self, total_limit: usize) -> Self {
534        self.total_limit = total_limit;
535        self
536    }
537
538    /// Sets maximum accepted data that will be read into memory. By default this limit is 2MiB.
539    pub fn memory_limit(mut self, memory_limit: usize) -> Self {
540        self.memory_limit = memory_limit;
541        self
542    }
543
544    /// Sets custom error handler.
545    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    /// Extracts payload config from app data. Check both `T` and `Data<T>`, in that order, and fall
554    /// back to the default payload config.
555    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, // 50 MiB
564    memory_limit: 2_097_152, // 2 MiB
565    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    /// Test `Option` fields.
608    #[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    /// Test `Vec` fields.
632    #[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    /// Test `Option<Vec>` fields.
664    #[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    /// Test the `rename` field attribute.
698    #[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    /// Test the `deny_unknown_fields` struct attribute.
729    #[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    /// Test the `duplicate_field` struct attribute.
764    #[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    /// Test the Limits.
855    #[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        // Exceeds the 20 byte memory limit
891        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        // Memory limit should not apply when the data is being streamed to disk
897        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        // Within the 20 byte limit
917        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        // Exceeds the 20 byte overall limit
923        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        // Exceeds the 20 byte overall limit
929        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        // Within the 30 byte limit
961        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        // Exceeds the 30 byte limit
967        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        // Total of values (14 bytes) is within 30 byte limit for "field"
973        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        // Total of values exceeds 30 byte limit for "field"
980        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        // mangle content-type, keeping the boundary
1010        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                    // exhaust field stream
1038                    while let Some(_chunk) = field.try_next().await? {}
1039
1040                    // poll again, crash
1041                    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        // panics with Err(Connect(Disconnected)) due to form NullSink panic
1064        let _res = send_form(&srv, form, "/").await;
1065    }
1066}