Percent-encode characters: '[', ']', '\', '^', '|'.

Fixes #808.
This commit is contained in:
Sergio Benitez 2018-11-02 00:10:01 -07:00
parent 4dbd87a36f
commit 5762208bba
4 changed files with 84 additions and 17 deletions

View File

@ -15,8 +15,8 @@
#[macro_use]
extern crate pear;
extern crate smallvec;
#[macro_use]
extern crate percent_encoding;
#[doc(hidden)] #[macro_use]
pub extern crate percent_encoding;
extern crate cookie;
extern crate time;
extern crate indexmap;

View File

@ -59,6 +59,22 @@ pub enum Uri<'a> {
Asterisk,
}
/// This encode set is used for strings where '/' characters are known to be
/// safe; all other special path segment characters are encoded.
define_encode_set! {
#[doc(hidden)]
pub UNSAFE_PATH_ENCODE_SET = [::percent_encoding::DEFAULT_ENCODE_SET] | {
'%', '[', '\\', ']', '^', '|'
}
}
/// This encode set should be used for path segments (components) of a
/// `/`-separated path. It encodes as much as possible.
define_encode_set! {
#[doc(hidden)]
pub DEFAULT_ENCODE_SET = [UNSAFE_PATH_ENCODE_SET] | { '/' }
}
impl<'a> Uri<'a> {
#[inline]
crate unsafe fn raw_absolute(
@ -161,7 +177,7 @@ impl<'a> Uri<'a> {
/// Returns a URL-encoded version of the string. Any characters outside of
/// visible ASCII-range are encoded as well as ' ', '"', '#', '<', '>', '`',
/// '?', '{', '}', '%', and '/'.
/// '?', '{', '}', '%', '/', '[', '\\', ']', '^', and '|'.
///
/// # Examples
///
@ -173,8 +189,7 @@ impl<'a> Uri<'a> {
/// assert_eq!(encoded, "hello%3Fa=%3Cb%3Ehi%3C%2Fb%3E");
/// ```
pub fn percent_encode(string: &str) -> Cow<str> {
let set = ::percent_encoding::PATH_SEGMENT_ENCODE_SET;
::percent_encoding::utf8_percent_encode(string, set).into()
::percent_encoding::utf8_percent_encode(string, DEFAULT_ENCODE_SET).into()
}
/// Returns a URL-decoded version of the string. If the percent encoded

View File

@ -2,17 +2,10 @@ use std::fmt;
use std::path::{Path, PathBuf};
use std::borrow::Cow;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use percent_encoding::utf8_percent_encode;
use {RawStr, uri::{Uri, Formatter}, ext::Normalize};
mod priv_encode_set {
/// This encode set is used for strings where '/' characters are known to be
/// safe; all other special path segment characters are encoded.
define_encode_set! { pub PATH_ENCODE_SET = [super::DEFAULT_ENCODE_SET] | {'%'} }
}
use self::priv_encode_set::PATH_ENCODE_SET;
use uri::{Uri, Formatter, UNSAFE_PATH_ENCODE_SET};
use {RawStr, ext::Normalize};
/// Trait implemented by types that can be displayed as part of a URI in `uri!`.
///
@ -241,7 +234,7 @@ impl UriDisplay for PathBuf {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let string = self.normalized_str();
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
let enc: Cow<str> = utf8_percent_encode(&string, UNSAFE_PATH_ENCODE_SET).into();
f.write_raw(&enc)
}
}
@ -251,7 +244,7 @@ impl UriDisplay for Path {
#[inline]
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let string = self.normalized_str();
let enc: Cow<str> = utf8_percent_encode(&string, PATH_ENCODE_SET).into();
let enc: Cow<str> = utf8_percent_encode(&string, UNSAFE_PATH_ENCODE_SET).into();
f.write_raw(&enc)
}
}

View File

@ -0,0 +1,59 @@
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use] extern crate rocket;
use rocket::response::Redirect;
use rocket::http::uri::Uri;
const NAME: &str = "John[]|\\%@^";
#[get("/hello/<name>")]
fn hello(name: String) -> String {
format!("Hello, {}!", name)
}
#[get("/raw")]
fn raw_redirect() -> Redirect {
Redirect::to(format!("/hello/{}", Uri::percent_encode(NAME)))
}
#[get("/uri")]
fn uri_redirect() -> Redirect {
Redirect::to(uri!(hello: NAME))
}
fn rocket() -> rocket::Rocket {
rocket::ignite().mount("/", routes![hello, uri_redirect, raw_redirect])
}
mod tests {
use super::*;
use rocket::local::Client;
use rocket::http::{Status, uri::Uri};
#[test]
fn uri_percent_encoding_redirect() {
let expected_location = vec!["/hello/John%5B%5D%7C%5C%25@%5E"];
let client = Client::new(rocket()).unwrap();
let response = client.get("/raw").dispatch();
let location: Vec<_> = response.headers().get("location").collect();
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(&location, &expected_location);
let response = client.get("/uri").dispatch();
let location: Vec<_> = response.headers().get("location").collect();
assert_eq!(response.status(), Status::SeeOther);
assert_eq!(&location, &expected_location);
}
#[test]
fn uri_percent_encoding_get() {
let client = Client::new(rocket()).unwrap();
let name = Uri::percent_encode(NAME);
let mut response = client.get(format!("/hello/{}", name)).dispatch();
assert_eq!(response.status(), Status::Ok);
assert_eq!(response.body_string().unwrap(), format!("Hello, {}!", NAME));
}
}