rust_web_server/body/multipart_form_data/
mod.rs1#[cfg(test)]
2mod tests;
3
4use std::io;
5use std::io::{BufRead, Cursor};
6use crate::ext::string_ext::StringExt;
7use crate::header::Header;
8use crate::symbol::SYMBOL;
9
10pub struct FormMultipartData;
12
13pub struct Part {
15 pub headers: Vec<Header>,
16 pub body: Vec<u8>,
17}
18
19impl Part {
20 pub fn get_header(&self, name: String) -> Option<&Header> {
21 let header = self.headers.iter().find(|x| x.name.to_lowercase() == name.to_lowercase());
22 header
23 }
24}
25
26impl FormMultipartData {
27 pub fn parse(data: &[u8], boundary: String) -> Result<Vec<Part>, String> {
30
31 let cursor = io::Cursor::new(data);
32 let bytes_read : i128 = 0;
33 let total_bytes : i128 = data.len() as i128;
34
35 let part_list : Vec<Part> = vec![];
36
37 let boxed_part_list = FormMultipartData::
38 parse_form_part_recursively(
39 cursor,
40 boundary,
41 bytes_read,
42 total_bytes,
43 part_list
44 );
45
46 if boxed_part_list.is_err() {
47 let message = boxed_part_list.err().unwrap();
48 return Err(message)
49 }
50
51 Ok(boxed_part_list.unwrap())
52 }
53
54 fn parse_form_part_recursively(
55 mut cursor: Cursor<&[u8]>,
56 boundary: String,
57 mut bytes_read: i128,
58 total_bytes: i128,
59 mut part_list: Vec<Part>) -> Result<Vec<Part>, String> {
60 loop {
64 let mut buf = vec![];
65 let mut part = Part { headers: vec![], body: vec![] };
66
67 if bytes_read == 0 {
69 let boxed_read = cursor.read_until(b'\n', &mut buf);
70 if boxed_read.is_err() {
71 let message = boxed_read.err().unwrap().to_string();
72 return Err(message);
73 }
74 let bytes_offset = boxed_read.unwrap();
75 let b : &[u8] = &buf;
76 bytes_read = bytes_read + bytes_offset as i128;
77
78 let boxed_line = String::from_utf8(Vec::from(b));
79 if boxed_line.is_err() {
80 let error_message = boxed_line.err().unwrap().to_string();
81 return Err(error_message);
82 }
83 let string = boxed_line.unwrap();
84 let string = StringExt::filter_ascii_control_characters(&string);
85 let string = StringExt::truncate_new_line_carriage_return(&string);
86
87 let _current_string_is_boundary =
88 string.replace(SYMBOL.hyphen, SYMBOL.empty_string)
89 .ends_with(&boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string));
90
91 if !_current_string_is_boundary {
92 let message = format!("Body in multipart/form-data request needs to start with a boundary, actual string: '{}'", string);
93 return Err(message.to_string())
94 }
95 }
96
97 let mut current_string_is_empty = false;
100 while !current_string_is_empty {
101 buf = vec![];
102 let boxed_read = cursor.read_until(b'\n', &mut buf);
103 if boxed_read.is_err() {
104 let message = boxed_read.err().unwrap().to_string();
105 return Err(message);
106 }
107 let bytes_offset = boxed_read.unwrap();
108 let b : &[u8] = &buf;
109 bytes_read = bytes_read + bytes_offset as i128;
110
111 let boxed_line = String::from_utf8(Vec::from(b));
112 if boxed_line.is_err() {
113 let error_message = boxed_line.err().unwrap().to_string();
114 return Err(error_message);
115 }
116 let string = boxed_line.unwrap();
117
118 let string = StringExt::filter_ascii_control_characters(&string);
119 current_string_is_empty = string.trim().len() == 0;
120
121 let _current_string_is_boundary =
122 string.replace(SYMBOL.hyphen, SYMBOL.empty_string)
123 .ends_with(&boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string));
124
125 if _current_string_is_boundary {
126 let message = "There is at least one missing body part in the multipart/form-data request";
127 return Err(message.to_string())
128 }
129
130 if bytes_read == total_bytes as i128 {
131 return Ok(part_list)
132 }
133
134
135 if current_string_is_empty && part.headers.len() == 0 {
137 let message = "One of the body parts does not have any header specified. At least Content-Disposition is required";
138 return Err(message.to_string());
139 }
140
141 if !current_string_is_empty {
142 let boxed_header = Header::parse_header(&string);
143 if boxed_header.is_err() {
144 let message = boxed_header.err().unwrap();
145 return Err(message)
146 }
147
148 let header = boxed_header.unwrap();
149 part.headers.push(header);
150 }
151 }
152
153
154 let mut _boundary_position = 0;
156 let mut current_string_is_boundary = false;
157 while !current_string_is_boundary {
158 buf = vec![];
159
160 let boxed_read = cursor.read_until(b'\n', &mut buf);
161 if boxed_read.is_err() {
162 let message = boxed_read.err().unwrap().to_string();
163 return Err(message);
164 }
165
166 let bytes_offset = boxed_read.unwrap();
167
168 if bytes_offset == 0 { break };
169
170 let b : &[u8] = &buf;
171
172 bytes_read = bytes_read + bytes_offset as i128;
173
174 let escaped_dash_boundary = boundary.replace(SYMBOL.hyphen, SYMBOL.empty_string);
175
176 current_string_is_boundary = false;
177 if b.len() >= escaped_dash_boundary.len() {
178 let boxed_sequence = FormMultipartData::find_subsequence(b, escaped_dash_boundary.as_bytes());
179 if boxed_sequence.is_some() {
180 current_string_is_boundary = true;
181 _boundary_position = boxed_sequence.unwrap();
182 }
183 }
184
185 if !current_string_is_boundary {
186 part.body.append(&mut buf.clone());
187 }
188
189 }
190
191 if !current_string_is_boundary && bytes_read == total_bytes as i128 {
192 let message = "No end boundary present in the multipart/form-data request body";
193 return Err(message.to_string());
194 }
195
196 let body_length = part.body.len();
199 if body_length > 2 { let is_new_line_carriage_return_ending =
201 *part.body.get(body_length-2).unwrap() == b'\r'
202 && *part.body.get(body_length-1).unwrap() == b'\n';
203
204 let is_new_line_ending =
205 *part.body.get(body_length-2).unwrap() != b'\r'
206 && *part.body.get(body_length-1).unwrap() == b'\n';
207
208 if is_new_line_carriage_return_ending {
209 part.body.remove(body_length - 1); part.body.remove(body_length - 2); }
212
213 if is_new_line_ending {
214 part.body.remove(body_length - 1); }
216 }
217
218
219
220 part_list.push(part);
221
222
223 if bytes_read == total_bytes as i128 {
224 return Ok(part_list)
225 }
226
227 }
228 }
229
230 pub fn extract_boundary(content_type: &str) -> Result<String, String> {
231 let boxed_split = content_type.split_once("boundary=");
232 if boxed_split.is_none() {
233 let message = "No boundary found in Content-Type header";
234 return Err(message.to_string())
235 }
236
237
238 let (_, boundary) = boxed_split.unwrap();
239 Ok(boundary.to_string())
240 }
241
242 fn find_subsequence(haystack: &[u8], needle: &[u8]) -> Option<usize> {
243 haystack.windows(needle.len()).position(|window| window == needle)
244 }
245
246 pub fn generate_part(part: Part) -> Result<Vec<u8>, String> {
247 if part.headers.len() == 0 {
248 let message = "One of the body parts does not have any header specified. At least Content-Disposition is required";
249 return Err(message.to_string())
250 }
251
252 let mut formatted_header_list : String = "".to_string();
253 for header in part.headers.into_iter() {
254 let formatted = format!("{}{}", header.as_string(), SYMBOL.new_line_carriage_return.to_string());
255 formatted_header_list = [formatted_header_list, formatted].join(SYMBOL.empty_string);
256 }
257
258 let header_body_delimiter = SYMBOL.new_line_carriage_return.to_string();
259
260 let body = part.body;
261
262 let part = [
263 formatted_header_list.as_bytes().to_vec(),
264 header_body_delimiter.as_bytes().to_vec(),
265 body
266 ].join(SYMBOL.empty_string.as_bytes());
267
268 Ok(part)
269 }
270
271 pub fn generate(part_list: Vec<Part>, boundary: &str) -> Result<Vec<u8>, String> {
272 if part_list.len() == 0 {
273 let message = "List of the multipart/form-data request body parts is empty";
274 return Err(message.to_string());
275 }
276
277 let mut bytes = vec![];
278 bytes.push(boundary.as_bytes().to_vec());
279
280 for part in part_list.into_iter() {
281 let boxed_part_as_bytes = FormMultipartData::generate_part(part);
282 if boxed_part_as_bytes.is_err() {
283 let message = boxed_part_as_bytes.err().unwrap();
284 return Err(message);
285 }
286 let part_as_bytes = boxed_part_as_bytes.unwrap();
287 bytes.push(part_as_bytes);
288 bytes.push(boundary.as_bytes().to_vec());
289 }
290
291 let result = bytes.join(SYMBOL.new_line_carriage_return.as_bytes());
292
293 Ok(result)
294 }
295}