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 fn into_reader(self) -> impl Read {
153 self.reader()
154 }
155
156 pub(crate) fn compute_length(&mut self) -> Option<u64> {
160 self.inner.compute_length()
161 }
162
163 fn with_inner<F>(self, func: F) -> Self
164 where
165 F: FnOnce(FormParts<Part>) -> FormParts<Part>,
166 {
167 Form {
168 inner: func(self.inner),
169 }
170 }
171}
172
173impl fmt::Debug for Form {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 self.inner.fmt_fields("Form", f)
176 }
177}
178
179impl Part {
180 pub fn text<T>(value: T) -> Part
182 where
183 T: Into<Cow<'static, str>>,
184 {
185 let body = match value.into() {
186 Cow::Borrowed(slice) => Body::from(slice),
187 Cow::Owned(string) => Body::from(string),
188 };
189 Part::new(body)
190 }
191
192 pub fn bytes<T>(value: T) -> Part
194 where
195 T: Into<Cow<'static, [u8]>>,
196 {
197 let body = match value.into() {
198 Cow::Borrowed(slice) => Body::from(slice),
199 Cow::Owned(vec) => Body::from(vec),
200 };
201 Part::new(body)
202 }
203
204 pub fn reader<T: Read + Send + 'static>(value: T) -> Part {
208 Part::new(Body::new(value))
209 }
210
211 pub fn reader_with_length<T: Read + Send + 'static>(value: T, length: u64) -> Part {
215 Part::new(Body::sized(value, length))
216 }
217
218 pub fn file<T: AsRef<Path>>(path: T) -> io::Result<Part> {
224 let path = path.as_ref();
225 let file_name = path
226 .file_name()
227 .map(|filename| filename.to_string_lossy().into_owned());
228 let ext = path.extension().and_then(|ext| ext.to_str()).unwrap_or("");
229 let mime = mime_guess::from_ext(ext).first_or_octet_stream();
230 let file = File::open(path)?;
231 let field = Part::new(Body::from(file)).mime(mime);
232
233 Ok(if let Some(file_name) = file_name {
234 field.file_name(file_name)
235 } else {
236 field
237 })
238 }
239
240 fn new(value: Body) -> Part {
241 Part {
242 meta: PartMetadata::new(),
243 value,
244 }
245 }
246
247 pub fn mime_str(self, mime: &str) -> crate::Result<Part> {
249 Ok(self.mime(mime.parse().map_err(crate::error::builder)?))
250 }
251
252 fn mime(self, mime: Mime) -> Part {
254 self.with_inner(move |inner| inner.mime(mime))
255 }
256
257 pub fn file_name<T>(self, filename: T) -> Part
259 where
260 T: Into<Cow<'static, str>>,
261 {
262 self.with_inner(move |inner| inner.file_name(filename))
263 }
264
265 pub fn headers(self, headers: HeaderMap) -> Part {
267 self.with_inner(move |inner| inner.headers(headers))
268 }
269
270 fn with_inner<F>(self, func: F) -> Self
271 where
272 F: FnOnce(PartMetadata) -> PartMetadata,
273 {
274 Part {
275 meta: func(self.meta),
276 value: self.value,
277 }
278 }
279}
280
281impl fmt::Debug for Part {
282 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283 let mut dbg = f.debug_struct("Part");
284 dbg.field("value", &self.value);
285 self.meta.fmt_fields(&mut dbg);
286 dbg.finish()
287 }
288}
289
290impl PartProps for Part {
291 fn value_len(&self) -> Option<u64> {
292 self.value.len()
293 }
294
295 fn metadata(&self) -> &PartMetadata {
296 &self.meta
297 }
298}
299
300pub(crate) struct Reader {
301 form: Form,
302 active_reader: Option<Box<dyn Read + Send>>,
303}
304
305impl fmt::Debug for Reader {
306 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
307 f.debug_struct("Reader").field("form", &self.form).finish()
308 }
309}
310
311impl Reader {
312 fn new(form: Form) -> Reader {
313 let mut reader = Reader {
314 form,
315 active_reader: None,
316 };
317 reader.next_reader();
318 reader
319 }
320
321 fn next_reader(&mut self) {
322 self.active_reader = if !self.form.inner.fields.is_empty() {
323 let (name, field) = self.form.inner.fields.remove(0);
325 let boundary = Cursor::new(format!("--{}\r\n", self.form.boundary()));
326 let header = Cursor::new({
327 let mut h = if !self.form.inner.computed_headers.is_empty() {
329 self.form.inner.computed_headers.remove(0)
330 } else {
331 self.form
332 .inner
333 .percent_encoding
334 .encode_headers(&name, field.metadata())
335 };
336 h.extend_from_slice(b"\r\n\r\n");
337 h
338 });
339 let reader = boundary
340 .chain(header)
341 .chain(field.value.into_reader())
342 .chain(Cursor::new("\r\n"));
343 if !self.form.inner.fields.is_empty() {
346 Some(Box::new(reader))
347 } else {
348 Some(Box::new(reader.chain(Cursor::new(format!(
349 "--{}--\r\n",
350 self.form.boundary()
351 )))))
352 }
353 } else {
354 None
355 }
356 }
357}
358
359impl Read for Reader {
360 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
361 let mut total_bytes_read = 0usize;
362 let mut last_read_bytes;
363 loop {
364 match self.active_reader {
365 Some(ref mut reader) => {
366 last_read_bytes = reader.read(&mut buf[total_bytes_read..])?;
367 total_bytes_read += last_read_bytes;
368 if total_bytes_read == buf.len() {
369 return Ok(total_bytes_read);
370 }
371 }
372 None => return Ok(total_bytes_read),
373 };
374 if last_read_bytes == 0 && !buf.is_empty() {
375 self.next_reader();
376 }
377 }
378 }
379}
380
381#[cfg(test)]
382mod tests {
383 use super::*;
384
385 #[test]
386 fn form_empty() {
387 let mut output = Vec::new();
388 let mut form = Form::new();
389 let length = form.compute_length();
390 form.reader().read_to_end(&mut output).unwrap();
391 assert_eq!(output, b"");
392 assert_eq!(length.unwrap(), 0);
393 }
394
395 #[test]
396 fn read_to_end() {
397 let mut output = Vec::new();
398 let mut form = Form::new()
399 .part("reader1", Part::reader(std::io::empty()))
400 .part("key1", Part::text("value1"))
401 .part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
402 .part("reader2", Part::reader(std::io::empty()))
403 .part("key3", Part::text("value3").file_name("filename"));
404 form.inner.boundary = "boundary".to_string();
405 let length = form.compute_length();
406 let expected = "--boundary\r\n\
407 Content-Disposition: form-data; name=\"reader1\"\r\n\r\n\
408 \r\n\
409 --boundary\r\n\
410 Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
411 value1\r\n\
412 --boundary\r\n\
413 Content-Disposition: form-data; name=\"key2\"\r\n\
414 Content-Type: image/bmp\r\n\r\n\
415 value2\r\n\
416 --boundary\r\n\
417 Content-Disposition: form-data; name=\"reader2\"\r\n\r\n\
418 \r\n\
419 --boundary\r\n\
420 Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
421 value3\r\n--boundary--\r\n";
422 form.reader().read_to_end(&mut output).unwrap();
423 println!(
425 "START REAL\n{}\nEND REAL",
426 std::str::from_utf8(&output).unwrap()
427 );
428 println!("START EXPECTED\n{expected}\nEND EXPECTED");
429 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
430 assert!(length.is_none());
431 }
432
433 #[test]
434 fn read_to_end_with_length() {
435 let mut output = Vec::new();
436 let mut form = Form::new()
437 .text("key1", "value1")
438 .part("key2", Part::text("value2").mime(mime::IMAGE_BMP))
439 .part("key3", Part::text("value3").file_name("filename"));
440 form.inner.boundary = "boundary".to_string();
441 let length = form.compute_length();
442 let expected = "--boundary\r\n\
443 Content-Disposition: form-data; name=\"key1\"\r\n\r\n\
444 value1\r\n\
445 --boundary\r\n\
446 Content-Disposition: form-data; name=\"key2\"\r\n\
447 Content-Type: image/bmp\r\n\r\n\
448 value2\r\n\
449 --boundary\r\n\
450 Content-Disposition: form-data; name=\"key3\"; filename=\"filename\"\r\n\r\n\
451 value3\r\n--boundary--\r\n";
452 form.reader().read_to_end(&mut output).unwrap();
453 println!(
455 "START REAL\n{}\nEND REAL",
456 std::str::from_utf8(&output).unwrap()
457 );
458 println!("START EXPECTED\n{expected}\nEND EXPECTED");
459 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
460 assert_eq!(length.unwrap(), expected.len() as u64);
461 }
462
463 #[test]
464 fn read_to_end_with_header() {
465 let mut output = Vec::new();
466 let mut part = Part::text("value2").mime(mime::IMAGE_BMP);
467 let mut headers = HeaderMap::new();
468 headers.insert("Hdr3", "/a/b/c".parse().unwrap());
469 part = part.headers(headers);
470 let mut form = Form::new().part("key2", part);
471 form.inner.boundary = "boundary".to_string();
472 let expected = "--boundary\r\n\
473 Content-Disposition: form-data; name=\"key2\"\r\n\
474 Content-Type: image/bmp\r\n\
475 hdr3: /a/b/c\r\n\
476 \r\n\
477 value2\r\n\
478 --boundary--\r\n";
479 form.reader().read_to_end(&mut output).unwrap();
480 println!(
482 "START REAL\n{}\nEND REAL",
483 std::str::from_utf8(&output).unwrap()
484 );
485 println!("START EXPECTED\n{expected}\nEND EXPECTED");
486 assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
487 }
488}