diff --git a/examples/forms/src/main.rs b/examples/forms/src/main.rs index 45b410e1..633e90e3 100644 --- a/examples/forms/src/main.rs +++ b/examples/forms/src/main.rs @@ -12,7 +12,7 @@ use rocket::response::Redirect; #[derive(FromForm)] struct UserLogin<'r> { username: &'r str, - password: &'r str, + password: String, age: Result, } @@ -27,7 +27,7 @@ fn login<'a>(user_form: Form<'a, UserLogin<'a>>) -> Result { }; if user.username == "Sergio" { - match user.password { + match user.password.as_str() { "password" => Ok(Redirect::to("/user/Sergio")), _ => Err("Wrong password!".to_string()) } diff --git a/lib/src/request/form/from_form_value.rs b/lib/src/request/form/from_form_value.rs index 8e8b2afd..218badb4 100644 --- a/lib/src/request/form/from_form_value.rs +++ b/lib/src/request/form/from_form_value.rs @@ -73,21 +73,11 @@ impl<'v> FromFormValue<'v> for String { // This actually parses the value according to the standard. fn from_form_value(v: &'v str) -> Result { - let result = URI::percent_decode(v.as_bytes()); + let replaced = v.replace("+", " "); + let result = URI::percent_decode(replaced.as_bytes()); match result { Err(_) => Err(v), - Ok(mut string) => Ok({ - // Entirely safe because we're changing the single-byte '+'. - unsafe { - for c in string.to_mut().as_mut_vec() { - if *c == b'+' { - *c = b' '; - } - } - } - - string.into_owned() - }) + Ok(string) => Ok(string.into_owned()) } } } diff --git a/lib/tests/form_method-issue-45.rs b/lib/tests/form_method-issue-45.rs index ef071feb..ff1e8bff 100644 --- a/lib/tests/form_method-issue-45.rs +++ b/lib/tests/form_method-issue-45.rs @@ -42,6 +42,6 @@ fn get_passes_through() { .header(ContentType::Form) .body("_method=patch&form_data=Form+data"); - let mut response = req.dispatch_with(&rocket); + let response = req.dispatch_with(&rocket); assert_eq!(response.status(), Status::NotFound); } diff --git a/lib/tests/form_value_decoding-issue-82.rs b/lib/tests/form_value_decoding-issue-82.rs new file mode 100644 index 00000000..d49ddf55 --- /dev/null +++ b/lib/tests/form_value_decoding-issue-82.rs @@ -0,0 +1,45 @@ +#![feature(plugin, custom_derive)] +#![plugin(rocket_codegen)] + +extern crate rocket; + +use rocket::request::Form; + +#[derive(FromForm)] +struct FormData { + form_data: String, +} + +#[post("/", data = "")] +fn bug(form_data: Form) -> String { + form_data.into_inner().form_data +} + +use rocket::testing::MockRequest; +use rocket::http::Method::*; +use rocket::http::ContentType; +use rocket::http::Status; + +fn check_decoding(raw: &str, decoded: &str) { + let rocket = rocket::ignite().mount("/", routes![bug]); + + let mut req = MockRequest::new(Post, "/") + .header(ContentType::Form) + .body(format!("form_data={}", raw)); + + let mut response = req.dispatch_with(&rocket); + let body_string = response.body().and_then(|b| b.into_string()); + assert_eq!(response.status(), Status::Ok); + assert_eq!(Some(decoded.to_string()), body_string); +} + +#[test] +fn test_proper_decoding() { + check_decoding("password", "password"); + check_decoding("", ""); + check_decoding("+", " "); + check_decoding("%2B", "+"); + check_decoding("1+1", "1 1"); + check_decoding("1%2B1", "1+1"); + check_decoding("%3Fa%3D1%26b%3D2", "?a=1&b=2"); +}