Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly parse file URIs #421

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions src/uri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -802,10 +802,16 @@ fn parse_full(mut s: Bytes) -> Result<Uri, InvalidUri> {
}
Scheme2::Other(n) => {
// Grab the protocol
let mut scheme = s.split_to(n + 3);
let scheme = s.split_to(n);

// Strip ://, TODO: truncate
let _ = scheme.split_off(n);
// Remove ":" or "://" but not ":/"
if s[1] == b'/' && s[2] == b'/' {
// Remove "://"
let _ = s.split_to(3);
} else {
// Remove ":"
let _ = s.split_to(1);
}

// Allocate the ByteStr
let val = unsafe { ByteStr::from_utf8_unchecked(scheme) };
Expand Down Expand Up @@ -834,16 +840,16 @@ fn parse_full(mut s: Bytes) -> Result<Uri, InvalidUri> {
});
}

// Authority is required when absolute
if authority_end == 0 {
return Err(ErrorKind::InvalidFormat.into());
}

let authority = s.split_to(authority_end);
let authority = Authority {
data: unsafe { ByteStr::from_utf8_unchecked(authority) },
};

if authority.as_str().len() == 0 && s.len() == 0 {
// Neither path nor authority present.
return Err(ErrorKind::InvalidFormat.into());
}

Ok(Uri {
scheme: scheme.into(),
authority: authority,
Expand Down Expand Up @@ -901,11 +907,13 @@ impl PartialEq<str> for Uri {

other = &other[scheme.len()..];

if &other[..3] != b"://" {
if &other[..3] == b"://" {
other = &other[3..];
} else if &other[..1] == b":" {
other = &other[1..];
} else {
return false;
}

other = &other[3..];
}

if let Some(auth) = self.authority() {
Expand Down
18 changes: 9 additions & 9 deletions src/uri/scheme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,30 +281,30 @@ impl Scheme2<usize> {
}
}

if s.len() > 3 {
if s.len() >= 3 {
for i in 0..s.len() {
let b = s[i];

match SCHEME_CHARS[b as usize] {
b':' => {
// Not enough data remaining
if s.len() < i + 3 {
break;
}

// Not a scheme
if &s[i + 1..i + 3] != b"//" {
// According to https://tools.ietf.org/html/rfc3986#section-3 the URI "x:"
// has scheme "x", but to differentiate from shortcuts like
// "localhost:3000", which should be handled equivalent to
// "http://localhost:3000" we only treat an URI part as a scheme if the ':'
// is followed by a '/'.
if (i + 1) >= s.len() || s[i + 1] != b'/' {
break;
}

// Check length
if i > MAX_SCHEME_LEN {
return Err(ErrorKind::SchemeTooLong.into());
}

// Return scheme
return Ok(Scheme2::Other(i));
}
// Invald scheme character, abort
// Invalid scheme character, abort
0 => break,
_ => {}
}
Expand Down
28 changes: 28 additions & 0 deletions src/uri/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,34 @@ test_parse! {
port = Port::from_str("1234").ok(),
}

test_parse! {
test_uri_parse_scheme_single_slash,
"x:/y",
[],

scheme = part!("x"),
path = "/y",
}

test_parse! {
test_uri_parse_scheme_triple_slash,
"x:///y",
[],

scheme = part!("x"),
path = "/y",
}


test_parse! {
test_uri_parse_scheme_file,
"file:/foo/bar",
["file:///foo/bar"],

scheme = part!("file"),
path = "/foo/bar",
}

test_parse! {
test_userinfo1,
"http://a:[email protected]:1234/",
Expand Down