Add a way to add headers to parts of multipart form data (#331)
This commit is contained in:
		| @@ -8,6 +8,7 @@ use std::path::Path; | |||||||
| use mime_guess::{self, Mime}; | use mime_guess::{self, Mime}; | ||||||
| use url::percent_encoding; | use url::percent_encoding; | ||||||
| use uuid::Uuid; | use uuid::Uuid; | ||||||
|  | use http::HeaderMap; | ||||||
|  |  | ||||||
| use {Body}; | use {Body}; | ||||||
|  |  | ||||||
| @@ -131,6 +132,7 @@ pub struct Part { | |||||||
|     value: Body, |     value: Body, | ||||||
|     mime: Option<Mime>, |     mime: Option<Mime>, | ||||||
|     file_name: Option<Cow<'static, str>>, |     file_name: Option<Cow<'static, str>>, | ||||||
|  |     headers: HeaderMap<String>, | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Part { | impl Part { | ||||||
| @@ -185,6 +187,7 @@ impl Part { | |||||||
|             value: value, |             value: value, | ||||||
|             mime: None, |             mime: None, | ||||||
|             file_name: None, |             file_name: None, | ||||||
|  |             headers: HeaderMap::default() | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -199,6 +202,16 @@ impl Part { | |||||||
|         self.file_name = Some(filename.into()); |         self.file_name = Some(filename.into()); | ||||||
|         self |         self | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /// Returns a reference to the map with additional header fields | ||||||
|  |     pub fn headers(&self) -> &HeaderMap<String> { | ||||||
|  |         &self.headers | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     /// Returns a reference to the map with additional header fields | ||||||
|  |     pub fn headers_mut(&mut self) -> &mut HeaderMap<String> { | ||||||
|  |         &mut self.headers | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl fmt::Debug for Part { | impl fmt::Debug for Part { | ||||||
| @@ -207,6 +220,7 @@ impl fmt::Debug for Part { | |||||||
|             .field("value", &self.value) |             .field("value", &self.value) | ||||||
|             .field("mime", &self.mime) |             .field("mime", &self.mime) | ||||||
|             .field("file_name", &self.file_name) |             .field("file_name", &self.file_name) | ||||||
|  |             .field("headers", &self.headers) | ||||||
|             .finish() |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -289,7 +303,7 @@ impl Read for Reader { | |||||||
|  |  | ||||||
| fn header(name: &str, field: &Part) -> String { | fn header(name: &str, field: &Part) -> String { | ||||||
|     format!( |     format!( | ||||||
|         "Content-Disposition: form-data; {}{}{}", |         "Content-Disposition: form-data; {}{}{}{}", | ||||||
|         format_parameter("name", name), |         format_parameter("name", name), | ||||||
|         match field.file_name { |         match field.file_name { | ||||||
|             Some(ref file_name) => format!("; {}", format_parameter("filename", file_name)), |             Some(ref file_name) => format!("; {}", format_parameter("filename", file_name)), | ||||||
| @@ -298,7 +312,11 @@ fn header(name: &str, field: &Part) -> String { | |||||||
|         match field.mime { |         match field.mime { | ||||||
|             Some(ref mime) => format!("\r\nContent-Type: {}", mime), |             Some(ref mime) => format!("\r\nContent-Type: {}", mime), | ||||||
|             None => "".to_string(), |             None => "".to_string(), | ||||||
|         } |         }, | ||||||
|  |         field.headers.iter().fold( | ||||||
|  |             "".to_string(), | ||||||
|  |             |header, (k,v)| header + "\r\n" + k.as_str() + ": " + v | ||||||
|  |         ) | ||||||
|     ) |     ) | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -409,6 +427,30 @@ mod tests { | |||||||
|         assert_eq!(length.unwrap(), expected.len() as u64); |         assert_eq!(length.unwrap(), expected.len() as u64); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn read_to_end_with_header() { | ||||||
|  |         let mut output = Vec::new(); | ||||||
|  |         let mut part = Part::text("value2").mime(::mime::IMAGE_BMP); | ||||||
|  |         part.headers_mut().insert("Hdr3", "/a/b/c".to_string()); | ||||||
|  |         let mut form = Form::new().part("key2", part); | ||||||
|  |         form.boundary = "boundary".to_string(); | ||||||
|  |         let expected = "--boundary\r\n\ | ||||||
|  |                         Content-Disposition: form-data; name=\"key2\"\r\n\ | ||||||
|  |                         Content-Type: image/bmp\r\n\ | ||||||
|  |                         hdr3: /a/b/c\r\n\ | ||||||
|  |                         \r\n\ | ||||||
|  |                         value2\r\n\ | ||||||
|  |                         --boundary--\r\n"; | ||||||
|  |         form.reader().read_to_end(&mut output).unwrap(); | ||||||
|  |         // These prints are for debug purposes in case the test fails | ||||||
|  |         println!( | ||||||
|  |             "START REAL\n{}\nEND REAL", | ||||||
|  |             ::std::str::from_utf8(&output).unwrap() | ||||||
|  |         ); | ||||||
|  |         println!("START EXPECTED\n{}\nEND EXPECTED", expected); | ||||||
|  |         assert_eq!(::std::str::from_utf8(&output).unwrap(), expected); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     #[test] |     #[test] | ||||||
|     fn header_percent_encoding() { |     fn header_percent_encoding() { | ||||||
|         let name = "start%'\"\r\nßend"; |         let name = "start%'\"\r\nßend"; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user