add configuration to multipart::Form to choose percent-encoding format
Sets the default back to `path-segment`, since the "fix" breaks anyone using reqwest from before. Closes #363
This commit is contained in:
@@ -6,8 +6,7 @@ use std::io::{self, Cursor, Read};
|
||||
use std::path::Path;
|
||||
|
||||
use mime_guess::{self, Mime};
|
||||
use url::percent_encoding;
|
||||
use url::percent_encoding::EncodeSet;
|
||||
use url::percent_encoding::{self, EncodeSet, PATH_SEGMENT_ENCODE_SET};
|
||||
use uuid::Uuid;
|
||||
use http::HeaderMap;
|
||||
|
||||
@@ -18,6 +17,12 @@ pub struct Form {
|
||||
boundary: String,
|
||||
fields: Vec<(Cow<'static, str>, Part)>,
|
||||
headers: Vec<Vec<u8>>,
|
||||
percent_encoding: PercentEncoding,
|
||||
}
|
||||
|
||||
enum PercentEncoding {
|
||||
PathSegment,
|
||||
AttrChar,
|
||||
}
|
||||
|
||||
impl Form {
|
||||
@@ -27,6 +32,7 @@ impl Form {
|
||||
boundary: format!("{}", Uuid::new_v4().to_simple()),
|
||||
fields: Vec::new(),
|
||||
headers: Vec::new(),
|
||||
percent_encoding: PercentEncoding::PathSegment,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +90,18 @@ impl Form {
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure this `Form` to percent-encode using the `path-segment` rules.
|
||||
pub fn percent_encode_path_segment(mut self) -> Form {
|
||||
self.percent_encoding = PercentEncoding::PathSegment;
|
||||
self
|
||||
}
|
||||
|
||||
/// Configure this `Form` to percent-encode using the `attr-char` rules.
|
||||
pub fn percent_encode_attr_chars(mut self) -> Form {
|
||||
self.percent_encoding = PercentEncoding::AttrChar;
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn reader(self) -> Reader {
|
||||
Reader::new(self)
|
||||
}
|
||||
@@ -98,7 +116,7 @@ impl Form {
|
||||
Some(value_length) => {
|
||||
// We are constructing the header just to get its length. To not have to
|
||||
// construct it again when the request is sent we cache these headers.
|
||||
let header = header(name, field);
|
||||
let header = self.percent_encoding.encode_headers(name, field);
|
||||
let header_length = header.len();
|
||||
self.headers.push(header);
|
||||
// The additions mimick the format string out of which the field is constructed
|
||||
@@ -277,7 +295,7 @@ impl Reader {
|
||||
let mut h = if self.form.headers.len() > 0 {
|
||||
self.form.headers.remove(0)
|
||||
} else {
|
||||
header(&name, &field)
|
||||
self.form.percent_encoding.encode_headers(&name, &field)
|
||||
};
|
||||
h.extend_from_slice(b"\r\n\r\n");
|
||||
h
|
||||
@@ -350,12 +368,13 @@ impl EncodeSet for AttrCharEncodeSet {
|
||||
|
||||
}
|
||||
|
||||
fn header(name: &str, field: &Part) -> Vec<u8> {
|
||||
impl PercentEncoding {
|
||||
fn encode_headers(&self, name: &str, field: &Part) -> Vec<u8> {
|
||||
let s = format!(
|
||||
"Content-Disposition: form-data; {}{}{}",
|
||||
format_parameter("name", name),
|
||||
self.format_parameter("name", name),
|
||||
match field.file_name {
|
||||
Some(ref file_name) => format!("; {}", format_parameter("filename", file_name)),
|
||||
Some(ref file_name) => format!("; {}", self.format_parameter("filename", file_name)),
|
||||
None => String::new(),
|
||||
},
|
||||
match field.mime {
|
||||
@@ -370,12 +389,19 @@ fn header(name: &str, field: &Part) -> Vec<u8> {
|
||||
header.extend_from_slice(v.as_bytes());
|
||||
header
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn format_parameter(name: &str, value: &str) -> String {
|
||||
let legal_value =
|
||||
fn format_parameter(&self, name: &str, value: &str) -> String {
|
||||
let legal_value = match *self {
|
||||
PercentEncoding::PathSegment => {
|
||||
percent_encoding::utf8_percent_encode(value, PATH_SEGMENT_ENCODE_SET)
|
||||
.to_string()
|
||||
},
|
||||
PercentEncoding::AttrChar => {
|
||||
percent_encoding::utf8_percent_encode(value, AttrCharEncodeSet)
|
||||
.to_string();
|
||||
.to_string()
|
||||
},
|
||||
};
|
||||
if value.len() == legal_value.len() {
|
||||
// nothing has been percent encoded
|
||||
format!("{}=\"{}\"", name, value)
|
||||
@@ -383,8 +409,10 @@ fn format_parameter(name: &str, value: &str) -> String {
|
||||
// something has been percent encoded
|
||||
format!("{}*=utf-8''{}", name, legal_value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -507,8 +535,15 @@ mod tests {
|
||||
fn header_percent_encoding() {
|
||||
let name = "start%'\"\r\nßend";
|
||||
let field = Part::text("");
|
||||
let expected = "Content-Disposition: form-data; name*=utf-8''start%25%27%22%0D%0A%C3%9Fend";
|
||||
|
||||
assert_eq!(header(name, &field), expected.as_bytes());
|
||||
assert_eq!(
|
||||
PercentEncoding::PathSegment.encode_headers(name, &field),
|
||||
&b"Content-Disposition: form-data; name*=utf-8''start%25'%22%0D%0A%C3%9Fend"[..]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
PercentEncoding::AttrChar.encode_headers(name, &field),
|
||||
&b"Content-Disposition: form-data; name*=utf-8''start%25%27%22%0D%0A%C3%9Fend"[..]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user