Escape backslash in special URL path components

Closes #468
This commit is contained in:
Steven Fackler 2018-11-06 10:07:17 -08:00
parent a07eac08a0
commit 20bebc236e
3 changed files with 39 additions and 3 deletions

View file

@ -140,6 +140,11 @@ define_encode_set! {
/// space, double quote ("), hash (#), inequality qualifiers (<), (>), backtick (`),
/// question mark (?), and curly brackets ({), (}), percent sign (%), forward slash (/) are
/// encoded.
///
/// # Note
///
/// For [special URLs](https://url.spec.whatwg.org/#is-special), the backslash (\) character should
/// additionally be escaped, but that is *not* included in this encode set.
pub PATH_SEGMENT_ENCODE_SET = [DEFAULT_ENCODE_SET] | {'%', '/'}
}

View file

@ -19,9 +19,24 @@ use host::{Host, HostInternal};
use percent_encoding::{
utf8_percent_encode, percent_encode,
SIMPLE_ENCODE_SET, DEFAULT_ENCODE_SET, USERINFO_ENCODE_SET, QUERY_ENCODE_SET,
PATH_SEGMENT_ENCODE_SET
PATH_SEGMENT_ENCODE_SET, EncodeSet
};
// The backslash (\) character is treated as a path separator in special URLs
// so it needs to be additionally escaped in that case.
#[derive(Clone)]
struct SPECIAL_PATH_SEGMENT_ENCODE_SET;
impl EncodeSet for SPECIAL_PATH_SEGMENT_ENCODE_SET {
#[inline]
fn contains(&self, byte: u8) -> bool {
match byte {
b'\\' => true,
_ => PATH_SEGMENT_ENCODE_SET.contains(byte)
}
}
}
pub type ParseResult<T> = Result<T, ParseError>;
macro_rules! simple_enum_error {
@ -1011,8 +1026,13 @@ impl<'a> Parser<'a> {
_ => {
self.check_url_code_point(c, &input);
if self.context == Context::PathSegmentSetter {
if scheme_type.is_special() {
self.serialization.extend(utf8_percent_encode(
utf8_c, SPECIAL_PATH_SEGMENT_ENCODE_SET));
} else {
self.serialization.extend(utf8_percent_encode(
utf8_c, PATH_SEGMENT_ENCODE_SET));
}
} else {
self.serialization.extend(utf8_percent_encode(
utf8_c, DEFAULT_ENCODE_SET));

View file

@ -109,6 +109,17 @@ fn new_directory_paths() {
}
}
#[test]
fn path_backslash_fun() {
let mut special_url = "http://foobar.com".parse::<Url>().unwrap();
special_url.path_segments_mut().unwrap().push("foo\\bar");
assert_eq!(special_url.as_str(), "http://foobar.com/foo%5Cbar");
let mut nonspecial_url = "thing://foobar.com".parse::<Url>().unwrap();
nonspecial_url.path_segments_mut().unwrap().push("foo\\bar");
assert_eq!(nonspecial_url.as_str(), "thing://foobar.com/foo\\bar");
}
#[test]
fn from_str() {
assert!("http://testing.com/this".parse::<Url>().is_ok());