1use std::borrow::Cow;
40use std::fmt;
41use std::fs::File;
42use std::io::{self, Cursor, Read};
43use std::path::Path;
44
45use mime_guess::{self, Mime};
46
47use super::Body;
48use crate::async_impl::multipart::{FormParts, PartMetadata, PartProps};
49use crate::header::HeaderMap;
50
51pub struct Form {
53 inner: FormParts<Part>,
54}
55
56pub struct Part {
58 meta: PartMetadata,
59 value: Body,
60}
61
62impl Default for Form {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl Form {
69 pub fn new() -> Form {
71 Form {
72 inner: FormParts::new(),
73 }
74 }
75
76 #[inline]
78 pub fn boundary(&self) -> &str {
79 self.inner.boundary()
80 }
81
82 pub fn text<T, U>(self, name: T, value: U) -> Form
92 where
93 T: Into<Cow<'static, str>>,
94 U: Into<Cow<'static, str>>,
95 {
96 self.part(name, Part::text(value))
97 }
98
99 pub fn file<T, U>(self, name: T, path: U) -> io::Result<Form>
117 where
118 T: Into<Cow<'static, str>>,
119 U: AsRef<Path>,
120 {
121 Ok(self.part(name, Part::file(path)?))
122 }
123
124 pub fn part<T>(self, name: T, part: Part) -> Form
126 where
127 T: Into<Cow<'static, str>>,
128 {
129 self.with_inner(move |inner| inner.part(name, part))
130 }
131
132 pub fn percent_encode_path_segment(self) -> Form {
134 self.with_inner(|inner| inner.percent_encode_path_segment())
135 }
136
137 pub fn percent_encode_attr_chars(self) -> Form {
139 self.with_inner(|inner| inner.percent_encode_attr_chars())
140 }
141
142 pub fn percent_encode_noop(self) -> Form {
144 self.with_inner(|inner| inner.percent_encode_noop())
145 }
146
147 pub(crate) fn reader(self) -> Reader {
148 Reader::new(self)
149 }
150
151 pub(crate) fn compute_length(&mut self) -> Option<u64> {
155 self.inner.compute_length()
156 }
157
158 fn with_inner<F>(self, func: F) -> Self
159 where
160 F: FnOnce(FormParts<Part>) -> FormParts<Part>,
161 {
162 Form {
163 inner: func(self.inner),
164 }
165 }
166}
167
168impl fmt::Debug for Form {
169 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170 self.inner.fmt_fields("Form", f)
171 }
172}
173
174impl Part {
175 pub fn text<T>(value: T) -> Part
177 where
178 T: Into<Cow<'static, str>>,
179 {
180 let body = match value.into() {
181 Cow::Borrowed(slice) => Body::from(slice),
182 Cow::Owned(string) => Body::from(string),
183 };
184 Part::new(body)
185 }
186
187 pub fn bytes<T>(value: T) -> Part
189 where
190 T: Into<Cow<'static, [u8]>>,
191 {
192 let body = match value.into() {
193 Cow::Borrowed(slice) => Body::from(slice),
194 Cow::Owned(vec) => Body::from(vec),
195 };
196 Part::new(body)
197 }
198
199 pub fn reader<T: Read + Send + 'static>(value: T) -> Part {
203 Part::new(Body::new(value))
204 }
205
206 pub fn reader_with_length<T: Read + Send + 'static>(value: T, length: u64) -> Part {
210 Part::new(Body::sized(value, length))
211 }
212
213 pub fn file<T: AsRef<Path>>(path: T) -> io::Result<Part> {
219 let path = path.as_ref();
220 let file_name = path
221 .file_name()
222 .map(|filename| filename.to_string_lossy().into_owned());
223 let ext = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
224 let mime = mime_guess::from_ext(ext).first_or_octet_stream();
225 let file = File::open(path)?;
226 let field = Part::new(Body::from(file)).mime(mime);
227
228 Ok(if let Some(file_name) = file_name {
229 field.file_name(file_name)
230 } else {
231 field
232 })
233 }
234
235 fn new(value: Body) -> Part {
236 Part {
237 meta: PartMetadata::new(),
238 value,
239 }
240 }
241
242 pub fn mime_str(self, mime: &str) -> crate::Result<Part> {
244 Ok(self.mime(mime.parse().map_err(crate::error::builder)?))
245 }
246
247 fn mime(self, mime: Mime) -> Part {
249 self.with_inner(move |inner| inner.mime(mime))
250 }
251
252 pub fn file_name<T>(self, filename: T) -> Part
254 where
255 T: Into<Cow<'static, str>>,
256 {
257 self.with_inner(move |inner| inner.file_name(filename))
258 }
259
260 pub fn headers(self, headers: HeaderMap) -> Part {
262 self.with_inner(move |inner| inner.headers(headers))
263 }
264
265 fn with_inner<F>(self, func: F) -> Self
266 where
267 F: FnOnce(PartMetadata) -> PartMetadata,
268 {
269 Part {
270 meta: func(self.meta),
271 value: self.value,
272 }
273 }
274}
275
276impl fmt::Debug for Part {
277 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
278 let mut dbg = f.debug_struct("Part");
279 dbg.field("value", &self.value);
280 self.meta.fmt_fields(&mut dbg);
281 dbg.finish()
282 }
283}
284
285impl PartProps for Part {
286 fn value_len(&self) -> Option<u64> {
287 self.value.len()
288 }
289
290 fn metadata(&self) -> &PartMetadata {
291 &self.meta
292 }
293}
294
295pub(crate) struct Reader {
296 form: Form,
297 active_reader: Option<Box<dyn Read + Send>>,
298}
299
300impl fmt::Debug for Reader {
301 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
302 f.debug_struct("Reader").field("form", &self.form).finish()
303 }
304}
305
306impl Reader {
307 fn new(form: Form) -> Reader {
308 let mut reader = Reader {
309 form,
310 active_reader: None,
311 };
312 reader.next_reader();
313 reader
314 }
315
316 fn next_reader(&mut self) {
317 self.active_reader = if !self.form.inner.fields.is_empty() {
318 let (name, field) = self.form.inner.fields.remove(0);
320 let boundary = Cursor::new(format!("--{}\r\n", self.form.boundary()));
321 let header = Cursor::new({
322 let mut h = if !self.form.inner.computed_headers.is_empty() {
324 self.form.inner.computed_headers.remove(0)
325 } else {
326 self.form
327 .inner
328 .percent_encoding
329 .encode_headers(&name, field.metadata())
330 };
331 h.extend_from_slice(b"\r\n\r\n");
332 h
333 });
334 let reader = boundary
335 .chain(header)
336 .chain(field.value.into_reader())
337 .chain(Cursor::new("\r\n"));
338 if !self.form.inner.fields.is_empty() {
341 Some(Box::new(reader))
342 } else {
343 Some(Box::new(reader.chain(Cursor::new(format!(
344 "--{}--\r\n",
345 self.form.boundary()
346 )))))
347 }
348 } else {
349 None
350 }
351 }
352}
353
354impl Read for Reader {
355 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
356 let mut total_bytes_read = 0usize;
357 let mut last_read_bytes;
358 loop {
359 match self.active_reader {
360 Some(ref mut reader) => {
361 last_read_bytes = reader.read(&mut buf[total_bytes_read..])?;
362 total_bytes_read += last_read_bytes;
363 if total_bytes_read == buf.len() {
364 return Ok(total_bytes_read);
365 }
366 }
367 None => return Ok(total_bytes_read),
368 };
369 if last_read_bytes == 0 && !buf.is_empty() {
370 self.next_reader();
371 }
372 }
373 }
374}
375
376#[cfg(test)]
377mod tests {
378 use super::*;
379
380 #[test]
381 fn form_empty() {
382 let mut output = Vec::new();
383 let mut form = Form::new();
384 let length = form.compute_length();
385 form.reader().read_to_end(&mut output).unwrap();
386 assert_eq!(output, b"");
387 assert_eq!(length.unwrap(), 0);
388 }
389
390 #[test]
391 fn read_to_end() {
392 let mut output = Vec::new();
393 let mut form = Form::new()
394 .part("reader1", Part::reader(std::io::empty()))
395 .part("key1", Part::text("value1"))
396 .part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
397 .part("reader2", Part::reader(std::io::empty()))
398 .part("key3", Part::text("value3").file_name("filename"));
399 form.inner.boundary = "boundary".to_string();
400 let length = form.compute_length();
401 let expected = "--boundary\r\n\
402 Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
403 \r\n\
404 --boundary\r\n\
405 Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
406 value1\r\n\
407 --boundary\r\n\
408 Content-Disposition: form-data; name=\"key2\"\r\n\
409 Content-Type: image/bmp\r\n\r\n\
410 value2\r\n\
411 --boundary\r\n\
412 Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
413 \r\n\
414 --boundary\r\n\
415 Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
416 value3\r\n--boundary--\r\n";
417 form.reader().read_to_end(&mut output).unwrap();
418 println!(
420 "START REAL\n{}\nEND REAL",
421 std::str::from_utf8(&output).unwrap()
422 );
423 println!("START EXPECTED\n{expected}\nEND EXPECTED");
424 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
425 assert!(length.is_none());
426 }
427
428 #[test]
429 fn read_to_end_with_length() {
430 let mut output = Vec::new();
431 let mut form = Form::new()
432 .text("key1", "value1")
433 .part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
434 .part("key3", Part::text("value3").file_name("filename"));
435 form.inner.boundary = "boundary".to_string();
436 let length = form.compute_length();
437 let expected = "--boundary\r\n\
438 Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
439 value1\r\n\
440 --boundary\r\n\
441 Content-Disposition: form-data; name=\"key2\"\r\n\
442 Content-Type: image/bmp\r\n\r\n\
443 value2\r\n\
444 --boundary\r\n\
445 Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
446 value3\r\n--boundary--\r\n";
447 form.reader().read_to_end(&mut output).unwrap();
448 println!(
450 "START REAL\n{}\nEND REAL",
451 std::str::from_utf8(&output).unwrap()
452 );
453 println!("START EXPECTED\n{expected}\nEND EXPECTED");
454 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
455 assert_eq!(length.unwrap(), expected.len() as u64);
456 }
457
458 #[test]
459 fn read_to_end_with_header() {
460 let mut output = Vec::new();
461 let mut part = Part::text("value2").mime(mime::IMAGE_BMP);
462 let mut headers = HeaderMap::new();
463 headers.insert("Hdr3", "/a/b/c".parse().unwrap());
464 part = part.headers(headers);
465 let mut form = Form::new().part("key2", part);
466 form.inner.boundary = "boundary".to_string();
467 let expected = "--boundary\r\n\
468 Content-Disposition: form-data; name=\"key2\"\r\n\
469 Content-Type: image/bmp\r\n\
470 hdr3: /a/b/c\r\n\
471 \r\n\
472 value2\r\n\
473 --boundary--\r\n";
474 form.reader().read_to_end(&mut output).unwrap();
475 println!(
477 "START REAL\n{}\nEND REAL",
478 std::str::from_utf8(&output).unwrap()
479 );
480 println!("START EXPECTED\n{expected}\nEND EXPECTED");
481 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
482 }
483}