1#![allow(clippy::type_complexity)]
3pub mod bytes;
4pub mod json;
5#[cfg(feature = "tempfile")]
6pub mod tempfile;
7pub mod text;
8
9use actix_http::error::PayloadError;
10use actix_multipart::{Field, Multipart, MultipartError};
11use actix_web::dev::Payload;
12use actix_web::http::{header, StatusCode};
13use actix_web::{web, FromRequest, HttpRequest, ResponseError};
14use derive_more::{Deref, DerefMut, Display, Error, From};
15use futures_core::future::LocalBoxFuture;
16use futures_util::TryFutureExt;
17use futures_util::{FutureExt, TryStreamExt};
18use std::any::Any;
19use std::collections::HashMap;
20use std::future::{ready, Future};
21use std::sync::Arc;
22
23#[cfg(test)]
25extern crate self as actix_easy_multipart;
26
27#[doc(hidden)]
29pub use actix_multipart;
30
31pub use actix_easy_multipart_derive::MultipartForm;
141
142#[derive(Debug, Display, Error, From)]
143pub enum Error {
144 #[display(fmt = "{}", _0)]
145 Multipart(actix_multipart::MultipartError),
146
147 #[display(fmt = "An error occurred processing field `{field_name}`: {source}")]
149 Field {
150 field_name: String,
151 source: actix_web::Error,
152 },
153
154 #[display(fmt = "Duplicate field found for: `{}`", _0)]
156 #[from(ignore)]
157 DuplicateField(#[error(not(source))] String),
158
159 #[display(fmt = "Field with name `{}` is required", _0)]
161 #[from(ignore)]
162 MissingField(#[error(not(source))] String),
163
164 #[display(fmt = "Unsupported field `{}`", _0)]
166 #[from(ignore)]
167 UnsupportedField(#[error(not(source))] String),
168}
169
170impl ResponseError for Error {
171 fn status_code(&self) -> StatusCode {
172 match &self {
173 Error::Field { source, .. } => source.as_response_error().status_code(),
174 _ => StatusCode::BAD_REQUEST,
175 }
176 }
177}
178
179pub trait FieldReader<'t>: Sized + Any {
183 type Future: Future<Output = Result<Self, Error>>;
185
186 fn read_field(req: &'t HttpRequest, field: Field, limits: &'t mut Limits) -> Self::Future;
188}
189
190#[doc(hidden)]
192#[derive(Default, Deref, DerefMut)]
193pub struct State(pub HashMap<String, Box<dyn Any>>);
194
195#[doc(hidden)]
197pub trait FieldGroupReader<'t>: Sized + Any {
198 type Future: Future<Output = Result<(), Error>>;
199
200 fn handle_field(
202 req: &'t HttpRequest,
203 field: Field,
204 limits: &'t mut Limits,
205 state: &'t mut State,
206 duplicate_action: DuplicateAction,
207 ) -> Self::Future;
208
209 fn from_state(name: &str, state: &'t mut State) -> Result<Self, Error>;
211}
212
213impl<'t, T> FieldGroupReader<'t> for Option<T>
214where
215 T: FieldReader<'t>,
216{
217 type Future = LocalBoxFuture<'t, Result<(), Error>>;
218
219 fn handle_field(
220 req: &'t HttpRequest,
221 field: Field,
222 limits: &'t mut Limits,
223 state: &'t mut State,
224 duplicate_action: DuplicateAction,
225 ) -> Self::Future {
226 if state.contains_key(field.name()) {
227 match duplicate_action {
228 DuplicateAction::Ignore => return ready(Ok(())).boxed_local(),
229 DuplicateAction::Deny => {
230 return ready(Err(Error::DuplicateField(field.name().to_string())))
231 .boxed_local()
232 }
233 DuplicateAction::Replace => {}
234 }
235 }
236 async move {
237 let field_name = field.name().to_string();
238 let t = T::read_field(req, field, limits).await?;
239 state.insert(field_name, Box::new(t));
240 Ok(())
241 }
242 .boxed_local()
243 }
244
245 fn from_state(name: &str, state: &'t mut State) -> Result<Self, Error> {
246 Ok(state.remove(name).map(|m| *m.downcast::<T>().unwrap()))
247 }
248}
249
250impl<'t, T> FieldGroupReader<'t> for Vec<T>
251where
252 T: FieldReader<'t>,
253{
254 type Future = LocalBoxFuture<'t, Result<(), Error>>;
255
256 fn handle_field(
257 req: &'t HttpRequest,
258 field: Field,
259 limits: &'t mut Limits,
260 state: &'t mut State,
261 _duplicate_action: DuplicateAction,
262 ) -> Self::Future {
263 async move {
265 let field_name = field.name().to_string();
266 let vec = state
267 .entry(field_name)
268 .or_insert_with(|| Box::new(Vec::<T>::new()))
269 .downcast_mut::<Vec<T>>()
270 .unwrap();
271 let item = T::read_field(req, field, limits).await?;
272 vec.push(item);
273 Ok(())
274 }
275 .boxed_local()
276 }
277
278 fn from_state(name: &str, state: &'t mut State) -> Result<Self, Error> {
279 Ok(state
280 .remove(name)
281 .map(|m| *m.downcast::<Vec<T>>().unwrap())
282 .unwrap_or_default())
283 }
284}
285
286impl<'t, T> FieldGroupReader<'t> for T
287where
288 T: FieldReader<'t>,
289{
290 type Future = LocalBoxFuture<'t, Result<(), Error>>;
291
292 fn handle_field(
293 req: &'t HttpRequest,
294 field: Field,
295 limits: &'t mut Limits,
296 state: &'t mut State,
297 duplicate_action: DuplicateAction,
298 ) -> Self::Future {
299 if state.contains_key(field.name()) {
300 match duplicate_action {
301 DuplicateAction::Ignore => return ready(Ok(())).boxed_local(),
302 DuplicateAction::Deny => {
303 return ready(Err(Error::DuplicateField(field.name().to_string())))
304 .boxed_local()
305 }
306 DuplicateAction::Replace => {}
307 }
308 }
309 async move {
310 let field_name = field.name().to_string();
311 let t = T::read_field(req, field, limits).await?;
312 state.insert(field_name, Box::new(t));
313 Ok(())
314 }
315 .boxed_local()
316 }
317
318 fn from_state(name: &str, state: &'t mut State) -> Result<Self, Error> {
319 state
320 .remove(name)
321 .map(|m| *m.downcast::<T>().unwrap())
322 .ok_or_else(|| Error::MissingField(name.to_owned()))
323 }
324}
325
326pub trait MultipartFormTrait: Sized {
329 fn limit(field_name: &str) -> Option<usize>;
332
333 fn handle_field<'t>(
336 req: &'t HttpRequest,
337 field: Field,
338 limits: &'t mut Limits,
339 state: &'t mut State,
340 ) -> LocalBoxFuture<'t, Result<(), Error>>;
341
342 fn from_state(state: State) -> Result<Self, Error>;
345}
346
347#[doc(hidden)]
348pub enum DuplicateAction {
349 Ignore,
351 Deny,
353 Replace,
355}
356
357pub struct Limits {
359 pub total_limit_remaining: usize,
360 pub memory_limit_remaining: usize,
361 pub field_limit_remaining: Option<usize>,
362}
363
364impl Limits {
365 pub fn new(total_limit: usize, memory_limit: usize) -> Self {
366 Self {
367 total_limit_remaining: total_limit,
368 memory_limit_remaining: memory_limit,
369 field_limit_remaining: None,
370 }
371 }
372
373 pub fn try_consume_limits(&mut self, bytes: usize, in_memory: bool) -> Result<(), Error> {
381 self.total_limit_remaining = self
382 .total_limit_remaining
383 .checked_sub(bytes)
384 .ok_or(MultipartError::Payload(PayloadError::Overflow))?;
385 if in_memory {
386 self.memory_limit_remaining = self
387 .memory_limit_remaining
388 .checked_sub(bytes)
389 .ok_or(MultipartError::Payload(PayloadError::Overflow))?;
390 }
391 if let Some(field_limit) = self.field_limit_remaining {
392 self.field_limit_remaining = Some(
393 field_limit
394 .checked_sub(bytes)
395 .ok_or(MultipartError::Payload(PayloadError::Overflow))?,
396 );
397 }
398 Ok(())
399 }
400}
401
402#[derive(Deref, DerefMut)]
410pub struct MultipartForm<T: MultipartFormTrait>(pub T);
411
412impl<T: MultipartFormTrait> MultipartForm<T> {
413 pub fn into_inner(self) -> T {
415 self.0
416 }
417}
418
419impl<T> FromRequest for MultipartForm<T>
420where
421 T: MultipartFormTrait,
422{
423 type Error = actix_web::Error;
424 type Future = LocalBoxFuture<'static, Result<Self, Self::Error>>;
425
426 #[inline]
427 fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
428 let mut payload = Multipart::new(req.headers(), payload.take());
429 let config = MultipartFormConfig::from_req(req);
430 let mut limits = Limits::new(config.total_limit, config.memory_limit);
431 let req = req.clone();
432 let req2 = req.clone();
433 let err_handler = config.err_handler.clone();
434
435 async move {
436 let mut state = State::default();
437 let mut field_limits = HashMap::<String, Option<usize>>::new();
439
440 while let Some(field) = payload.try_next().await? {
441 let entry = field_limits
443 .entry(field.name().to_owned())
444 .or_insert_with(|| T::limit(field.name()));
445 limits.field_limit_remaining = entry.to_owned();
446
447 T::handle_field(&req, field, &mut limits, &mut state).await?;
448
449 *entry = limits.field_limit_remaining;
451 }
452 let inner = T::from_state(state)?;
453 Ok(MultipartForm(inner))
454 }
455 .map_err(move |e| {
456 if let Some(handler) = err_handler {
457 (*handler)(e, &req2)
458 } else {
459 e.into()
460 }
461 })
462 .boxed_local()
463 }
464}
465
466type MultipartFormErrorHandler =
467 Option<Arc<dyn Fn(Error, &HttpRequest) -> actix_web::Error + Send + Sync>>;
468
469#[derive(Clone)]
471pub struct MultipartFormConfig {
472 total_limit: usize,
473 memory_limit: usize,
474 err_handler: MultipartFormErrorHandler,
475}
476
477impl MultipartFormConfig {
478 pub fn total_limit(mut self, total_limit: usize) -> Self {
480 self.total_limit = total_limit;
481 self
482 }
483
484 pub fn memory_limit(mut self, memory_limit: usize) -> Self {
486 self.memory_limit = memory_limit;
487 self
488 }
489
490 pub fn error_handler<F>(mut self, f: F) -> Self
492 where
493 F: Fn(Error, &HttpRequest) -> actix_web::Error + Send + Sync + 'static,
494 {
495 self.err_handler = Some(Arc::new(f));
496 self
497 }
498
499 fn from_req(req: &HttpRequest) -> &Self {
502 req.app_data::<Self>()
503 .or_else(|| req.app_data::<web::Data<Self>>().map(|d| d.as_ref()))
504 .unwrap_or(&DEFAULT_CONFIG)
505 }
506}
507
508const DEFAULT_CONFIG: MultipartFormConfig = MultipartFormConfig {
509 total_limit: 52_428_800, memory_limit: 2_097_152, err_handler: None,
512};
513
514impl Default for MultipartFormConfig {
515 fn default() -> Self {
516 DEFAULT_CONFIG.clone()
517 }
518}
519
520fn field_mime(field: &Field) -> Option<mime::Mime> {
522 if field.headers().get(&header::CONTENT_TYPE).is_none() {
523 None
524 } else {
525 Some(field.content_type().clone())
526 }
527}
528
529#[cfg(test)]
530mod tests {
531 use super::MultipartForm;
532 use crate::bytes::Bytes;
533 use crate::text::Text;
534 use crate::MultipartFormConfig;
535 use actix_http::encoding::Decoder;
536 use actix_http::Payload;
537 use actix_multipart_rfc7578::client::multipart;
538 use actix_test::TestServer;
539 use actix_web::http::StatusCode;
540 use actix_web::{web, App, HttpResponse, Responder};
541 use awc::{Client, ClientResponse};
542
543 pub async fn send_form(
544 srv: &TestServer,
545 form: multipart::Form<'static>,
546 uri: &'static str,
547 ) -> ClientResponse<Decoder<Payload>> {
548 Client::default()
549 .post(srv.url(uri))
550 .content_type(form.content_type())
551 .send_body(multipart::Body::from(form))
552 .await
553 .unwrap()
554 }
555
556 #[derive(MultipartForm)]
559 struct TestOptions {
560 field1: Option<Text<String>>,
561 field2: Option<Text<String>>,
562 }
563
564 async fn test_options_route(form: MultipartForm<TestOptions>) -> impl Responder {
565 assert!(form.field1.is_some());
566 assert!(form.field2.is_none());
567 HttpResponse::Ok().finish()
568 }
569
570 #[actix_rt::test]
571 async fn test_options() {
572 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_options_route)));
573
574 let mut form = multipart::Form::default();
575 form.add_text("field1", "value");
576
577 let response = send_form(&srv, form, "/").await;
578 assert_eq!(response.status(), StatusCode::OK);
579 }
580
581 #[derive(MultipartForm)]
584 struct TestVec {
585 list1: Vec<Text<String>>,
586 list2: Vec<Text<String>>,
587 }
588
589 async fn test_vec_route(form: MultipartForm<TestVec>) -> impl Responder {
590 let form = form.into_inner();
591 let strings = form
592 .list1
593 .into_iter()
594 .map(|s| s.into_inner())
595 .collect::<Vec<_>>();
596 assert_eq!(strings, vec!["value1", "value2", "value3"]);
597 assert_eq!(form.list2.len(), 0);
598 HttpResponse::Ok().finish()
599 }
600
601 #[actix_rt::test]
602 async fn test_vec() {
603 let srv = actix_test::start(|| App::new().route("/", web::post().to(test_vec_route)));
604
605 let mut form = multipart::Form::default();
606 form.add_text("list1", "value1");
607 form.add_text("list1", "value2");
608 form.add_text("list1", "value3");
609
610 let response = send_form(&srv, form, "/").await;
611 assert_eq!(response.status(), StatusCode::OK);
612 }
613
614 #[derive(MultipartForm)]
617 struct TestFieldRenaming {
618 #[multipart(rename = "renamed")]
619 field1: Text<String>,
620 #[multipart(rename = "field1")]
621 field2: Text<String>,
622 field3: Text<String>,
623 }
624
625 async fn test_field_renaming_route(form: MultipartForm<TestFieldRenaming>) -> impl Responder {
626 assert_eq!(&*form.field1, "renamed");
627 assert_eq!(&*form.field2, "field1");
628 assert_eq!(&*form.field3, "field3");
629 HttpResponse::Ok().finish()
630 }
631
632 #[actix_rt::test]
633 async fn test_field_renaming() {
634 let srv =
635 actix_test::start(|| App::new().route("/", web::post().to(test_field_renaming_route)));
636
637 let mut form = multipart::Form::default();
638 form.add_text("renamed", "renamed");
639 form.add_text("field1", "field1");
640 form.add_text("field3", "field3");
641
642 let response = send_form(&srv, form, "/").await;
643 assert_eq!(response.status(), StatusCode::OK);
644 }
645
646 #[derive(MultipartForm)]
649 #[multipart(deny_unknown_fields)]
650 struct TestDenyUnknown {}
651
652 #[derive(MultipartForm)]
653 struct TestAllowUnknown {}
654
655 async fn test_deny_unknown_route(_: MultipartForm<TestDenyUnknown>) -> impl Responder {
656 HttpResponse::Ok().finish()
657 }
658
659 async fn test_allow_unknown_route(_: MultipartForm<TestAllowUnknown>) -> impl Responder {
660 HttpResponse::Ok().finish()
661 }
662
663 #[actix_rt::test]
664 async fn test_deny_unknown() {
665 let srv = actix_test::start(|| {
666 App::new()
667 .route("/deny", web::post().to(test_deny_unknown_route))
668 .route("/allow", web::post().to(test_allow_unknown_route))
669 });
670
671 let mut form = multipart::Form::default();
672 form.add_text("unknown", "value");
673 let response = send_form(&srv, form, "/deny").await;
674 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
675
676 let mut form = multipart::Form::default();
677 form.add_text("unknown", "value");
678 let response = send_form(&srv, form, "/allow").await;
679 assert_eq!(response.status(), StatusCode::OK);
680 }
681
682 #[derive(MultipartForm)]
685 #[multipart(duplicate_action = "deny")]
686 struct TestDuplicateDeny {
687 _field: Text<String>,
688 }
689
690 #[derive(MultipartForm)]
691 #[multipart(duplicate_action = "replace")]
692 struct TestDuplicateReplace {
693 field: Text<String>,
694 }
695
696 #[derive(MultipartForm)]
697 #[multipart(duplicate_action = "ignore")]
698 struct TestDuplicateIgnore {
699 field: Text<String>,
700 }
701
702 async fn test_duplicate_deny_route(_: MultipartForm<TestDuplicateDeny>) -> impl Responder {
703 HttpResponse::Ok().finish()
704 }
705
706 async fn test_duplicate_replace_route(
707 form: MultipartForm<TestDuplicateReplace>,
708 ) -> impl Responder {
709 assert_eq!(&*form.field, "second_value");
710 HttpResponse::Ok().finish()
711 }
712
713 async fn test_duplicate_ignore_route(
714 form: MultipartForm<TestDuplicateIgnore>,
715 ) -> impl Responder {
716 assert_eq!(&*form.field, "first_value");
717 HttpResponse::Ok().finish()
718 }
719
720 #[actix_rt::test]
721 async fn test_duplicate_action() {
722 let srv = actix_test::start(|| {
723 App::new()
724 .route("/deny", web::post().to(test_duplicate_deny_route))
725 .route("/replace", web::post().to(test_duplicate_replace_route))
726 .route("/ignore", web::post().to(test_duplicate_ignore_route))
727 });
728
729 let mut form = multipart::Form::default();
730 form.add_text("_field", "first_value");
731 form.add_text("_field", "second_value");
732 let response = send_form(&srv, form, "/deny").await;
733 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
734
735 let mut form = multipart::Form::default();
736 form.add_text("field", "first_value");
737 form.add_text("field", "second_value");
738 let response = send_form(&srv, form, "/replace").await;
739 assert_eq!(response.status(), StatusCode::OK);
740
741 let mut form = multipart::Form::default();
742 form.add_text("field", "first_value");
743 form.add_text("field", "second_value");
744 let response = send_form(&srv, form, "/ignore").await;
745 assert_eq!(response.status(), StatusCode::OK);
746 }
747
748 #[derive(MultipartForm)]
751 struct TestMemoryUploadLimits {
752 field: Bytes,
753 }
754
755 #[derive(MultipartForm)]
756 struct TestFileUploadLimits {
757 #[cfg(feature = "tempfile")]
758 field: crate::tempfile::Tempfile,
759 }
760
761 async fn test_upload_limits_memory(
762 form: MultipartForm<TestMemoryUploadLimits>,
763 ) -> impl Responder {
764 assert!(form.field.data.len() > 0);
765 HttpResponse::Ok().finish()
766 }
767
768 #[allow(unused_variables)]
769 async fn test_upload_limits_file(form: MultipartForm<TestFileUploadLimits>) -> impl Responder {
770 #[cfg(feature = "tempfile")]
771 assert!(form.field.size > 0);
772 HttpResponse::Ok().finish()
773 }
774
775 #[actix_rt::test]
776 async fn test_memory_limits() {
777 let srv = actix_test::start(|| {
778 App::new()
779 .route("/text", web::post().to(test_upload_limits_memory))
780 .route("/file", web::post().to(test_upload_limits_file))
781 .app_data(
782 MultipartFormConfig::default()
783 .memory_limit(20)
784 .total_limit(usize::MAX),
785 )
786 });
787
788 let mut form = multipart::Form::default();
790 form.add_text("field", "this string is 28 bytes long");
791 let response = send_form(&srv, form, "/text").await;
792 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
793
794 let mut form = multipart::Form::default();
796 form.add_text("field", "this string is 28 bytes long");
797 let response = send_form(&srv, form, "/file").await;
798 assert_eq!(response.status(), StatusCode::OK);
799 }
800
801 #[actix_rt::test]
802 #[cfg(feature = "tempfile")]
803 async fn test_total_limit() {
804 let srv = actix_test::start(|| {
805 App::new()
806 .route("/text", web::post().to(test_upload_limits_memory))
807 .route("/file", web::post().to(test_upload_limits_file))
808 .app_data(
809 MultipartFormConfig::default()
810 .memory_limit(usize::MAX)
811 .total_limit(20),
812 )
813 });
814
815 let mut form = multipart::Form::default();
817 form.add_text("field", "7 bytes");
818 let response = send_form(&srv, form, "/text").await;
819 assert_eq!(response.status(), StatusCode::OK);
820
821 let mut form = multipart::Form::default();
823 form.add_text("field", "this string is 28 bytes long");
824 let response = send_form(&srv, form, "/text").await;
825 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
826
827 let mut form = multipart::Form::default();
829 form.add_text("field", "this string is 28 bytes long");
830 let response = send_form(&srv, form, "/file").await;
831 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
832 }
833
834 #[derive(MultipartForm)]
835 struct TestFieldLevelLimits {
836 #[multipart(limit = "30B")]
837 field: Vec<Bytes>,
838 }
839
840 async fn test_field_level_limits_route(
841 form: MultipartForm<TestFieldLevelLimits>,
842 ) -> impl Responder {
843 assert!(form.field.len() > 0);
844 HttpResponse::Ok().finish()
845 }
846
847 #[actix_rt::test]
848 async fn test_field_level_limits() {
849 let srv = actix_test::start(|| {
850 App::new()
851 .route("/", web::post().to(test_field_level_limits_route))
852 .app_data(
853 MultipartFormConfig::default()
854 .memory_limit(usize::MAX)
855 .total_limit(usize::MAX),
856 )
857 });
858
859 let mut form = multipart::Form::default();
861 form.add_text("field", "this string is 28 bytes long");
862 let response = send_form(&srv, form, "/").await;
863 assert_eq!(response.status(), StatusCode::OK);
864
865 let mut form = multipart::Form::default();
867 form.add_text("field", "this string is more than 30 bytes long");
868 let response = send_form(&srv, form, "/").await;
869 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
870
871 let mut form = multipart::Form::default();
873 form.add_text("field", "7 bytes");
874 form.add_text("field", "7 bytes");
875 let response = send_form(&srv, form, "/").await;
876 assert_eq!(response.status(), StatusCode::OK);
877
878 let mut form = multipart::Form::default();
880 form.add_text("field", "this string is 28 bytes long");
881 form.add_text("field", "this string is 28 bytes long");
882 let response = send_form(&srv, form, "/").await;
883 assert_eq!(response.status(), StatusCode::BAD_REQUEST);
884 }
885}