racoon/core/forms/
mod.rs

1use std::{collections::HashMap, path::PathBuf};
2
3use async_tempfile::TempFile;
4
5#[derive(Debug)]
6pub struct FileField {
7    pub name: String,
8    temp_file: TempFile,
9    pub temp_path: PathBuf,
10}
11
12impl FileField {
13    pub fn from<S: AsRef<str>>(name: S, temp_file: TempFile) -> Self {
14        let temp_path = temp_file.file_path().clone();
15
16        Self {
17            name: name.as_ref().to_string(),
18            temp_file,
19            temp_path,
20        }
21    }
22
23    pub fn temp_file(&self) -> &TempFile {
24        &self.temp_file
25    }
26}
27
28pub type Files = HashMap<String, Vec<FileField>>;
29pub type FormData = HashMap<String, Vec<String>>;
30
31pub trait FileFieldShortcut {
32    /// Performs case-insensitive lookup and returns first file found.
33    fn value<S: AsRef<str>>(&self, name: S) -> Option<&FileField>;
34}
35
36impl FileFieldShortcut for Files {
37    fn value<S: AsRef<str>>(&self, name: S) -> Option<&FileField> {
38        let name = name.as_ref();
39
40        for (key, values) in self.iter() {
41            if key.to_lowercase() != name.to_lowercase() {
42                continue;
43            }
44
45            if let Some(field) = values.get(0) {
46                return Some(field);
47            }
48        }
49        None
50    }
51}
52
53///
54/// The form constraint works as a security measure while parsing request body.
55/// It can be set globally while creating the `Server` instance.
56///
57/// # Example
58///
59/// ```markdown
60///
61/// Server::bind("127.0.0.1:8080")
62///  .urls(paths)
63///  .form_constraints(FormConstraints {...})
64///  .run().await;
65/// ```
66///
67pub struct FormConstraints {
68    /// Maximum allowed body size.
69    max_body_size: usize,
70    /// Maximum allowed form part header size.
71    max_header_size: usize,
72    /// Maximum allowed form part file size.
73    max_file_size: usize,
74    /// Maximum allowed form field value size.
75    max_value_size: usize,
76    /// Map of field name and maximum allowed size.
77    custom_max_sizes: HashMap<String, usize>,
78}
79
80impl FormConstraints {
81    pub fn new(
82        max_body_size: usize,
83        max_header_size: usize,
84        max_file_size: usize,
85        max_value_size: usize,
86        custom_max_sizes: HashMap<String, usize>,
87    ) -> Self {
88        Self {
89            max_body_size,
90            max_header_size,
91            max_file_size,
92            max_value_size,
93            custom_max_sizes,
94        }
95    }
96
97    pub fn max_body_size(&self, buffer_size: usize) -> usize {
98        if buffer_size > self.max_body_size {
99            return buffer_size;
100        }
101
102        // Default size
103        self.max_body_size
104    }
105
106    pub fn max_header_size(&self, buffer_size: usize) -> usize {
107        if buffer_size > self.max_header_size {
108            return buffer_size;
109        }
110
111        // Default size
112        self.max_header_size
113    }
114
115    pub fn max_value_size(&self, buffer_size: usize) -> usize {
116        if buffer_size > self.max_value_size {
117            return buffer_size;
118        }
119
120        // Default size
121        self.max_value_size
122    }
123    pub fn max_size_for_field(&self, field_name: &String, buffer_size: usize) -> usize {
124        if let Some(max_size) = self.custom_max_sizes.get(field_name) {
125            if buffer_size > *max_size {
126                return buffer_size;
127            }
128            return max_size.to_owned();
129        }
130
131        // Default size
132        return self.max_value_size;
133    }
134
135    pub fn max_size_for_file(&self, field_name: &String, buffer_size: usize) -> usize {
136        if let Some(max_size) = self.custom_max_sizes.get(field_name) {
137            if buffer_size > *max_size {
138                return buffer_size;
139            }
140            return max_size.to_owned();
141        }
142
143        // Default size
144        return self.max_file_size;
145    }
146}
147
148#[derive(Debug)]
149pub enum FormFieldError {
150    /// Max form part body size exceeded.
151    MaxBodySizeExceed,
152    /// Maximum form part header size exceeded.
153    MaxHeaderSizeExceed,
154    /// Maximum file size exceeded.
155    MaxFileSizeExceed(String),
156    /// Maximum length of text length exceeded.
157    MaxValueSizeExceed(String),
158    /// (field_name, error, is_criticial)
159    /// If error is critical, don't expose to client.
160    Others(Option<String>, String, bool),
161}