1use super::{ActiveForm, FormFile, MultipartForm};
19use serde::de::{
20 self, DeserializeSeed, EnumAccess, Error as _, MapAccess, SeqAccess, VariantAccess, Visitor,
21};
22use serde::forward_to_deserialize_any;
23use serde::Deserializer;
24use std::collections::HashMap;
25use std::fmt;
26
27const FILE_HANDLE_KEY: &str = "$webx_form_file";
29
30#[derive(Debug, Clone)]
32pub struct FormError(String);
33
34impl FormError {
35 pub fn message(&self) -> &str {
37 &self.0
38 }
39}
40
41impl fmt::Display for FormError {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 f.write_str(&self.0)
44 }
45}
46
47impl std::error::Error for FormError {}
48
49impl de::Error for FormError {
50 fn custom<T: fmt::Display>(msg: T) -> Self {
51 FormError(msg.to_string())
52 }
53}
54
55enum ValueRef<'a> {
57 Text(&'a str),
58 File(usize),
60}
61
62struct Group<'a> {
63 name: &'a str,
64 values: Vec<ValueRef<'a>>,
65}
66
67pub struct FormDeserializer<'a> {
73 groups: Vec<Group<'a>>,
74}
75
76impl<'a> FormDeserializer<'a> {
77 pub fn new(form: &'a MultipartForm, route_params: &'a HashMap<String, String>) -> Self {
83 let mut groups: Vec<Group<'a>> = Vec::new();
84 let mut index: HashMap<&'a str, usize> = HashMap::new();
85
86 {
87 let mut push =
88 |groups: &mut Vec<Group<'a>>, name: &'a str, value: ValueRef<'a>| match index
89 .get(name)
90 {
91 Some(&at) => groups[at].values.push(value),
92 None => {
93 index.insert(name, groups.len());
94 groups.push(Group {
95 name,
96 values: vec![value],
97 });
98 }
99 };
100
101 for (name, value) in route_params {
102 push(&mut groups, name.as_str(), ValueRef::Text(value.as_str()));
103 }
104 for field in form.fields() {
105 if route_params.contains_key(&field.name) {
106 continue;
107 }
108 push(
109 &mut groups,
110 field.name.as_str(),
111 ValueRef::Text(field.value.as_str()),
112 );
113 }
114 for (position, file) in form.files().iter().enumerate() {
115 let name = file.field_name();
116 if route_params.contains_key(name) {
117 continue;
118 }
119 push(&mut groups, name, ValueRef::File(position));
120 }
121 }
122
123 Self { groups }
124 }
125}
126
127impl<'de, 'a> Deserializer<'de> for FormDeserializer<'a> {
128 type Error = FormError;
129
130 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
131 self.deserialize_map(visitor)
132 }
133
134 fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
135 visitor.visit_map(FormMapAccess {
136 groups: self.groups,
137 index: 0,
138 })
139 }
140
141 fn deserialize_struct<V: Visitor<'de>>(
142 self,
143 _name: &'static str,
144 _fields: &'static [&'static str],
145 visitor: V,
146 ) -> Result<V::Value, FormError> {
147 self.deserialize_map(visitor)
148 }
149
150 fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
151 visitor.visit_some(self)
152 }
153
154 fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
155 visitor.visit_unit()
156 }
157
158 fn deserialize_unit_struct<V: Visitor<'de>>(
159 self,
160 _name: &'static str,
161 visitor: V,
162 ) -> Result<V::Value, FormError> {
163 visitor.visit_unit()
164 }
165
166 fn deserialize_newtype_struct<V: Visitor<'de>>(
167 self,
168 _name: &'static str,
169 visitor: V,
170 ) -> Result<V::Value, FormError> {
171 visitor.visit_newtype_struct(self)
172 }
173
174 forward_to_deserialize_any! {
175 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
176 bytes byte_buf seq tuple tuple_struct enum identifier ignored_any
177 }
178}
179
180struct FormMapAccess<'a> {
181 groups: Vec<Group<'a>>,
182 index: usize,
183}
184
185impl<'de, 'a> MapAccess<'de> for FormMapAccess<'a> {
186 type Error = FormError;
187
188 fn next_key_seed<K: DeserializeSeed<'de>>(
189 &mut self,
190 seed: K,
191 ) -> Result<Option<K::Value>, FormError> {
192 if self.index >= self.groups.len() {
193 return Ok(None);
194 }
195 let key = seed.deserialize(StrDeserializer(self.groups[self.index].name))?;
196 Ok(Some(key))
197 }
198
199 fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, FormError> {
200 let values = &self.groups[self.index].values;
201 self.index += 1;
202 seed.deserialize(FormValueDeserializer { values })
203 }
204}
205
206struct FormValueDeserializer<'a> {
207 values: &'a [ValueRef<'a>],
208}
209
210impl<'a> FormValueDeserializer<'a> {
211 fn first_text(&self) -> Result<&'a str, FormError> {
217 self.values
218 .iter()
219 .find_map(|value| match value {
220 ValueRef::Text(text) => Some(*text),
221 ValueRef::File(_) => None,
222 })
223 .ok_or_else(|| FormError::custom("expected a text field, found a file part"))
224 }
225}
226
227macro_rules! parse_text {
228 ($method:ident, $visit:ident, $ty:ty, $label:literal) => {
229 fn $method<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
230 let text = self.first_text()?;
231 let value: $ty = text.parse().map_err(|_| {
232 FormError::custom(format!(
233 "cannot parse {text:?} as {} for {}",
234 $label,
235 stringify!($method)
236 ))
237 })?;
238 visitor.$visit(value)
239 }
240 };
241}
242
243impl<'de, 'a> Deserializer<'de> for FormValueDeserializer<'a> {
244 type Error = FormError;
245
246 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
247 match self.values {
248 [] => visitor.visit_unit(),
249 [ValueRef::Text(text)] => visitor.visit_str(text),
250 [ValueRef::File(position)] => visitor.visit_map(FormFileAccess::single(*position)),
251 many => visitor.visit_seq(FormSeqAccess {
252 values: many,
253 index: 0,
254 }),
255 }
256 }
257
258 fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
259 if self.values.is_empty() {
260 visitor.visit_none()
261 } else {
262 visitor.visit_some(self)
263 }
264 }
265
266 fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
267 visitor.visit_seq(FormSeqAccess {
268 values: self.values,
269 index: 0,
270 })
271 }
272
273 fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
274 match self.values {
275 [ValueRef::File(position)] => visitor.visit_map(FormFileAccess::single(*position)),
276 _ => Err(FormError::custom(
277 "expected a single file part for this field",
278 )),
279 }
280 }
281
282 fn deserialize_struct<V: Visitor<'de>>(
283 self,
284 _name: &'static str,
285 _fields: &'static [&'static str],
286 visitor: V,
287 ) -> Result<V::Value, FormError> {
288 self.deserialize_map(visitor)
289 }
290
291 fn deserialize_newtype_struct<V: Visitor<'de>>(
292 self,
293 _name: &'static str,
294 visitor: V,
295 ) -> Result<V::Value, FormError> {
296 visitor.visit_newtype_struct(self)
297 }
298
299 fn deserialize_enum<V: Visitor<'de>>(
300 self,
301 _name: &'static str,
302 _variants: &'static [&'static str],
303 visitor: V,
304 ) -> Result<V::Value, FormError> {
305 visitor.visit_enum(FormEnumAccess(self.first_text()?))
306 }
307
308 fn deserialize_unit<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
309 visitor.visit_unit()
310 }
311
312 fn deserialize_unit_struct<V: Visitor<'de>>(
313 self,
314 _name: &'static str,
315 visitor: V,
316 ) -> Result<V::Value, FormError> {
317 visitor.visit_unit()
318 }
319
320 fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
321 visitor.visit_str(self.first_text()?)
322 }
323
324 fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
325 visitor.visit_str(self.first_text()?)
326 }
327
328 fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
329 visitor.visit_str(self.first_text()?)
330 }
331
332 fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
333 visitor.visit_bytes(self.first_text()?.as_bytes())
334 }
335
336 fn deserialize_byte_buf<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
337 visitor.visit_byte_buf(self.first_text()?.as_bytes().to_vec())
338 }
339
340 parse_text!(deserialize_bool, visit_bool, bool, "a boolean");
341 parse_text!(deserialize_i8, visit_i8, i8, "an integer");
342 parse_text!(deserialize_i16, visit_i16, i16, "an integer");
343 parse_text!(deserialize_i32, visit_i32, i32, "an integer");
344 parse_text!(deserialize_i64, visit_i64, i64, "an integer");
345 parse_text!(deserialize_u8, visit_u8, u8, "an integer");
346 parse_text!(deserialize_u16, visit_u16, u16, "an integer");
347 parse_text!(deserialize_u32, visit_u32, u32, "an integer");
348 parse_text!(deserialize_u64, visit_u64, u64, "an integer");
349 parse_text!(deserialize_f32, visit_f32, f32, "a number");
350 parse_text!(deserialize_f64, visit_f64, f64, "a number");
351
352 fn deserialize_char<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
353 let text = self.first_text()?;
354 let mut chars = text.chars();
355 match (chars.next(), chars.next()) {
356 (Some(ch), None) => visitor.visit_char(ch),
357 _ => Err(FormError::custom(format!(
358 "expected a single character, got {text:?}"
359 ))),
360 }
361 }
362
363 forward_to_deserialize_any! {
364 i128 u128 tuple tuple_struct ignored_any
365 }
366}
367
368struct FormSeqAccess<'a> {
369 values: &'a [ValueRef<'a>],
370 index: usize,
371}
372
373impl<'de, 'a> SeqAccess<'de> for FormSeqAccess<'a> {
374 type Error = FormError;
375
376 fn next_element_seed<T: DeserializeSeed<'de>>(
377 &mut self,
378 seed: T,
379 ) -> Result<Option<T::Value>, FormError> {
380 if self.index >= self.values.len() {
381 return Ok(None);
382 }
383 let slice = &self.values[self.index..self.index + 1];
384 self.index += 1;
385 seed.deserialize(FormValueDeserializer { values: slice })
386 .map(Some)
387 }
388
389 fn size_hint(&self) -> Option<usize> {
390 Some(self.values.len() - self.index)
391 }
392}
393
394struct FormFileAccess {
400 position: usize,
401 index: usize,
402}
403
404impl FormFileAccess {
405 fn single(position: usize) -> Self {
406 Self { position, index: 0 }
407 }
408}
409
410impl<'de> MapAccess<'de> for FormFileAccess {
411 type Error = FormError;
412
413 fn next_key_seed<K: DeserializeSeed<'de>>(
414 &mut self,
415 seed: K,
416 ) -> Result<Option<K::Value>, FormError> {
417 if self.index > 0 {
418 return Ok(None);
419 }
420 seed.deserialize(StrDeserializer(FILE_HANDLE_KEY)).map(Some)
421 }
422
423 fn next_value_seed<V: DeserializeSeed<'de>>(&mut self, seed: V) -> Result<V::Value, FormError> {
424 self.index += 1;
425 seed.deserialize(U64Deserializer(self.position as u64))
426 }
427
428 fn size_hint(&self) -> Option<usize> {
429 Some(1 - self.index.min(1))
430 }
431}
432
433struct U64Deserializer(u64);
435
436impl<'de> Deserializer<'de> for U64Deserializer {
437 type Error = FormError;
438
439 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
440 visitor.visit_u64(self.0)
441 }
442
443 forward_to_deserialize_any! {
444 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
445 bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct
446 map struct enum identifier ignored_any
447 }
448}
449
450struct StrDeserializer<'a>(&'a str);
452
453impl<'de, 'a> Deserializer<'de> for StrDeserializer<'a> {
454 type Error = FormError;
455
456 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, FormError> {
457 visitor.visit_str(self.0)
458 }
459
460 forward_to_deserialize_any! {
461 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
462 bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct
463 map struct enum identifier ignored_any
464 }
465}
466
467struct FormEnumAccess<'a>(&'a str);
468
469impl<'de, 'a> EnumAccess<'de> for FormEnumAccess<'a> {
470 type Error = FormError;
471 type Variant = FormVariantAccess;
472
473 fn variant_seed<V: DeserializeSeed<'de>>(
474 self,
475 seed: V,
476 ) -> Result<(V::Value, Self::Variant), FormError> {
477 let variant = seed.deserialize(StrDeserializer(self.0))?;
478 Ok((variant, FormVariantAccess))
479 }
480}
481
482struct FormVariantAccess;
483
484impl<'de> VariantAccess<'de> for FormVariantAccess {
485 type Error = FormError;
486
487 fn unit_variant(self) -> Result<(), FormError> {
488 Ok(())
489 }
490
491 fn newtype_variant_seed<T: DeserializeSeed<'de>>(
492 self,
493 _seed: T,
494 ) -> Result<T::Value, FormError> {
495 Err(FormError::custom(
496 "form fields cannot carry a newtype enum variant; use a unit variant",
497 ))
498 }
499
500 fn tuple_variant<V: Visitor<'de>>(
501 self,
502 _len: usize,
503 _visitor: V,
504 ) -> Result<V::Value, FormError> {
505 Err(FormError::custom(
506 "form fields cannot carry a tuple enum variant; use a unit variant",
507 ))
508 }
509
510 fn struct_variant<V: Visitor<'de>>(
511 self,
512 _fields: &'static [&'static str],
513 _visitor: V,
514 ) -> Result<V::Value, FormError> {
515 Err(FormError::custom(
516 "form fields cannot carry a struct enum variant; use a unit variant",
517 ))
518 }
519}
520
521impl<'de> serde::Deserialize<'de> for FormFile {
526 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
527 deserializer.deserialize_map(FormFileVisitor)
528 }
529}
530
531struct FormFileVisitor;
532
533impl<'de> Visitor<'de> for FormFileVisitor {
534 type Value = FormFile;
535
536 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
537 f.write_str("an uploaded file part")
538 }
539
540 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<FormFile, A::Error> {
541 let mut handle: Option<u64> = None;
542 while let Some(key) = map.next_key::<String>()? {
543 if key == FILE_HANDLE_KEY {
544 handle = Some(map.next_value()?);
545 } else {
546 let _: de::IgnoredAny = map.next_value()?;
547 }
548 }
549
550 let Some(position) = handle else {
551 return Err(de::Error::custom(
552 "FormFile can only be bound from a multipart/form-data request",
553 ));
554 };
555
556 ActiveForm::resolve(position as usize).ok_or_else(|| {
557 de::Error::custom("FormFile can only be bound from a multipart/form-data request")
558 })
559 }
560}
561
562#[cfg(test)]
563mod tests {
564 use super::*;
565 use crate::form::FormFileBuilder;
566 use serde::Deserialize;
567
568 #[derive(Debug, Deserialize, PartialEq)]
569 struct Profile {
570 username: String,
571 age: u32,
572 verified: bool,
573 nickname: Option<String>,
574 }
575
576 fn text_form() -> MultipartForm {
577 let mut form = MultipartForm::new();
578 form.push_field("username", "ada");
579 form.push_field("age", "36");
580 form.push_field("verified", "true");
581 form
582 }
583
584 fn bind<T: serde::de::DeserializeOwned>(form: MultipartForm) -> Result<T, FormError> {
585 let form = std::sync::Arc::new(form);
586 let params = HashMap::new();
587 ActiveForm::run(std::sync::Arc::clone(&form), || {
588 T::deserialize(FormDeserializer::new(&form, ¶ms))
589 })
590 }
591
592 #[test]
593 fn binds_text_fields_to_a_struct() {
594 let profile: Profile = bind(text_form()).expect("bind failed");
595 assert_eq!(
596 profile,
597 Profile {
598 username: "ada".into(),
599 age: 36,
600 verified: true,
601 nickname: None,
602 }
603 );
604 }
605
606 #[test]
607 fn reports_a_clear_error_for_bad_numbers() {
608 let mut form = MultipartForm::new();
609 form.push_field("username", "ada");
610 form.push_field("age", "not-a-number");
611 form.push_field("verified", "true");
612 let err: FormError = bind::<Profile>(form).unwrap_err();
613 assert!(err.to_string().contains("cannot parse"), "got {err}");
614 }
615
616 #[test]
617 fn route_params_override_form_fields() {
618 let form = std::sync::Arc::new(text_form());
619 let mut params = HashMap::new();
620 params.insert("username".to_string(), "grace".to_string());
621 let profile = ActiveForm::run(std::sync::Arc::clone(&form), || {
622 Profile::deserialize(FormDeserializer::new(&form, ¶ms))
623 })
624 .unwrap();
625 assert_eq!(profile.username, "grace");
626 }
627
628 #[derive(Debug, Deserialize)]
629 struct Upload {
630 title: String,
631 avatar: FormFile,
632 }
633
634 #[tokio::test]
635 async fn binds_memory_backed_file_parts() {
636 let mut builder =
637 FormFileBuilder::new("avatar", "../evil/photo.png", Some("image/png".into()));
638 builder.write(b"\x89PNG").await.unwrap();
639 let mut form = MultipartForm::new();
640 form.push_field("title", "hi");
641 form.push_file(builder.finish().await.unwrap());
642
643 let upload: Upload = bind(form).unwrap();
644 assert_eq!(upload.title, "hi");
645 assert_eq!(upload.avatar.file_name(), "photo.png");
646 assert_eq!(upload.avatar.size(), 4);
647 assert_eq!(upload.avatar.read_bytes().await.unwrap(), b"\x89PNG");
648 }
649
650 #[derive(Debug, Deserialize)]
651 struct Gallery {
652 photos: Vec<FormFile>,
653 }
654
655 #[tokio::test]
656 async fn binds_repeated_file_parts_into_a_vec() {
657 let mut form = MultipartForm::new();
658 for (name, body) in [("a.png", &b"aaa"[..]), ("b.png", &b"bbbb"[..])] {
659 let mut builder = FormFileBuilder::new("photos", name, None);
660 builder.write(body).await.unwrap();
661 form.push_file(builder.finish().await.unwrap());
662 }
663 let gallery: Gallery = bind(form).unwrap();
664 assert_eq!(gallery.photos.len(), 2);
665 assert_eq!(gallery.photos[0].read_bytes().await.unwrap(), b"aaa");
666 assert_eq!(gallery.photos[1].read_bytes().await.unwrap(), b"bbbb");
667 }
668
669 #[tokio::test]
670 async fn spooled_files_outlive_the_form() {
671 let dir = tempfile::tempdir().unwrap();
672 let mut builder = FormFileBuilder::new("avatar", "big.bin", None)
673 .memory_threshold(1)
674 .spool_dir(dir.path().to_path_buf());
675 builder.write(b"0123456789").await.unwrap();
676 let mut form = MultipartForm::new();
677 form.push_file(builder.finish().await.unwrap());
678
679 let upload: Upload = {
680 let mut form = form.clone();
681 form.push_field("title", "hi");
682 bind(form).unwrap()
683 };
684 assert_eq!(upload.avatar.read_bytes().await.unwrap(), b"0123456789");
686 }
687
688 #[test]
689 fn json_cannot_smuggle_a_file_handle() {
690 let json = r#"{"title":"x","avatar":{"$webx_form_file":0}}"#;
691 let err = serde_json::from_str::<Upload>(json).unwrap_err();
692 assert!(
693 err.to_string().contains("multipart/form-data"),
694 "unexpected error: {err}"
695 );
696 }
697
698 #[test]
699 fn json_with_arbitrary_keys_is_rejected() {
700 let json = r#"{"title":"x","avatar":{"path":"/etc/passwd"}}"#;
701 let err = serde_json::from_str::<Upload>(json).unwrap_err();
702 assert!(
703 err.to_string().contains("multipart/form-data"),
704 "unexpected error: {err}"
705 );
706 }
707
708 #[derive(Debug, Deserialize, PartialEq)]
709 enum Kind {
710 Disk,
711 Ssd,
712 }
713
714 #[derive(Debug, Deserialize)]
715 struct WithEnum {
716 kind: Kind,
717 }
718
719 #[test]
720 fn binds_unit_enum_variants() {
721 let mut form = MultipartForm::new();
722 form.push_field("kind", "Ssd");
723 let bound: WithEnum = bind(form).unwrap();
724 assert_eq!(bound.kind, Kind::Ssd);
725 }
726}