diff --git a/src/uri/mod.rs b/src/uri/mod.rs index eb7781a8..16b45c84 100644 --- a/src/uri/mod.rs +++ b/src/uri/mod.rs @@ -135,6 +135,7 @@ enum ErrorKind { SchemeMissing, AuthorityMissing, PathAndQueryMissing, + PathDoesNotStartWithSlash, TooLong, Empty, SchemeTooLong, @@ -872,10 +873,17 @@ fn parse_full(mut s: Bytes) -> Result { data: unsafe { ByteStr::from_utf8_unchecked(authority) }, }; + // When absolute, path is coered to / if empty. + let path_and_query = if s.is_empty() { + PathAndQuery::slash() + } else { + PathAndQuery::from_shared(s)? + }; + Ok(Uri { scheme: scheme.into(), authority, - path_and_query: PathAndQuery::from_shared(s)?, + path_and_query, }) } @@ -1070,6 +1078,7 @@ impl InvalidUri { ErrorKind::SchemeMissing => "scheme missing", ErrorKind::AuthorityMissing => "authority missing", ErrorKind::PathAndQueryMissing => "path missing", + ErrorKind::PathDoesNotStartWithSlash => "path does not start with slash", ErrorKind::TooLong => "uri too long", ErrorKind::Empty => "empty string", ErrorKind::SchemeTooLong => "scheme too long", diff --git a/src/uri/path.rs b/src/uri/path.rs index 4c0bbbe7..dfbb2e95 100644 --- a/src/uri/path.rs +++ b/src/uri/path.rs @@ -412,6 +412,14 @@ const fn scan_path_and_query(bytes: &[u8]) -> Result { let mut is_maybe_not_utf8 = false; + if bytes.is_empty() { + return Err(ErrorKind::Empty); + } + + if !matches!(bytes[0], b'/' | b'?' | b'#') { + return Err(ErrorKind::PathDoesNotStartWithSlash); + } + while i < bytes.len() { // See https://url.spec.whatwg.org/#path-state match bytes[i] { @@ -621,6 +629,16 @@ mod tests { PathAndQuery::try_from(&[b'/', b'a', b'?', 0xFF][..]).expect_err("reject invalid utf8"); } + #[test] + fn rejects_empty_string() { + PathAndQuery::try_from("").expect_err("reject empty str"); + } + + #[test] + fn requires_starting_with_slash() { + PathAndQuery::try_from("sneaky").expect_err("reject missing slash"); + } + #[test] fn json_is_fine() { assert_eq!(