diff --git a/url/src/lib.rs b/url/src/lib.rs index 0be004e6f..632aa702f 100644 --- a/url/src/lib.rs +++ b/url/src/lib.rs @@ -683,7 +683,14 @@ impl Url { assert_eq!(self.host_end, self.scheme_end + 1); assert_eq!(self.host, HostInternal::None); assert_eq!(self.port, None); - assert_eq!(self.path_start, self.scheme_end + 1); + if self.path().starts_with("//") { + // special case when first path fragment is empty + assert_eq!(self.byte_at(self.scheme_end + 1), b'/'); + assert_eq!(self.byte_at(self.scheme_end + 2), b'.'); + assert_eq!(self.path_start, self.scheme_end + 3); + } else { + assert_eq!(self.path_start, self.scheme_end + 1); + } } if let Some(start) = self.query_start { assert!(start >= self.path_start); diff --git a/url/src/parser.rs b/url/src/parser.rs index 7829e03a0..57b7181f5 100644 --- a/url/src/parser.rs +++ b/url/src/parser.rs @@ -467,19 +467,28 @@ impl<'a> Parser<'a> { return self.after_double_slash(input, scheme_type, scheme_end); } // Anarchist URL (no authority) - let path_start = to_u32(self.serialization.len())?; + let mut path_start = to_u32(self.serialization.len())?; let username_end = path_start; let host_start = path_start; let host_end = path_start; let host = HostInternal::None; let port = None; let remaining = if let Some(input) = input.split_prefix('/') { - let path_start = self.serialization.len(); self.serialization.push('/'); - self.parse_path(scheme_type, &mut false, path_start, input) + self.parse_path(scheme_type, &mut false, path_start as usize, input) } else { self.parse_cannot_be_a_base_path(input) }; + // This prevents web+demo:/.//not-a-host/ or web+demo:/path/..//not-a-host/, + // when parsed and then serialized, from ending up as web+demo://not-a-host/ + // (they end up as web+demo:/.//not-a-host/). + if self.serialization[path_start as usize..].starts_with("//") { + // If url’s host is null, url does not have an opaque path, + // url’s path’s size is greater than 1, and url’s path[0] is the empty string, + // then append U+002F (/) followed by U+002E (.) to output. + self.serialization.insert_str(path_start as usize, "/."); + path_start += 2; + } self.with_query_and_fragment( scheme_type, scheme_end, @@ -1281,16 +1290,6 @@ impl<'a> Parser<'a> { self.serialization.push_str(path.trim_start_matches('/')); } - // This prevents web+demo:/.//not-a-host/ or web+demo:/path/..//not-a-host/, - // when parsed and then serialized, from ending up as web+demo://not-a-host/ - // (they end up as web+demo:/.//not-a-host/). - if !*has_host && self.serialization[path_start..].starts_with("//") { - // If url’s host is null, url does not have an opaque path, - // url’s path’s size is greater than 1, and url’s path[0] is the empty string, - // then append U+002F (/) followed by U+002E (.) to output. - self.serialization.insert_str(path_start, "/."); - } - input } diff --git a/url/src/slicing.rs b/url/src/slicing.rs index a90337bb6..c2e441ef2 100644 --- a/url/src/slicing.rs +++ b/url/src/slicing.rs @@ -149,7 +149,14 @@ impl Url { } } - Position::AfterPort => self.path_start as usize, + Position::AfterPort => { + if let Some(port) = self.port { + debug_assert!(self.byte_at(self.host_end) == b':'); + self.host_end as usize + ":".len() + port.to_string().len() + } else { + self.host_end as usize + } + } Position::BeforePath => self.path_start as usize, diff --git a/url/tests/urltestdata.json b/url/tests/urltestdata.json index 8bda080a9..1a352bff1 100644 --- a/url/tests/urltestdata.json +++ b/url/tests/urltestdata.json @@ -7487,7 +7487,6 @@ "hash": "" }, "Serialize /. in path", - "skip next", { "input": "non-spec:/.//", "base": "about:blank", @@ -7502,7 +7501,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "non-spec:/..//", "base": "about:blank", @@ -7517,7 +7515,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "non-spec:/a/..//", "base": "about:blank", @@ -7532,7 +7529,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "non-spec:/.//path", "base": "about:blank", @@ -7547,7 +7543,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "non-spec:/..//path", "base": "about:blank", @@ -7562,7 +7557,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "non-spec:/a/..//path", "base": "about:blank", @@ -7637,7 +7631,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "", "base": "non-spec:/..//p", @@ -7652,7 +7645,6 @@ "search": "", "hash": "" }, - "skip next", { "input": "path", "base": "non-spec:/..//p",