diff options
Diffstat (limited to 'vendor/url')
-rw-r--r-- | vendor/url/.cargo-checksum.json | 2 | ||||
-rw-r--r-- | vendor/url/Cargo.toml | 63 | ||||
-rw-r--r-- | vendor/url/LICENSE-MIT | 2 | ||||
-rw-r--r-- | vendor/url/README.md | 14 | ||||
-rw-r--r-- | vendor/url/src/host.rs | 86 | ||||
-rw-r--r-- | vendor/url/src/lib.rs | 82 | ||||
-rw-r--r-- | vendor/url/src/origin.rs | 3 | ||||
-rw-r--r-- | vendor/url/src/parser.rs | 37 | ||||
-rw-r--r-- | vendor/url/src/quirks.rs | 59 | ||||
-rw-r--r-- | vendor/url/tests/data.rs | 79 | ||||
-rw-r--r-- | vendor/url/tests/debugger_visualizer.rs | 102 | ||||
-rw-r--r-- | vendor/url/tests/unit.rs | 54 | ||||
-rw-r--r-- | vendor/url/tests/urltestdata.json | 1625 |
13 files changed, 1715 insertions, 493 deletions
diff --git a/vendor/url/.cargo-checksum.json b/vendor/url/.cargo-checksum.json index 2c03fac5f..8a659101a 100644 --- a/vendor/url/.cargo-checksum.json +++ b/vendor/url/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"9814ea7672535568bb8c84b23512fe0119f85a14a2cc84d2f7f0b3848c61a65c","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e","src/host.rs":"88adbe3a8dc691a4e32d7ed664dc7f17f8a1a863ffa4cde4d618032a00ec6653","src/lib.rs":"30372556e2352f93fa56178b85174eb0c3a819e87b12c288b67a101f7535a712","src/origin.rs":"2b967dd04fa5852b4a89f13cc628f3bfd84d1432e9447b2d3cbc6fee8aba3738","src/parser.rs":"eab4f6c7bffc021e9722375447a259683b8257dc6dff784c715032c39d4b4923","src/path_segments.rs":"dd6b637245b2ad77ce96221df3f80c8b4ad858cd52aecc86b97166dec386882a","src/quirks.rs":"8a504a2eeb74429e3282f2628014f3af9a567284c2b08bd0360bfd72d3a17e43","src/slicing.rs":"25425fc5c4100a3a5da49d1e0b497f6536066afcc91c4ba4dff5ace0860acd40","tests/data.rs":"dace394aed466df0e8afea0037bb8c90306d30ce75efdaff39feb00dbd07fa79","tests/setters_tests.json":"486f6d129960d0d0d99b533caf9bef21113b31adcdb83296dfc4a59cd8431715","tests/unit.rs":"61e014d600aa558f4913ac7bc268d652eda67daead72b0b0cf288524b1d71d13","tests/urltestdata.json":"afe55dd9583d125305d31e152478de9cc19801006b413c5f4fb7d430c50b1d11"},"package":"a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"}
\ No newline at end of file +{"files":{"Cargo.toml":"c861c99782bfd4be160167bf659e87792f00d7e1eb1ee072a59546143fa980f4","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"76e972ac0f4ddb116e86e10100132a783931a596e7b9872eaa31be15cd4d751d","README.md":"71b01ec6f2f4ce47235ee430ba0c41afac563403a9dbcda23a584c3e915395ac","src/host.rs":"60cd21e0b4cbb208c36eec5d40628a8bbe1d7ef13e481479b33f89cef96277ec","src/lib.rs":"c48655bc0455efecb7f7e36dfee8b92f6d96d9a1fbc5b24890ef255fb7843956","src/origin.rs":"19a4b451e8615bfef7239d2fc719c489398fe5044edb0df7c84b54eef4ceba1b","src/parser.rs":"5c05f233722560fd21899e982b9defc828ba13fbe70e7cf157a439968716c6ae","src/path_segments.rs":"dd6b637245b2ad77ce96221df3f80c8b4ad858cd52aecc86b97166dec386882a","src/quirks.rs":"82903ff9c95604409cded330e36ebba0ede91c469d1aa40fb646186e2bef0769","src/slicing.rs":"25425fc5c4100a3a5da49d1e0b497f6536066afcc91c4ba4dff5ace0860acd40","tests/data.rs":"dbd795b25fe7869b242aeeb822c736167d5b4be1f6a867f563e23977d07c8fc5","tests/debugger_visualizer.rs":"d3a9466c636ee80ec9181817a8d30ea3e0cee699776ee4b805175981e4775448","tests/setters_tests.json":"486f6d129960d0d0d99b533caf9bef21113b31adcdb83296dfc4a59cd8431715","tests/unit.rs":"9707676cd80d7c8131b9c805f03b387a36ca2b1dda84a4d567788814c8afe59f","tests/urltestdata.json":"9dca78fbbcb6fceb0584f2fae7b3bb4e8a868750f2426c359c6694aed25f2682"},"package":"0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643"}
\ No newline at end of file diff --git a/vendor/url/Cargo.toml b/vendor/url/Cargo.toml index 108b1493e..4363ae58f 100644 --- a/vendor/url/Cargo.toml +++ b/vendor/url/Cargo.toml @@ -3,52 +3,81 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" +rust-version = "1.51" name = "url" -version = "2.2.2" +version = "2.3.1" authors = ["The rust-url developers"] -include = ["src/**/*", "LICENSE-*", "README.md", "tests/**"] +include = [ + "src/**/*", + "LICENSE-*", + "README.md", + "tests/**", +] description = "URL library for Rust, based on the WHATWG URL Standard" documentation = "https://docs.rs/url" -readme = "../README.md" -keywords = ["url", "parser"] -categories = ["parser-implementations", "web-programming", "encoding"] -license = "MIT/Apache-2.0" +readme = "README.md" +keywords = [ + "url", + "parser", +] +categories = [ + "parser-implementations", + "web-programming", + "encoding", +] +license = "MIT OR Apache-2.0" repository = "https://github.com/servo/rust-url" +[[test]] +name = "debugger_visualizer" +path = "tests/debugger_visualizer.rs" +test = false +required-features = ["debugger_visualizer"] + [[bench]] name = "parse_url" path = "benches/parse_url.rs" harness = false + [dependencies.form_urlencoded] -version = "1.0.0" +version = "1.1.0" [dependencies.idna] -version = "0.2.0" - -[dependencies.matches] -version = "0.1" +version = "0.3.0" [dependencies.percent-encoding] -version = "2.1.0" +version = "2.2.0" [dependencies.serde] version = "1.0" features = ["derive"] optional = true + [dev-dependencies.bencher] version = "0.1" +[dev-dependencies.debugger_test] +version = "0.1" + +[dev-dependencies.debugger_test_parser] +version = "0.1" + [dev-dependencies.serde_json] version = "1.0" + +[features] +debugger_visualizer = [] +default = [] +expose_internals = [] + [badges.appveyor] repository = "Manishearth/rust-url" diff --git a/vendor/url/LICENSE-MIT b/vendor/url/LICENSE-MIT index 24de6b418..51d5dc7eb 100644 --- a/vendor/url/LICENSE-MIT +++ b/vendor/url/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2013-2016 The rust-url developers +Copyright (c) 2013-2022 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/vendor/url/README.md b/vendor/url/README.md new file mode 100644 index 000000000..ab31bffc6 --- /dev/null +++ b/vendor/url/README.md @@ -0,0 +1,14 @@ +rust-url +======== + +[![Build status](https://github.com/servo/rust-url/workflows/CI/badge.svg)](https://github.com/servo/rust-url/actions?query=workflow%3ACI) +[![Coverage](https://codecov.io/gh/servo/rust-url/branch/master/graph/badge.svg)](https://codecov.io/gh/servo/rust-url) +[![Chat](https://img.shields.io/badge/chat-%23rust--url:mozilla.org-%2346BC99?logo=Matrix)](https://matrix.to/#/#rust-url:mozilla.org) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE-MIT) +[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE-APACHE) + +URL library for Rust, based on the [URL Standard](https://url.spec.whatwg.org/). + +[Documentation](https://docs.rs/url/) + +Please see [UPGRADING.md](https://github.com/servo/rust-url/blob/master/UPGRADING.md) if you are upgrading from a previous version. diff --git a/vendor/url/src/host.rs b/vendor/url/src/host.rs index 953743649..f1921c654 100644 --- a/vendor/url/src/host.rs +++ b/vendor/url/src/host.rs @@ -82,7 +82,9 @@ impl Host<String> { return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6); } let domain = percent_decode(input.as_bytes()).decode_utf8_lossy(); - let domain = idna::domain_to_ascii(&domain)?; + + let domain = Self::domain_to_ascii(&domain)?; + if domain.is_empty() { return Err(ParseError::EmptyHost); } @@ -90,9 +92,7 @@ impl Host<String> { let is_invalid_domain_char = |c| { matches!( c, - '\0' | '\t' - | '\n' - | '\r' + '\0'..='\u{001F}' | ' ' | '#' | '%' @@ -106,12 +106,15 @@ impl Host<String> { | '\\' | ']' | '^' + | '\u{007F}' + | '|' ) }; if domain.find(is_invalid_domain_char).is_some() { Err(ParseError::InvalidDomainCharacter) - } else if let Some(address) = parse_ipv4addr(&domain)? { + } else if ends_in_a_number(&domain) { + let address = parse_ipv4addr(&domain)?; Ok(Host::Ipv4(address)) } else { Ok(Host::Domain(domain)) @@ -145,6 +148,7 @@ impl Host<String> { | '\\' | ']' | '^' + | '|' ) }; @@ -156,6 +160,11 @@ impl Host<String> { )) } } + + /// convert domain with idna + fn domain_to_ascii(domain: &str) -> Result<String, ParseError> { + idna::domain_to_ascii(domain).map_err(Into::into) + } } impl<S: AsRef<str>> fmt::Display for Host<S> { @@ -247,8 +256,33 @@ fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) { } } +/// <https://url.spec.whatwg.org/#ends-in-a-number-checker> +fn ends_in_a_number(input: &str) -> bool { + let mut parts = input.rsplit('.'); + let last = parts.next().unwrap(); + let last = if last.is_empty() { + if let Some(last) = parts.next() { + last + } else { + return false; + } + } else { + last + }; + if !last.is_empty() && last.chars().all(|c| ('0'..='9').contains(&c)) { + return true; + } + + parse_ipv4number(last).is_ok() +} + /// <https://url.spec.whatwg.org/#ipv4-number-parser> +/// Ok(None) means the input is a valid number, but it overflows a `u32`. fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> { + if input.is_empty() { + return Err(()); + } + let mut r = 10; if input.starts_with("0x") || input.starts_with("0X") { input = &input[2..]; @@ -258,10 +292,10 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> { r = 8; } - // At the moment we can't know the reason why from_str_radix fails - // https://github.com/rust-lang/rust/issues/22639 - // So instead we check if the input looks like a real number and only return - // an error when it's an overflow. + if input.is_empty() { + return Ok(Some(0)); + } + let valid_number = match r { 8 => input.chars().all(|c| ('0'..='7').contains(&c)), 10 => input.chars().all(|c| ('0'..='9').contains(&c)), @@ -270,50 +304,34 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> { }), _ => false, }; - if !valid_number { - return Ok(None); + return Err(()); } - if input.is_empty() { - return Ok(Some(0)); - } - if input.starts_with('+') { - return Ok(None); - } match u32::from_str_radix(input, r) { - Ok(number) => Ok(Some(number)), - Err(_) => Err(()), + Ok(num) => Ok(Some(num)), + Err(_) => Ok(None), // The only possible error kind here is an integer overflow. + // The validity of the chars in the input is checked above. } } /// <https://url.spec.whatwg.org/#concept-ipv4-parser> -fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> { - if input.is_empty() { - return Ok(None); - } +fn parse_ipv4addr(input: &str) -> ParseResult<Ipv4Addr> { let mut parts: Vec<&str> = input.split('.').collect(); if parts.last() == Some(&"") { parts.pop(); } if parts.len() > 4 { - return Ok(None); + return Err(ParseError::InvalidIpv4Address); } let mut numbers: Vec<u32> = Vec::new(); - let mut overflow = false; for part in parts { - if part.is_empty() { - return Ok(None); - } match parse_ipv4number(part) { Ok(Some(n)) => numbers.push(n), - Ok(None) => return Ok(None), - Err(()) => overflow = true, + Ok(None) => return Err(ParseError::InvalidIpv4Address), // u32 overflow + Err(()) => return Err(ParseError::InvalidIpv4Address), }; } - if overflow { - return Err(ParseError::InvalidIpv4Address); - } let mut ipv4 = numbers.pop().expect("a non-empty list of numbers"); // Equivalent to: ipv4 >= 256 ** (4 − numbers.len()) if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) { @@ -325,7 +343,7 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> { for (counter, n) in numbers.iter().enumerate() { ipv4 += n << (8 * (3 - counter as u32)) } - Ok(Some(Ipv4Addr::from(ipv4))) + Ok(Ipv4Addr::from(ipv4)) } /// <https://url.spec.whatwg.org/#concept-ipv6-parser> diff --git a/vendor/url/src/lib.rs b/vendor/url/src/lib.rs index 42793cf48..6dc09d12f 100644 --- a/vendor/url/src/lib.rs +++ b/vendor/url/src/lib.rs @@ -118,12 +118,16 @@ See [serde documentation](https://serde.rs) for more information. ```toml url = { version = "2", features = ["serde"] } ``` + */ -#![doc(html_root_url = "https://docs.rs/url/2.2.2")] +#![doc(html_root_url = "https://docs.rs/url/2.3.1")] +#![cfg_attr( + feature = "debugger_visualizer", + feature(debugger_visualizer), + debugger_visualizer(natvis_file = "../../debug_metadata/url.natvis") +)] -#[macro_use] -extern crate matches; pub use form_urlencoded; #[cfg(feature = "serde")] @@ -460,7 +464,7 @@ impl Url { } // Add the filename if they are not the same - if base_filename != url_filename { + if !relative.is_empty() || base_filename != url_filename { // If the URIs filename is empty this means that it was a directory // so we'll have to append a '/'. // @@ -1234,14 +1238,9 @@ impl Url { /// # } /// # run().unwrap(); /// ``` - #[allow(clippy::manual_strip)] // introduced in 1.45, MSRV is 1.36 pub fn path_segments(&self) -> Option<str::Split<'_, char>> { let path = self.path(); - if path.starts_with('/') { - Some(path[1..].split('/')) - } else { - None - } + path.strip_prefix('/').map(|remainder| remainder.split('/')) } /// Return this URL’s query string, if any, as a percent-encoded ASCII string. @@ -1304,7 +1303,7 @@ impl Url { /// # Ok(()) /// # } /// # run().unwrap(); - /// + /// ``` #[inline] pub fn query_pairs(&self) -> form_urlencoded::Parse<'_> { @@ -1351,7 +1350,7 @@ impl Url { } fn mutate<F: FnOnce(&mut Parser<'_>) -> R, R>(&mut self, f: F) -> R { - let mut parser = Parser::for_setter(mem::replace(&mut self.serialization, String::new())); + let mut parser = Parser::for_setter(mem::take(&mut self.serialization)); let result = f(&mut parser); self.serialization = parser.serialization; result @@ -1541,6 +1540,19 @@ impl Url { /// url.set_path("data/report.csv"); /// assert_eq!(url.as_str(), "https://example.com/data/report.csv"); /// assert_eq!(url.path(), "/data/report.csv"); + /// + /// // `set_path` percent-encodes the given string if it's not already percent-encoded. + /// let mut url = Url::parse("https://example.com")?; + /// url.set_path("api/some comments"); + /// assert_eq!(url.as_str(), "https://example.com/api/some%20comments"); + /// assert_eq!(url.path(), "/api/some%20comments"); + /// + /// // `set_path` will not double percent-encode the string if it's already percent-encoded. + /// let mut url = Url::parse("https://example.com")?; + /// url.set_path("api/some%20comments"); + /// assert_eq!(url.as_str(), "https://example.com/api/some%20comments"); + /// assert_eq!(url.path(), "/api/some%20comments"); + /// /// # Ok(()) /// # } /// # run().unwrap(); @@ -1792,8 +1804,10 @@ impl Url { return Err(ParseError::SetHostOnCannotBeABaseUrl); } + let scheme_type = SchemeType::from(self.scheme()); + if let Some(host) = host { - if host.is_empty() && SchemeType::from(self.scheme()).is_special() { + if host.is_empty() && scheme_type.is_special() && !scheme_type.is_file() { return Err(ParseError::EmptyHost); } let mut host_substr = host; @@ -1817,15 +1831,20 @@ impl Url { self.set_host_internal(Host::parse_opaque(host_substr)?, None); } } else if self.has_host() { - let scheme_type = SchemeType::from(self.scheme()); - if scheme_type.is_special() { + if scheme_type.is_special() && !scheme_type.is_file() { return Err(ParseError::EmptyHost); } else if self.serialization.len() == self.path_start as usize { self.serialization.push('/'); } debug_assert!(self.byte_at(self.scheme_end) == b':'); debug_assert!(self.byte_at(self.path_start) == b'/'); - let new_path_start = self.scheme_end + 1; + + let new_path_start = if scheme_type.is_file() { + self.scheme_end + 3 + } else { + self.scheme_end + 1 + }; + self.serialization .drain(new_path_start as usize..self.path_start as usize); let offset = self.path_start - new_path_start; @@ -2127,7 +2146,7 @@ impl Url { /// /// # Examples /// - /// Change the URL’s scheme from `https` to `foo`: + /// Change the URL’s scheme from `https` to `http`: /// /// ``` /// use url::Url; @@ -2298,7 +2317,7 @@ impl Url { /// # run().unwrap(); /// # } /// ``` - #[cfg(any(unix, windows, target_os = "redox"))] + #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] #[allow(clippy::result_unit_err)] pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> { let mut serialization = "file://".to_owned(); @@ -2335,7 +2354,7 @@ impl Url { /// /// Note that `std::path` does not consider trailing slashes significant /// and usually does not include them (e.g. in `Path::parent()`). - #[cfg(any(unix, windows, target_os = "redox"))] + #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] #[allow(clippy::result_unit_err)] pub fn from_directory_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> { let mut url = Url::from_file_path(path)?; @@ -2452,7 +2471,7 @@ impl Url { /// (That is, if the percent-decoded path contains a NUL byte or, /// for a Windows path, is not UTF-8.) #[inline] - #[cfg(any(unix, windows, target_os = "redox"))] + #[cfg(any(unix, windows, target_os = "redox", target_os = "wasi"))] #[allow(clippy::result_unit_err)] pub fn to_file_path(&self) -> Result<PathBuf, ()> { if let Some(segments) = self.path_segments() { @@ -2511,7 +2530,7 @@ impl fmt::Display for Url { } } -/// String converstion. +/// String conversion. impl From<Url> for String { fn from(value: Url) -> String { value.serialization @@ -2656,12 +2675,15 @@ impl<'de> serde::Deserialize<'de> for Url { } } -#[cfg(any(unix, target_os = "redox"))] +#[cfg(any(unix, target_os = "redox", target_os = "wasi"))] fn path_to_file_url_segments( path: &Path, serialization: &mut String, ) -> Result<(u32, HostInternal), ()> { + #[cfg(any(unix, target_os = "redox"))] use std::os::unix::prelude::OsStrExt; + #[cfg(target_os = "wasi")] + use std::os::wasi::prelude::OsStrExt; if !path.is_absolute() { return Err(()); } @@ -2706,6 +2728,7 @@ fn path_to_file_url_segments_windows( let host_start = serialization.len() + 1; let host_end; let host_internal; + match components.next() { Some(Component::Prefix(ref p)) => match p.kind() { Prefix::Disk(letter) | Prefix::VerbatimDisk(letter) => { @@ -2726,7 +2749,6 @@ fn path_to_file_url_segments_windows( } _ => return Err(()), }, - _ => return Err(()), } @@ -2735,12 +2757,15 @@ fn path_to_file_url_segments_windows( if component == Component::RootDir { continue; } + path_only_has_prefix = false; // FIXME: somehow work with non-unicode? let component = component.as_os_str().to_str().ok_or(())?; + serialization.push('/'); serialization.extend(percent_encode(component.as_bytes(), PATH_SEGMENT)); } + // A windows drive letter must end with a slash. if serialization.len() > host_start && parser::is_windows_drive_letter(&serialization[host_start..]) @@ -2748,16 +2773,20 @@ fn path_to_file_url_segments_windows( { serialization.push('/'); } + Ok((host_end, host_internal)) } -#[cfg(any(unix, target_os = "redox"))] +#[cfg(any(unix, target_os = "redox", target_os = "wasi"))] fn file_url_segments_to_pathbuf( host: Option<&str>, segments: str::Split<'_, char>, ) -> Result<PathBuf, ()> { use std::ffi::OsStr; + #[cfg(any(unix, target_os = "redox"))] use std::os::unix::prelude::OsStrExt; + #[cfg(target_os = "wasi")] + use std::os::wasi::prelude::OsStrExt; if host.is_some() { return Err(()); @@ -2768,10 +2797,12 @@ fn file_url_segments_to_pathbuf( } else { Vec::new() }; + for segment in segments { bytes.push(b'/'); bytes.extend(percent_decode(segment.as_bytes())); } + // A windows drive letter must end with a slash. if bytes.len() > 2 && matches!(bytes[bytes.len() - 2], b'a'..=b'z' | b'A'..=b'Z') @@ -2779,12 +2810,15 @@ fn file_url_segments_to_pathbuf( { bytes.push(b'/'); } + let os_str = OsStr::from_bytes(&bytes); let path = PathBuf::from(os_str); + debug_assert!( path.is_absolute(), "to_file_path() failed to produce an absolute Path" ); + Ok(path) } diff --git a/vendor/url/src/origin.rs b/vendor/url/src/origin.rs index be2d948b8..81193f510 100644 --- a/vendor/url/src/origin.rs +++ b/vendor/url/src/origin.rs @@ -9,7 +9,6 @@ use crate::host::Host; use crate::parser::default_port; use crate::Url; -use idna::domain_to_unicode; use std::sync::atomic::{AtomicUsize, Ordering}; pub fn url_origin(url: &Url) -> Origin { @@ -93,7 +92,7 @@ impl Origin { Origin::Tuple(ref scheme, ref host, port) => { let host = match *host { Host::Domain(ref domain) => { - let (domain, _errors) = domain_to_unicode(domain); + let (domain, _errors) = idna::domain_to_unicode(domain); Host::Domain(domain) } _ => host.clone(), diff --git a/vendor/url/src/parser.rs b/vendor/url/src/parser.rs index 57be11052..f5438c505 100644 --- a/vendor/url/src/parser.rs +++ b/vendor/url/src/parser.rs @@ -52,15 +52,12 @@ macro_rules! simple_enum_error { /// /// This may be extended in the future so exhaustive matching is /// discouraged with an unused variant. - #[allow(clippy::manual_non_exhaustive)] // introduced in 1.40, MSRV is 1.36 #[derive(PartialEq, Eq, Clone, Copy, Debug)] + #[non_exhaustive] pub enum ParseError { $( $name, )+ - /// Unused variant enable non-exhaustive matching - #[doc(hidden)] - __FutureProof, } impl fmt::Display for ParseError { @@ -69,9 +66,6 @@ macro_rules! simple_enum_error { $( ParseError::$name => fmt.write_str($description), )+ - ParseError::__FutureProof => { - unreachable!("Don't abuse the FutureProof!"); - } } } } @@ -105,15 +99,12 @@ macro_rules! syntax_violation_enum { /// /// This may be extended in the future so exhaustive matching is /// discouraged with an unused variant. - #[allow(clippy::manual_non_exhaustive)] // introduced in 1.40, MSRV is 1.36 #[derive(PartialEq, Eq, Clone, Copy, Debug)] + #[non_exhaustive] pub enum SyntaxViolation { $( $name, )+ - /// Unused variant enable non-exhaustive matching - #[doc(hidden)] - __FutureProof, } impl SyntaxViolation { @@ -122,9 +113,6 @@ macro_rules! syntax_violation_enum { $( SyntaxViolation::$name => $description, )+ - SyntaxViolation::__FutureProof => { - unreachable!("Don't abuse the FutureProof!"); - } } } } @@ -154,7 +142,7 @@ impl fmt::Display for SyntaxViolation { } } -#[derive(Copy, Clone, PartialEq)] +#[derive(Copy, Clone, PartialEq, Eq)] pub enum SchemeType { File, SpecialNotFile, @@ -1227,13 +1215,11 @@ impl<'a> Parser<'a> { } } } - // Going from &str to String to &str to please the 1.33.0 borrow checker - let before_slash_string = if ends_with_slash { - self.serialization[segment_start..self.serialization.len() - 1].to_owned() + let segment_before_slash = if ends_with_slash { + &self.serialization[segment_start..self.serialization.len() - 1] } else { - self.serialization[segment_start..self.serialization.len()].to_owned() + &self.serialization[segment_start..self.serialization.len()] }; - let segment_before_slash: &str = &before_slash_string; match segment_before_slash { // If buffer is a double-dot path segment, shorten url’s path, ".." | "%2e%2e" | "%2e%2E" | "%2E%2e" | "%2E%2E" | "%2e." | "%2E." | ".%2e" @@ -1292,7 +1278,7 @@ impl<'a> Parser<'a> { //FIXME: log violation let path = self.serialization.split_off(path_start); self.serialization.push('/'); - self.serialization.push_str(&path.trim_start_matches('/')); + self.serialization.push_str(path.trim_start_matches('/')); } input @@ -1423,7 +1409,8 @@ impl<'a> Parser<'a> { scheme_end: u32, mut input: Input<'i>, ) -> Option<Input<'i>> { - let mut query = String::new(); // FIXME: use a streaming decoder instead + let len = input.chars.as_str().len(); + let mut query = String::with_capacity(len); // FIXME: use a streaming decoder instead let mut remaining = None; while let Some(c) = input.next() { if c == '#' && self.context == Context::UrlParser { @@ -1563,17 +1550,17 @@ fn is_normalized_windows_drive_letter(segment: &str) -> bool { is_windows_drive_letter(segment) && segment.as_bytes()[1] == b':' } -/// Wether the scheme is file:, the path has a single segment, and that segment +/// Whether the scheme is file:, the path has a single segment, and that segment /// is a Windows drive letter #[inline] pub fn is_windows_drive_letter(segment: &str) -> bool { segment.len() == 2 && starts_with_windows_drive_letter(segment) } -/// Wether path starts with a root slash +/// Whether path starts with a root slash /// and a windows drive letter eg: "/c:" or "/a:/" fn path_starts_with_windows_drive_letter(s: &str) -> bool { - if let Some(c) = s.as_bytes().get(0) { + if let Some(c) = s.as_bytes().first() { matches!(c, b'/' | b'\\' | b'?' | b'#') && starts_with_windows_drive_letter(&s[1..]) } else { false diff --git a/vendor/url/src/quirks.rs b/vendor/url/src/quirks.rs index 0dbc6eb44..0674ebb62 100644 --- a/vendor/url/src/quirks.rs +++ b/vendor/url/src/quirks.rs @@ -14,6 +14,49 @@ use crate::parser::{default_port, Context, Input, Parser, SchemeType}; use crate::{Host, ParseError, Position, Url}; +/// Internal components / offsets of a URL. +/// +/// https://user@pass:example.com:1234/foo/bar?baz#quux +/// | | | | ^^^^| | | +/// | | | | | | | `----- fragment_start +/// | | | | | | `--------- query_start +/// | | | | | `----------------- path_start +/// | | | | `--------------------- port +/// | | | `----------------------- host_end +/// | | `---------------------------------- host_start +/// | `--------------------------------------- username_end +/// `---------------------------------------------- scheme_end +#[derive(Copy, Clone)] +#[cfg(feature = "expose_internals")] +pub struct InternalComponents { + pub scheme_end: u32, + pub username_end: u32, + pub host_start: u32, + pub host_end: u32, + pub port: Option<u16>, + pub path_start: u32, + pub query_start: Option<u32>, + pub fragment_start: Option<u32>, +} + +/// Internal component / parsed offsets of the URL. +/// +/// This can be useful for implementing efficient serialization +/// for the URL. +#[cfg(feature = "expose_internals")] +pub fn internal_components(url: &Url) -> InternalComponents { + InternalComponents { + scheme_end: url.scheme_end, + username_end: url.username_end, + host_start: url.host_start, + host_end: url.host_end, + port: url.port, + path_start: url.path_start, + query_start: url.query_start, + fragment_start: url.fragment_start, + } +} + /// https://url.spec.whatwg.org/#dom-url-domaintoascii pub fn domain_to_ascii(domain: &str) -> String { match Host::parse(domain) { @@ -138,14 +181,10 @@ pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> { } } // Make sure we won't set an empty host to a url with a username or a port - if host == Host::Domain("".to_string()) { - if !username(&url).is_empty() { - return Err(()); - } else if let Some(Some(_)) = opt_port { - return Err(()); - } else if url.port().is_some() { - return Err(()); - } + if host == Host::Domain("".to_string()) + && (!username(url).is_empty() || matches!(opt_port, Some(Some(_))) || url.port().is_some()) + { + return Err(()); } url.set_host_internal(host, opt_port); Ok(()) @@ -177,10 +216,10 @@ pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> { // Empty host on special not file url if SchemeType::from(url.scheme()) == SchemeType::SpecialNotFile // Port with an empty host - ||!port(&url).is_empty() + ||!port(url).is_empty() // Empty host that includes credentials || !url.username().is_empty() - || !url.password().unwrap_or(&"").is_empty() + || !url.password().unwrap_or("").is_empty() { return Err(()); } diff --git a/vendor/url/tests/data.rs b/vendor/url/tests/data.rs index b72c33306..f4001ca2b 100644 --- a/vendor/url/tests/data.rs +++ b/vendor/url/tests/data.rs @@ -8,7 +8,6 @@ //! Data-driven tests -use std::ops::Deref; use std::str::FromStr; use serde_json::Value; @@ -16,7 +15,20 @@ use url::{quirks, Url}; #[test] fn urltestdata() { - // Copied form https://github.com/w3c/web-platform-tests/blob/master/url/ + let idna_skip_inputs = [ + "http://www.foo。bar.com", + "http://Go.com", + "http://你好你好", + "https://faß.ExAmPlE/", + "http://0Xc0.0250.01", + "ftp://%e2%98%83", + "https://%e2%98%83", + "file://a\u{ad}b/p", + "file://a%C2%ADb/p", + "http://GOO\u{200b}\u{2060}\u{feff}goo.com", + ]; + + // Copied from https://github.com/web-platform-tests/wpt/blob/master/url/ let mut json = Value::from_str(include_str!("urltestdata.json")) .expect("JSON parse error in urltestdata.json"); @@ -26,25 +38,39 @@ fn urltestdata() { continue; // ignore comments } - let base = entry.take_string("base"); + let maybe_base = entry + .take_key("base") + .expect("missing base key") + .maybe_string(); let input = entry.take_string("input"); let failure = entry.take_key("failure").is_some(); - let base = match Url::parse(&base) { - Ok(base) => base, - Err(_) if failure => continue, - Err(message) => { - eprint_failure( - format!(" failed: error parsing base {:?}: {}", base, message), - &format!("parse base for {:?}", input), - None, - ); - passed = false; + { + if idna_skip_inputs.contains(&input.as_str()) { continue; } + } + + let res = if let Some(base) = maybe_base { + let base = match Url::parse(&base) { + Ok(base) => base, + Err(_) if failure => continue, + Err(message) => { + eprint_failure( + format!(" failed: error parsing base {:?}: {}", base, message), + &format!("parse base for {:?}", input), + None, + ); + passed = false; + continue; + } + }; + base.join(&input) + } else { + Url::parse(&input) }; - let url = match (base.join(&input), failure) { + let url = match (res, failure) { (Ok(url), false) => url, (Err(_), true) => continue, (Err(message), false) => { @@ -91,7 +117,6 @@ fn urltestdata() { assert!(passed) } -#[allow(clippy::option_as_ref_deref)] // introduced in 1.40, MSRV is 1.36 #[test] fn setters_tests() { let mut json = Value::from_str(include_str!("setters_tests.json")) @@ -106,15 +131,22 @@ fn setters_tests() { let mut tests = json.take_key(attr).unwrap(); for mut test in tests.as_array_mut().unwrap().drain(..) { let comment = test.take_key("comment").map(|s| s.string()); + { + if let Some(comment) = comment.as_ref() { + if comment.starts_with("IDNA Nontransitional_Processing") { + continue; + } + } + } let href = test.take_string("href"); let new_value = test.take_string("new_value"); let name = format!("{:?}.{} = {:?}", href, attr, new_value); let mut expected = test.take_key("expected").unwrap(); let mut url = Url::parse(&href).unwrap(); - let comment_ref = comment.as_ref().map(|s| s.deref()); + let comment_ref = comment.as_deref(); passed &= check_invariants(&url, &name, comment_ref); - let _ = set(&mut url, attr, &new_value); + set(&mut url, attr, &new_value); for attr in ATTRIBS { if let Some(value) = expected.take_key(attr) { @@ -153,6 +185,7 @@ fn check_invariants(url: &Url, name: &str, comment: Option<&str>) -> bool { trait JsonExt { fn take_key(&mut self, key: &str) -> Option<Value>; fn string(self) -> String; + fn maybe_string(self) -> Option<String>; fn take_string(&mut self, key: &str) -> String; } @@ -162,10 +195,14 @@ impl JsonExt for Value { } fn string(self) -> String { - if let Value::String(s) = self { - s - } else { - panic!("Not a Value::String") + self.maybe_string().expect("") + } + + fn maybe_string(self) -> Option<String> { + match self { + Value::String(s) => Some(s), + Value::Null => None, + _ => panic!("Not a Value::String or Value::Null"), } } diff --git a/vendor/url/tests/debugger_visualizer.rs b/vendor/url/tests/debugger_visualizer.rs new file mode 100644 index 000000000..4558e0701 --- /dev/null +++ b/vendor/url/tests/debugger_visualizer.rs @@ -0,0 +1,102 @@ +use debugger_test::debugger_test; +use url::Url; + +#[inline(never)] +fn __break() {} + +#[debugger_test( + debugger = "cdb", + commands = " + .nvlist + + dx base_url + + dx url_with_non_special_scheme + + dx url_with_user_pass_port_query_fragments + + dx url_blob + + dx url_with_base + + dx url_with_base_replaced + + dx url_with_comma", + expected_statements = r#" + pattern:debugger_visualizer-.*\.exe \(embedded NatVis ".*-[0-9]+\.natvis"\) + + base_url : "http://example.org/foo/bar" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "http" + [host] : "example.org" + [path] : "/foo/bar" + + url_with_non_special_scheme : "non-special://test/x" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "non-special" + [host] : "test" + [path] : "/x" + + url_with_user_pass_port_query_fragments : "http://user:pass@foo:21/bar;par?b#c" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "http" + [username] : "user" + [host] : "foo" + [port] : 21 + [path] : "/bar;par" + [query] : "b" + [fragment] : "c" + + url_blob : "blob:https://example.com:443/" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "blob" + [path] : "https://example.com:443/" + + url_with_base : "http://example.org/a%2fc" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "http" + [host] : "example.org" + [path] : "/a%2fc" + + url_with_base_replaced : "http://[::7f00:1]/" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "http" + [host] : "[::7f00:1]" + [path] : "/" + + url_with_comma : "data:text/html,test#test" [Type: url::Url] + [<Raw View>] [Type: url::Url] + [scheme] : "data" + [path] : "text/html,test" + [fragment] : "test" + "# +)] +fn test_url_visualizer() { + // Copied from https://github.com/web-platform-tests/wpt/blob/master/url/ + let base_url = Url::parse("http://example.org/foo/bar").unwrap(); + assert_eq!(base_url.as_str(), "http://example.org/foo/bar"); + + let url_with_non_special_scheme = Url::parse("non-special://:@test/x").unwrap(); + assert_eq!(url_with_non_special_scheme.as_str(), "non-special://test/x"); + + let url_with_user_pass_port_query_fragments = + Url::parse("http://user:pass@foo:21/bar;par?b#c").unwrap(); + assert_eq!( + url_with_user_pass_port_query_fragments.as_str(), + "http://user:pass@foo:21/bar;par?b#c" + ); + + let url_blob = Url::parse("blob:https://example.com:443/").unwrap(); + assert_eq!(url_blob.as_str(), "blob:https://example.com:443/"); + + let url_with_base = base_url.join("/a%2fc").unwrap(); + assert_eq!(url_with_base.as_str(), "http://example.org/a%2fc"); + + let url_with_base_replaced = base_url.join("http://[::127.0.0.1]").unwrap(); + assert_eq!(url_with_base_replaced.as_str(), "http://[::7f00:1]/"); + + let url_with_comma = base_url.join("data:text/html,test#test").unwrap(); + assert_eq!(url_with_comma.as_str(), "data:text/html,test#test"); + + __break(); +} diff --git a/vendor/url/tests/unit.rs b/vendor/url/tests/unit.rs index 13055a473..55ff59ada 100644 --- a/vendor/url/tests/unit.rs +++ b/vendor/url/tests/unit.rs @@ -43,6 +43,14 @@ fn test_set_empty_host() { assert_eq!(base.as_str(), "moz:/baz"); base.set_host(Some("servo")).unwrap(); assert_eq!(base.as_str(), "moz://servo/baz"); + + let mut base: Url = "file://server/share/foo/bar".parse().unwrap(); + base.set_host(None).unwrap(); + assert_eq!(base.as_str(), "file:///share/foo/bar"); + + let mut base: Url = "file://server/share/foo/bar".parse().unwrap(); + base.set_host(Some("foo")).unwrap(); + assert_eq!(base.as_str(), "file://foo/share/foo/bar"); } #[test] @@ -256,7 +264,6 @@ fn host() { 0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344, )), ); - assert_host("http://1.35.+33.49", Host::Domain("1.35.+33.49")); assert_host( "http://[::]", Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)), @@ -271,7 +278,8 @@ fn host() { ); assert_host("http://0x1232131", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49))); assert_host("http://111", Host::Ipv4(Ipv4Addr::new(0, 0, 0, 111))); - assert_host("http://2..2.3", Host::Domain("2..2.3")); + assert!(Url::parse("http://1.35.+33.49").is_err()); + assert!(Url::parse("http://2..2.3").is_err()); assert!(Url::parse("http://42.0x1232131").is_err()); assert!(Url::parse("http://192.168.0.257").is_err()); @@ -720,6 +728,34 @@ fn test_domain_encoding_quirks() { } } +#[cfg(feature = "expose_internals")] +#[test] +fn test_expose_internals() { + use url::quirks::internal_components; + use url::quirks::InternalComponents; + + let url = Url::parse("https://example.com/path/file.ext?key=val&key2=val2#fragment").unwrap(); + let InternalComponents { + scheme_end, + username_end, + host_start, + host_end, + port, + path_start, + query_start, + fragment_start, + } = internal_components(&url); + + assert_eq!(scheme_end, 5); + assert_eq!(username_end, 8); + assert_eq!(host_start, 8); + assert_eq!(host_end, 19); + assert_eq!(port, None); + assert_eq!(path_start, 19); + assert_eq!(query_start, Some(33)); + assert_eq!(fragment_start, Some(51)); +} + #[test] fn test_windows_unc_path() { if !cfg!(windows) { @@ -796,7 +832,7 @@ fn test_syntax_violation_callback_types() { ("file:/foo.txt", ExpectedFileDoubleSlash, "expected // after file:"), ("file://mozilla.org/c:/file.txt", FileWithHostAndWindowsDrive, "file: with host and Windows drive letter"), ("http://mozilla.org/^", NonUrlCodePoint, "non-URL code point"), - ("http://mozilla.org/#\00", NullInFragment, "NULL characters are ignored in URL fragment identifiers"), + ("http://mozilla.org/#\x000", NullInFragment, "NULL characters are ignored in URL fragment identifiers"), ("http://mozilla.org/%1", PercentDecode, "expected 2 hex digits after %"), ("http://mozilla.org\t/foo", TabOrNewlineIgnored, "tabs or newlines are ignored in URLs"), ("http://user@:pass@mozilla.org", UnencodedAtSign, "unencoded @ sign in username or password") @@ -1081,6 +1117,16 @@ fn test_make_relative() { "http://127.0.0.1:8080/test/video?baz=meh#456", "video?baz=meh#456", ), + ( + "http://127.0.0.1:8080/file.txt", + "http://127.0.0.1:8080/test/file.txt", + "test/file.txt", + ), + ( + "http://127.0.0.1:8080/not_equal.txt", + "http://127.0.0.1:8080/test/file.txt", + "test/file.txt", + ), ]; for (base, uri, relative) in &tests { @@ -1093,7 +1139,7 @@ fn test_make_relative() { base, uri, relative ); assert_eq!( - base_uri.join(&relative).unwrap().as_str(), + base_uri.join(relative).unwrap().as_str(), *uri, "base: {}, uri: {}, relative: {}", base, diff --git a/vendor/url/tests/urltestdata.json b/vendor/url/tests/urltestdata.json index 554e61914..4265f5928 100644 --- a/vendor/url/tests/urltestdata.json +++ b/vendor/url/tests/urltestdata.json @@ -1,6 +1,6 @@ [ "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/script-tests/segments.js", - "# AS OF https://github.com/jsdom/whatwg-url/commit/35f04dfd3048cf6362f4398745bb13375c5020c2", + "# AS OF https://github.com/web-platform-tests/wpt/commit/2a64dae4641fbd61bd4257df460e188f425b492e", { "input": "http://example\t.\norg", "base": "http://example.org/foo/bar", @@ -541,6 +541,36 @@ "hash": "" }, { + "input": "\\x", + "base": "http://example.org/foo/bar", + "href": "http://example.org/x", + "origin": "http://example.org", + "protocol": "http:", + "username": "", + "password": "", + "host": "example.org", + "hostname": "example.org", + "port": "", + "pathname": "/x", + "search": "", + "hash": "" + }, + { + "input": "\\\\x\\hello", + "base": "http://example.org/foo/bar", + "href": "http://x/hello", + "origin": "http://x", + "protocol": "http:", + "username": "", + "password": "", + "host": "x", + "hostname": "x", + "port": "", + "pathname": "/hello", + "search": "", + "hash": "" + }, + { "input": "::", "base": "http://example.org/foo/bar", "href": "http://example.org/foo/::", @@ -3157,7 +3187,8 @@ { "input": "http:/:@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http://user@/www.example.com", @@ -3167,12 +3198,14 @@ { "input": "http:@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http:/@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http://@/www.example.com", @@ -3182,17 +3215,20 @@ { "input": "https:@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http:a:b@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http:/a:b@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http://a:b@/www.example.com", @@ -3202,7 +3238,8 @@ { "input": "http::@/www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http:a:@www.example.com", @@ -3267,12 +3304,14 @@ { "input": "http:@:www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http:/@:www.example.com", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "http://@:www.example.com", @@ -3646,6 +3685,38 @@ "search": "?%EF%BF%BD", "hash": "#%EF%BF%BD" }, + "Domain is ASCII, but a label is invalid IDNA", + { + "input": "http://a.b.c.xn--pokxncvks", + "base": "about:blank", + "failure": true + }, + { + "input": "http://10.0.0.xn--pokxncvks", + "base": "about:blank", + "failure": true + }, + "IDNA labels should be matched case-insensitively", + { + "input": "http://a.b.c.XN--pokxncvks", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a.b.c.Xn--pokxncvks", + "base": "about:blank", + "failure": true + }, + { + "input": "http://10.0.0.XN--pokxncvks", + "base": "about:blank", + "failure": true + }, + { + "input": "http://10.0.0.xN--pokxncvks", + "base": "about:blank", + "failure": true + }, "Test name prepping, fullwidth input should be converted to ASCII and NOT IDN-ized. This is 'Go' in fullwidth UTF-8/UTF-16.", { "input": "http://Go.com", @@ -3847,21 +3918,6 @@ "search": "", "hash": "" }, - { - "input": "http://0..0x300/", - "base": "about:blank", - "href": "http://0..0x300/", - "origin": "http://0..0x300", - "protocol": "http:", - "username": "", - "password": "", - "host": "0..0x300", - "hostname": "0..0x300", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, "Broken IPv6", { "input": "http://[www.google.com]/", @@ -4483,16 +4539,6 @@ "hash": "" }, { - "input": "sc://\u0000/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc:// /", - "base": "about:blank", - "failure": true - }, - { "input": "sc://%/", "base": "about:blank", "href": "sc://%/", @@ -4527,21 +4573,6 @@ "failure": true }, { - "input": "sc://[/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://\\/", - "base": "about:blank", - "failure": true - }, - { - "input": "sc://]/", - "base": "about:blank", - "failure": true - }, - { "input": "x", "base": "sc://ñ", "href": "sc://%C3%B1/x", @@ -4619,7 +4650,7 @@ "search": "", "hash": "" }, - "# unknown scheme with non-URL characters in the path", + "# unknown scheme with non-URL characters", { "input": "wow:\uFFFF", "base": "about:blank", @@ -4637,6 +4668,250 @@ }, "Forbidden host code points", { + "input": "sc://a\u0000b/", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a b/", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a<b", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a>b", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a[b/", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a\\b/", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a]b/", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a^b", + "base": "about:blank", + "failure": true + }, + { + "input": "sc://a|b/", + "base": "about:blank", + "failure": true + }, + "Forbidden host codepoints: tabs and newlines are removed during preprocessing", + { + "input": "foo://ho\u0009st/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "foo://ho\u000Ast/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "foo://ho\u000Dst/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"foo://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + "Forbidden domain code-points", + { + "input": "http://a\u0000b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0001b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0002b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0003b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0004b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0005b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0006b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0007b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0008b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u000Bb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u000Cb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u000Eb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u000Fb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0010b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0011b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0012b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0013b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0014b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0015b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0016b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0017b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0018b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u0019b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Ab/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Bb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Cb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Db/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Eb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u001Fb/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a%b/", + "base": "about:blank", + "failure": true + }, + { "input": "http://a<b", "base": "about:blank", "failure": true @@ -4647,51 +4922,330 @@ "failure": true }, { + "input": "http://a[b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a]b/", + "base": "about:blank", + "failure": true + }, + { "input": "http://a^b", "base": "about:blank", "failure": true }, { - "input": "non-special://a<b", + "input": "http://a|b/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://a\u007Fb/", + "base": "about:blank", + "failure": true + }, + "Forbidden domain codepoints: tabs and newlines are removed during preprocessing", + { + "input": "http://ho\u0009st/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"http://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "http:", + "search": "", + "username": "" + }, + { + "input": "http://ho\u000Ast/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"http://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "http:", + "search": "", + "username": "" + }, + { + "input": "http://ho\u000Dst/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href":"http://host/", + "password": "", + "pathname": "/", + "port":"", + "protocol": "http:", + "search": "", + "username": "" + }, + "Encoded forbidden domain codepoints in special URLs", + { + "input": "http://ho%00st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%01st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%02st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%03st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%04st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%05st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%06st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%07st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%08st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%09st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Ast/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Bst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Cst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Dst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Est/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%0Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%10st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%11st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%12st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%13st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%14st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%15st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%16st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%17st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%18st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%19st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Ast/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Bst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Cst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Dst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Est/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%1Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%20st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%23st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%25st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%2Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Ast/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Cst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Est/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%3Fst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%40st/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%5Bst/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://ho%5Cst/", "base": "about:blank", "failure": true }, { - "input": "non-special://a>b", + "input": "http://ho%5Dst/", "base": "about:blank", "failure": true }, { - "input": "non-special://a^b", + "input": "http://ho%7Cst/", "base": "about:blank", "failure": true }, - "Allowed host code points", { - "input": "http://\u001F!\"$&'()*+,-.;=_`{|}~/", + "input": "http://ho%7Fst/", "base": "about:blank", - "href": "http://\u001F!\"$&'()*+,-.;=_`{|}~/", - "origin": "http://\u001F!\"$&'()*+,-.;=_`{|}~", + "failure": true + }, + "Allowed host/domain code points", + { + "input": "http://!\"$&'()*+,-.;=_`{}~/", + "base": "about:blank", + "href": "http://!\"$&'()*+,-.;=_`{}~/", + "origin": "http://!\"$&'()*+,-.;=_`{}~", "protocol": "http:", "username": "", "password": "", - "host": "\u001F!\"$&'()*+,-.;=_`{|}~", - "hostname": "\u001F!\"$&'()*+,-.;=_`{|}~", + "host": "!\"$&'()*+,-.;=_`{}~", + "hostname": "!\"$&'()*+,-.;=_`{}~", "port": "", "pathname": "/", "search": "", "hash": "" }, { - "input": "sc://\u001F!\"$&'()*+,-.;=_`{|}~/", + "input": "sc://\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u000B\u000C\u000E\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001A\u001B\u001C\u001D\u001E\u001F\u007F!\"$%&'()*+,-.;=_`{}~/", "base": "about:blank", - "href": "sc://%1F!\"$&'()*+,-.;=_`{|}~/", + "href": "sc://%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~/", "origin": "null", "protocol": "sc:", "username": "", "password": "", - "host": "%1F!\"$&'()*+,-.;=_`{|}~", - "hostname": "%1F!\"$&'()*+,-.;=_`{|}~", + "host": "%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~", + "hostname": "%01%02%03%04%05%06%07%08%0B%0C%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%7F!\"$%&'()*+,-.;=_`{}~", "port": "", "pathname": "/", "search": "", @@ -4964,6 +5518,36 @@ }, "# IPv4 parsing (via https://github.com/nodejs/node/pull/10317)", { + "input": "http://1.2.3.4/", + "base": "http://other.com/", + "href": "http://1.2.3.4/", + "origin": "http://1.2.3.4", + "protocol": "http:", + "username": "", + "password": "", + "host": "1.2.3.4", + "hostname": "1.2.3.4", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { + "input": "http://1.2.3.4./", + "base": "http://other.com/", + "href": "http://1.2.3.4/", + "origin": "http://1.2.3.4", + "protocol": "http:", + "username": "", + "password": "", + "host": "1.2.3.4", + "hostname": "1.2.3.4", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { "input": "http://192.168.257", "base": "http://other.com/", "href": "http://192.168.1.1/", @@ -4979,6 +5563,21 @@ "hash": "" }, { + "input": "http://192.168.257.", + "base": "http://other.com/", + "href": "http://192.168.1.1/", + "origin": "http://192.168.1.1", + "protocol": "http:", + "username": "", + "password": "", + "host": "192.168.1.1", + "hostname": "192.168.1.1", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { "input": "http://192.168.257.com", "base": "http://other.com/", "href": "http://192.168.257.com/", @@ -5039,6 +5638,21 @@ "hash": "" }, { + "input": "http://999999999.", + "base": "http://other.com/", + "href": "http://59.154.201.255/", + "origin": "http://59.154.201.255", + "protocol": "http:", + "username": "", + "password": "", + "host": "59.154.201.255", + "hostname": "59.154.201.255", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { "input": "http://999999999.com", "base": "http://other.com/", "href": "http://999999999.com/", @@ -5119,21 +5733,6 @@ "failure": true }, { - "input": "http://256.256.256.256.256", - "base": "http://other.com/", - "href": "http://256.256.256.256.256/", - "origin": "http://256.256.256.256.256", - "protocol": "http:", - "username": "", - "password": "", - "host": "256.256.256.256.256", - "hostname": "256.256.256.256.256", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { "input": "https://0x.0x.0", "base": "about:blank", "href": "https://0.0.0.0/", @@ -5188,6 +5787,56 @@ "search": "", "hash": "" }, + { + "input": "file://%43%3A", + "base": "about:blank", + "failure": true + }, + { + "input": "file://%43%7C", + "base": "about:blank", + "failure": true + }, + { + "input": "file://%43|", + "base": "about:blank", + "failure": true + }, + { + "input": "file://C%7C", + "base": "about:blank", + "failure": true + }, + { + "input": "file://%43%7C/", + "base": "about:blank", + "failure": true + }, + { + "input": "https://%43%7C/", + "base": "about:blank", + "failure": true + }, + { + "input": "asdf://%43|/", + "base": "about:blank", + "failure": true + }, + { + "input": "asdf://%43%7C/", + "base": "about:blank", + "href": "asdf://%43%7C/", + "origin": "null", + "protocol": "asdf:", + "username": "", + "password": "", + "host": "%43%7C", + "hostname": "%43%7C", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, "# file URLs relative to other file URLs (via https://github.com/jsdom/whatwg-url/pull/60)", { "input": "pix/submit.gif", @@ -5247,6 +5896,20 @@ "hash": "" }, { + "input": "/", + "base": "file://h/a/b", + "href": "file://h/", + "protocol": "file:", + "username": "", + "password": "", + "host": "h", + "hostname": "h", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { "input": "//d:", "base": "file:///C:/a/b", "href": "file:///d:", @@ -5388,90 +6051,6 @@ }, "# File URLs and many (back)slashes", { - "input": "file:\\\\//", - "base": "about:blank", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\\\\\", - "base": "about:blank", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\\\\\?fox", - "base": "about:blank", - "href": "file:///?fox", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "?fox", - "hash": "" - }, - { - "input": "file:\\\\\\\\#guppy", - "base": "about:blank", - "href": "file:///#guppy", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "#guppy" - }, - { - "input": "file://spider///", - "base": "about:blank", - "href": "file://spider/", - "protocol": "file:", - "username": "", - "password": "", - "host": "spider", - "hostname": "spider", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "file:\\\\localhost//", - "base": "about:blank", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { "input": "file:///localhost//cat", "base": "about:blank", "href": "file:///localhost//cat", @@ -5486,48 +6065,6 @@ "hash": "" }, { - "input": "file://\\/localhost//cat", - "base": "about:blank", - "href": "file:///localhost//cat", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/localhost//cat", - "search": "", - "hash": "" - }, - { - "input": "file://localhost//a//../..//", - "base": "about:blank", - "href": "file:///", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/", - "search": "", - "hash": "" - }, - { - "input": "/////mouse", - "base": "file:///elephant", - "href": "file:///mouse", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/mouse", - "search": "", - "hash": "" - }, - { "input": "\\//pig", "base": "file://lion/", "href": "file:///pig", @@ -5542,48 +6079,6 @@ "hash": "" }, { - "input": "\\/localhost//pig", - "base": "file://lion/", - "href": "file:///pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pig", - "search": "", - "hash": "" - }, - { - "input": "//localhost//pig", - "base": "file://lion/", - "href": "file:///pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/pig", - "search": "", - "hash": "" - }, - { - "input": "/..//localhost//pig", - "base": "file://lion/", - "href": "file://lion/localhost//pig", - "protocol": "file:", - "username": "", - "password": "", - "host": "lion", - "hostname": "lion", - "port": "", - "pathname": "/localhost//pig", - "search": "", - "hash": "" - }, - { "input": "file://", "base": "file://ape/", "href": "file:///", @@ -5628,90 +6123,6 @@ }, "# Windows drive letter handling with the 'file:' base URL", { - "input": "C|", - "base": "file://host/dir/file", - "href": "file:///C:", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|#", - "base": "file://host/dir/file", - "href": "file:///C:#", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|?", - "base": "file://host/dir/file", - "href": "file:///C:?", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:", - "search": "", - "hash": "" - }, - { - "input": "C|/", - "base": "file://host/dir/file", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "C|\n/", - "base": "file://host/dir/file", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { - "input": "C|\\", - "base": "file://host/dir/file", - "href": "file:///C:/", - "protocol": "file:", - "username": "", - "password": "", - "host": "", - "hostname": "", - "port": "", - "pathname": "/C:/", - "search": "", - "hash": "" - }, - { "input": "C", "base": "file://host/dir/file", "href": "file://host/dir/C", @@ -5782,24 +6193,24 @@ "search": "", "hash": "" }, + "# Copy the empty host from the input in the following cases", { - "input": "/c:/foo/bar", - "base": "file://host/path", - "href": "file:///c:/foo/bar", + "input": "//C:/", + "base": "file://host/", + "href": "file:///C:/", "protocol": "file:", "username": "", "password": "", "host": "", "hostname": "", "port": "", - "pathname": "/c:/foo/bar", + "pathname": "/C:/", "search": "", "hash": "" }, - "# Windows drive letter quirk with not empty host", { - "input": "file://example.net/C:/", - "base": "about:blank", + "input": "file://C:/", + "base": "file://host/", "href": "file:///C:/", "protocol": "file:", "username": "", @@ -5812,8 +6223,8 @@ "hash": "" }, { - "input": "file://1.2.3.4/C:/", - "base": "about:blank", + "input": "///C:/", + "base": "file://host/", "href": "file:///C:/", "protocol": "file:", "username": "", @@ -5826,8 +6237,8 @@ "hash": "" }, { - "input": "file://[1::8]/C:/", - "base": "about:blank", + "input": "file:///C:/", + "base": "file://host/", "href": "file:///C:/", "protocol": "file:", "username": "", @@ -5971,7 +6382,8 @@ { "input": "\\\\\\.\\Y:", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, "# file: drive letter cases from https://crbug.com/1078698 but lowercased", { @@ -6033,7 +6445,51 @@ { "input": "\\\\\\.\\y:", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true + }, + "# Additional file URL tests for (https://github.com/whatwg/url/issues/405)", + { + "input": "file:///one/two", + "base": "file:///", + "href": "file:///one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/one/two", + "search": "", + "hash": "" + }, + { + "input": "//one/two", + "base": "file:///", + "href": "file://one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "one", + "hostname": "one", + "port": "", + "pathname": "/two", + "search": "", + "hash": "" + }, + { + "input": "///one/two", + "base": "file:///", + "href": "file:///one/two", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/one/two", + "search": "", + "hash": "" }, "# IPv6 tests", { @@ -6527,6 +6983,7 @@ "input": "blob:https://example.com:443/", "base": "about:blank", "href": "blob:https://example.com:443/", + "origin": "https://example.com", "protocol": "blob:", "username": "", "password": "", @@ -6541,6 +6998,7 @@ "input": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", "base": "about:blank", "href": "blob:d3958f5c-0777-0845-9dcf-2cb28783acaf", + "origin": "null", "protocol": "blob:", "username": "", "password": "", @@ -6551,21 +7009,22 @@ "search": "", "hash": "" }, - "Invalid IPv4 radix digits", { - "input": "http://0177.0.0.0189", + "input": "blob:", "base": "about:blank", - "href": "http://0177.0.0.0189/", - "protocol": "http:", + "href": "blob:", + "origin": "null", + "protocol": "blob:", "username": "", "password": "", - "host": "0177.0.0.0189", - "hostname": "0177.0.0.0189", + "host": "", + "hostname": "", "port": "", - "pathname": "/", + "pathname": "", "search": "", "hash": "" }, + "Invalid IPv4 radix digits", { "input": "http://0x7f.0.0.0x7g", "base": "about:blank", @@ -6760,17 +7219,20 @@ { "input": "a", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "a/", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, { "input": "a//", "base": "about:blank", - "failure": true + "failure": true, + "inputCanBeRelative": true }, "Bases that don't fail to parse but fail to be bases", { @@ -6987,5 +7449,460 @@ "pathname": "/", "search": "", "hash": "#link" + }, + "UTF-8 percent-encode of C0 control percent-encode set and supersets", + { + "input": "non-special:cannot-be-a-base-url-\u0000\u0001\u001F\u001E\u007E\u007F\u0080", + "base": "about:blank", + "hash": "", + "host": "", + "hostname": "", + "href": "non-special:cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80", + "origin": "null", + "password": "", + "pathname": "cannot-be-a-base-url-%00%01%1F%1E~%7F%C2%80", + "port": "", + "protocol": "non-special:", + "search": "", + "username": "" + }, + { + "input": "https://www.example.com/path{\u007Fpath.html?query'\u007F=query#fragment<\u007Ffragment", + "base": "about:blank", + "hash": "#fragment%3C%7Ffragment", + "host": "www.example.com", + "hostname": "www.example.com", + "href": "https://www.example.com/path%7B%7Fpath.html?query%27%7F=query#fragment%3C%7Ffragment", + "origin": "https://www.example.com", + "password": "", + "pathname": "/path%7B%7Fpath.html", + "port": "", + "protocol": "https:", + "search": "?query%27%7F=query", + "username": "" + }, + { + "input": "https://user:pass[\u007F@foo/bar", + "base": "http://example.org", + "hash": "", + "host": "foo", + "hostname": "foo", + "href": "https://user:pass%5B%7F@foo/bar", + "origin": "https://foo", + "password": "pass%5B%7F", + "pathname": "/bar", + "port": "", + "protocol": "https:", + "search": "", + "username": "user" + }, + "Tests for the distinct percent-encode sets", + { + "input": "foo:// !\"$%&'()*+,-.;<=>@[\\]^_`{|}~@host/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "foo://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/", + "origin": "null", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~" + }, + { + "input": "wss:// !\"$%&'()*+,-.;<=>@[]^_`{|}~@host/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "wss://%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/", + "origin": "wss://host", + "password": "", + "pathname": "/", + "port":"", + "protocol": "wss:", + "search": "", + "username": "%20!%22$%&'()*+,-.%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~" + }, + { + "input": "foo://joe: !\"$%&'()*+,-.:;<=>@[\\]^_`{|}~@host/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "foo://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~@host/", + "origin": "null", + "password": "%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5C%5D%5E_%60%7B%7C%7D~", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "joe" + }, + { + "input": "wss://joe: !\"$%&'()*+,-.:;<=>@[]^_`{|}~@host/", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "wss://joe:%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~@host/", + "origin": "wss://host", + "password": "%20!%22$%&'()*+,-.%3A%3B%3C%3D%3E%40%5B%5D%5E_%60%7B%7C%7D~", + "pathname": "/", + "port":"", + "protocol": "wss:", + "search": "", + "username": "joe" + }, + { + "input": "foo://!\"$%&'()*+,-.;=_`{}~/", + "base": "about:blank", + "hash": "", + "host": "!\"$%&'()*+,-.;=_`{}~", + "hostname": "!\"$%&'()*+,-.;=_`{}~", + "href":"foo://!\"$%&'()*+,-.;=_`{}~/", + "origin": "null", + "password": "", + "pathname": "/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "wss://!\"$&'()*+,-.;=_`{}~/", + "base": "about:blank", + "hash": "", + "host": "!\"$&'()*+,-.;=_`{}~", + "hostname": "!\"$&'()*+,-.;=_`{}~", + "href":"wss://!\"$&'()*+,-.;=_`{}~/", + "origin": "wss://!\"$&'()*+,-.;=_`{}~", + "password": "", + "pathname": "/", + "port":"", + "protocol": "wss:", + "search": "", + "username": "" + }, + { + "input": "foo://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "foo://host/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~", + "origin": "null", + "password": "", + "pathname": "/%20!%22$%&'()*+,-./:;%3C=%3E@[\\]^_%60%7B|%7D~", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "wss://host/ !\"$%&'()*+,-./:;<=>@[\\]^_`{|}~", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "wss://host/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~", + "origin": "wss://host", + "password": "", + "pathname": "/%20!%22$%&'()*+,-./:;%3C=%3E@[/]^_%60%7B|%7D~", + "port":"", + "protocol": "wss:", + "search": "", + "username": "" + }, + { + "input": "foo://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "foo://host/dir/?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~", + "origin": "null", + "password": "", + "pathname": "/dir/", + "port":"", + "protocol": "foo:", + "search": "?%20!%22$%&'()*+,-./:;%3C=%3E?@[\\]^_`{|}~", + "username": "" + }, + { + "input": "wss://host/dir/? !\"$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "base": "about:blank", + "hash": "", + "host": "host", + "hostname": "host", + "href": "wss://host/dir/?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~", + "origin": "wss://host", + "password": "", + "pathname": "/dir/", + "port":"", + "protocol": "wss:", + "search": "?%20!%22$%&%27()*+,-./:;%3C=%3E?@[\\]^_`{|}~", + "username": "" + }, + { + "input": "foo://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "base": "about:blank", + "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", + "host": "host", + "hostname": "host", + "href": "foo://host/dir/#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", + "origin": "null", + "password": "", + "pathname": "/dir/", + "port":"", + "protocol": "foo:", + "search": "", + "username": "" + }, + { + "input": "wss://host/dir/# !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "base": "about:blank", + "hash": "#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", + "host": "host", + "hostname": "host", + "href": "wss://host/dir/#%20!%22#$%&'()*+,-./:;%3C=%3E?@[\\]^_%60{|}~", + "origin": "wss://host", + "password": "", + "pathname": "/dir/", + "port":"", + "protocol": "wss:", + "search": "", + "username": "" + }, + "Ensure that input schemes are not ignored when resolving non-special URLs", + { + "input": "abc:rootless", + "base": "abc://host/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:rootless", + "base": "abc:/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:rootless", + "base": "abc:path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:rootless", + "password": "", + "pathname": "rootless", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + { + "input": "abc:/rooted", + "base": "abc://host/path", + "hash": "", + "host": "", + "hostname": "", + "href":"abc:/rooted", + "password": "", + "pathname": "/rooted", + "port":"", + "protocol": "abc:", + "search": "", + "username": "" + }, + "Empty query and fragment with blank should throw an error", + { + "input": "#", + "base": null, + "failure": true + }, + { + "input": "?", + "base": null, + "failure": true + }, + "Last component looks like a number, but not valid IPv4", + { + "input": "http://1.2.3.4.5", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://1.2.3.4.5.", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://0..0x300/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://0..0x300./", + "base": "about:blank", + "failure": true + }, + { + "input": "http://256.256.256.256.256", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://256.256.256.256.256.", + "base": "http://other.com/", + "failure": true + }, + { + "input": "http://1.2.3.08", + "base": "about:blank", + "failure": true + }, + { + "input": "http://1.2.3.08.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://1.2.3.09", + "base": "about:blank", + "failure": true + }, + { + "input": "http://09.2.3.4", + "base": "about:blank", + "failure": true + }, + { + "input": "http://09.2.3.4.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://01.2.3.4.5", + "base": "about:blank", + "failure": true + }, + { + "input": "http://01.2.3.4.5.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://0x100.2.3.4", + "base": "about:blank", + "failure": true + }, + { + "input": "http://0x100.2.3.4.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://0x1.2.3.4.5", + "base": "about:blank", + "failure": true + }, + { + "input": "http://0x1.2.3.4.5.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.1.2.3.4", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.1.2.3.4.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.2.3.4", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.2.3.4.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.09", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.09.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.0x4", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.0x4.", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.09..", + "base": "about:blank", + "hash": "", + "host": "foo.09..", + "hostname": "foo.09..", + "href":"http://foo.09../", + "password": "", + "pathname": "/", + "port":"", + "protocol": "http:", + "search": "", + "username": "" + }, + { + "input": "http://0999999999999999999/", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.0x", + "base": "about:blank", + "failure": true + }, + { + "input": "http://foo.0XFfFfFfFfFfFfFfFfFfAcE123", + "base": "about:blank", + "failure": true + }, + { + "input": "http://💩.123/", + "base": "about:blank", + "failure": true } ] |