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