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