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,
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    /// Test `Option` fields.
613    #[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    /// Test `Vec` fields.
637    #[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    /// Test `Option<Vec>` fields.
669    #[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    /// Test the `rename` field attribute.
703    #[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    /// Test the `deny_unknown_fields` struct attribute.
734    #[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    /// Test the `duplicate_field` struct attribute.
769    #[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    /// Test the Limits.
860    #[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        // Exceeds the 20 byte memory limit
896        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        // Memory limit should not apply when the data is being streamed to disk
902        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        // Within the 20 byte limit
922        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        // Exceeds the 20 byte overall limit
928        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        // Exceeds the 20 byte overall limit
934        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        // Within the 30 byte limit
966        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        // Exceeds the 30 byte limit
972        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        // Total of values (14 bytes) is within 30 byte limit for "field"
978        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        // Total of values exceeds 30 byte limit for "field"
985        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        // mangle content-type, keeping the boundary
1015        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                    // exhaust field stream
1043                    while let Some(_chunk) = field.try_next().await? {}
1044
1045                    // poll again, crash
1046                    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        // Run in this task so the test observes the field panic directly.
1080        let _res = call_service(&app, req).await;
1081    }
1082}