Skip to content

Commit

Permalink
Correctly parse file URIs
Browse files Browse the repository at this point in the history
This changes the URI parser to allow URIs of the forms
* scheme:/absolute/path
* scheme:///absolute/path

It does impact HTTP URI parsing in that HTTP URIs without an authority
part are now allowed.

Fixes issue hyperium#323
  • Loading branch information
Richard-W committed May 16, 2020
1 parent 59733e1 commit bd28b3f
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 21 deletions.
25 changes: 14 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,11 +840,6 @@ 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) },
Expand Down Expand Up @@ -901,11 +902,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
29 changes: 28 additions & 1 deletion 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 Expand Up @@ -419,7 +447,6 @@ fn test_uri_parse_error() {
Uri::from_str(s).unwrap_err();
}

err("http://");
err("htt:p//host");
err("hyper.rs/");
err("hyper.rs?key=val");
Expand Down

0 comments on commit bd28b3f

Please sign in to comment.