Bug 1744668 - Update url crate to v2.2.2. r=markh

The upcoming update of viaduct wants at least v2.2.

Use Into<String> instead of Url::into_string, the latter is deprecated
and causes a warning we turn into an error.

Differential Revision: https://phabricator.services.mozilla.com/D133024
This commit is contained in:
Mike Hommey 2021-12-07 21:25:29 +00:00
Родитель ff66cb8dea
Коммит dacc08f158
25 изменённых файлов: 3531 добавлений и 1143 удалений

15
Cargo.lock сгенерированный
Просмотреть файл

@ -1624,6 +1624,16 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
dependencies = [
"matches",
"percent-encoding",
]
[[package]]
name = "freetype"
version = "0.7.0"
@ -5325,10 +5335,11 @@ checksum = "2ace0b4755d0a2959962769239d56267f8a024fef2d9b32666b3dcd0946b0906"
[[package]]
name = "url"
version = "2.1.0"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
dependencies = [
"form_urlencoded",
"idna",
"matches",
"percent-encoding",

1
third_party/rust/form_urlencoded/.cargo-checksum.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
{"files":{"Cargo.toml":"aadc4e4ba33e86861d8d1d8b848ac11a27b6f87340d082b47f762387464c61ed","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e","src/lib.rs":"5d30edec687843447c97e4ea87583983eb9fc06135ae718c8ecc0fa8cebef2df"},"package":"5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"}

28
third_party/rust/form_urlencoded/Cargo.toml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# 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
#
# 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)
[package]
edition = "2018"
name = "form_urlencoded"
version = "1.0.1"
authors = ["The rust-url developers"]
description = "Parser and serializer for the application/x-www-form-urlencoded syntax, as used by HTML forms."
license = "MIT/Apache-2.0"
repository = "https://github.com/servo/rust-url"
[lib]
test = false
[dependencies.matches]
version = "0.1"
[dependencies.percent-encoding]
version = "2.1.0"

201
third_party/rust/form_urlencoded/LICENSE-APACHE поставляемый Normal file
Просмотреть файл

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

25
third_party/rust/form_urlencoded/LICENSE-MIT поставляемый Normal file
Просмотреть файл

@ -0,0 +1,25 @@
Copyright (c) 2013-2016 The rust-url developers
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -13,8 +13,10 @@
//! Converts between a string (such as an URLs query string)
//! and a sequence of (name, value) pairs.
#[macro_use]
extern crate matches;
use percent_encoding::{percent_decode, percent_encode_byte};
use query_encoding::{self, decode_utf8_lossy, EncodingOverride};
use std::borrow::{Borrow, Cow};
use std::str;
@ -26,7 +28,7 @@ use std::str;
/// The names and values are percent-decoded. For instance, `%23first=%25try%25` will be
/// converted to `[("#first", "%try%")]`.
#[inline]
pub fn parse(input: &[u8]) -> Parse {
pub fn parse(input: &[u8]) -> Parse<'_> {
Parse { input }
}
/// The return type of `parse()`.
@ -57,7 +59,7 @@ impl<'a> Iterator for Parse<'a> {
}
}
fn decode(input: &[u8]) -> Cow<str> {
fn decode(input: &[u8]) -> Cow<'_, str> {
let replaced = replace_plus(input);
decode_utf8_lossy(match percent_decode(&replaced).into() {
Cow::Owned(vec) => Cow::Owned(vec),
@ -66,7 +68,7 @@ fn decode(input: &[u8]) -> Cow<str> {
}
/// Replace b'+' with b' '
fn replace_plus(input: &[u8]) -> Cow<[u8]> {
fn replace_plus(input: &[u8]) -> Cow<'_, [u8]> {
match input.iter().position(|&b| b == b'+') {
None => Cow::Borrowed(input),
Some(first_position) => {
@ -108,7 +110,7 @@ impl<'a> Iterator for ParseIntoOwned<'a> {
/// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer).
///
/// Return an iterator of `&str` slices.
pub fn byte_serialize(input: &[u8]) -> ByteSerialize {
pub fn byte_serialize(input: &[u8]) -> ByteSerialize<'_> {
ByteSerialize { bytes: input }
}
@ -142,6 +144,10 @@ impl<'a> Iterator for ByteSerialize<'a> {
None => (self.bytes, &[][..]),
};
self.bytes = remaining;
// This unsafe is appropriate because we have already checked these
// bytes in byte_serialized_unchanged, which checks for a subset
// of UTF-8. So we know these bytes are valid UTF-8, and doing
// another UTF-8 check would be wasteful.
Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
} else {
None
@ -191,30 +197,6 @@ impl<'a> Target for &'a mut String {
type Finished = Self;
}
// `as_mut_string` string here exposes the internal serialization of an `Url`,
// which should not be exposed to users.
// We achieve that by not giving users direct access to `UrlQuery`:
// * Its fields are private
// (and so can not be constructed with struct literal syntax outside of this crate),
// * It has no constructor
// * It is only visible (on the type level) to users in the return type of
// `Url::query_pairs_mut` which is `Serializer<UrlQuery>`
// * `Serializer` keeps its target in a private field
// * Unlike in other `Target` impls, `UrlQuery::finished` does not return `Self`.
impl<'a> Target for ::UrlQuery<'a> {
fn as_mut_string(&mut self) -> &mut String {
&mut self.url.as_mut().unwrap().serialization
}
fn finish(mut self) -> &'a mut ::Url {
let url = self.url.take().unwrap();
url.restore_already_parsed_fragment(self.fragment.take());
url
}
type Finished = &'a mut ::Url;
}
impl<'a, T: Target> Serializer<'a, T> {
/// Create a new `application/x-www-form-urlencoded` serializer for the given target.
///
@ -230,7 +212,14 @@ impl<'a, T: Target> Serializer<'a, T> {
/// If that suffix is non-empty,
/// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
pub fn for_suffix(mut target: T, start_position: usize) -> Self {
&target.as_mut_string()[start_position..]; // Panic if out of bounds
if target.as_mut_string().len() < start_position {
panic!(
"invalid length {} for target of length {}",
start_position,
target.as_mut_string().len()
);
}
Serializer {
target: Some(target),
start_position,
@ -266,6 +255,19 @@ impl<'a, T: Target> Serializer<'a, T> {
self
}
/// Serialize and append a name of parameter without any value.
///
/// Panics if called after `.finish()`.
pub fn append_key_only(&mut self, name: &str) -> &mut Self {
append_key_only(
string(&mut self.target),
self.start_position,
self.encoding,
name,
);
self
}
/// Serialize and append a number of name/value pairs.
///
/// This simply calls `append_pair` repeatedly.
@ -296,10 +298,33 @@ impl<'a, T: Target> Serializer<'a, T> {
self
}
/// Serialize and append a number of names without values.
///
/// This simply calls `append_key_only` repeatedly.
/// This can be more convenient, so the user doesnt need to introduce a block
/// to limit the scope of `Serializer`s borrow of its string.
///
/// Panics if called after `.finish()`.
pub fn extend_keys_only<I, K>(&mut self, iter: I) -> &mut Self
where
I: IntoIterator,
I::Item: Borrow<K>,
K: AsRef<str>,
{
{
let string = string(&mut self.target);
for key in iter {
let k = key.borrow().as_ref();
append_key_only(string, self.start_position, self.encoding, k);
}
}
self
}
/// If this serializer was constructed with a string, take and return that string.
///
/// ```rust
/// use url::form_urlencoded;
/// use form_urlencoded;
/// let encoded: String = form_urlencoded::Serializer::new(String::new())
/// .append_pair("foo", "bar & baz")
/// .append_pair("saison", "Été+hiver")
@ -332,7 +357,7 @@ fn string<T: Target>(target: &mut Option<T>) -> &mut String {
fn append_pair(
string: &mut String,
start_position: usize,
encoding: EncodingOverride,
encoding: EncodingOverride<'_>,
name: &str,
value: &str,
) {
@ -342,6 +367,54 @@ fn append_pair(
append_encoded(value, string, encoding);
}
fn append_encoded(s: &str, string: &mut String, encoding: EncodingOverride) {
string.extend(byte_serialize(&query_encoding::encode(encoding, s.into())))
fn append_key_only(
string: &mut String,
start_position: usize,
encoding: EncodingOverride,
name: &str,
) {
append_separator_if_needed(string, start_position);
append_encoded(name, string, encoding);
}
fn append_encoded(s: &str, string: &mut String, encoding: EncodingOverride<'_>) {
string.extend(byte_serialize(&encode(encoding, s)))
}
pub(crate) fn encode<'a>(encoding_override: EncodingOverride<'_>, input: &'a str) -> Cow<'a, [u8]> {
if let Some(o) = encoding_override {
return o(input);
}
input.as_bytes().into()
}
pub(crate) fn decode_utf8_lossy(input: Cow<'_, [u8]>) -> Cow<'_, str> {
// Note: This function is duplicated in `percent_encoding/lib.rs`.
match input {
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
Cow::Owned(bytes) => {
match String::from_utf8_lossy(&bytes) {
Cow::Borrowed(utf8) => {
// If from_utf8_lossy returns a Cow::Borrowed, then we can
// be sure our original bytes were valid UTF-8. This is because
// if the bytes were invalid UTF-8 from_utf8_lossy would have
// to allocate a new owned string to back the Cow so it could
// replace invalid bytes with a placeholder.
// First we do a debug_assert to confirm our description above.
let raw_utf8: *const [u8];
raw_utf8 = utf8.as_bytes();
debug_assert!(raw_utf8 == &*bytes as *const [u8]);
// Given we know the original input bytes are valid UTF-8,
// and we have ownership of those bytes, we re-use them and
// return a Cow::Owned here.
Cow::Owned(unsafe { String::from_utf8_unchecked(bytes) })
}
Cow::Owned(s) => Cow::Owned(s),
}
}
}
}
pub type EncodingOverride<'a> = Option<&'a dyn Fn(&str) -> Cow<'_, [u8]>>;

2
third_party/rust/url/.cargo-checksum.json поставляемый
Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"911d5d7758567d81f098c351b005ca2b9a9963dce59b2e4c9601990b23d32bc1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e","README.md":"8f7f1699e885e867f16e2beacf161751735582fbc080ada0762ba512244f28fc","UPGRADING.md":"fbcc2d39bdf17db0745793db6626fcd5c909dddd4ce13b27566cfabece22c368","appveyor.yml":"c78486dbfbe6ebbf3d808afb9a19f7ec18c4704ce451c6305f0716999b70a1a6","benches/parse_url.rs":"821ecb051c3c6c40eb3b268ba7337b2988333627d0af0c8e1afc84734ffbbf2b","src/form_urlencoded.rs":"97b948b959460ef1323bdbb9147eef3edac9db2e236f75cb49668cbab8d1f708","src/host.rs":"8401138bbda58771e0377e5e45b695844a1ad3320584115d931e933c2097e4d1","src/lib.rs":"f822ca47da43bb283af3b590d541321ff1ec828045e12a8d97fb688e7e3f8610","src/origin.rs":"5ee6d1dc360a191362e2bee9840bdc26df8f20cb1b70282024cebd3e14490e92","src/parser.rs":"912733b8f62bf765e077dde2bc21918bef39f6f41d262059cd1738f3d6a8824c","src/path_segments.rs":"c322c048a075db47dd73289e83876cbb25b69a3b17cad6054ddb47a93fab85dd","src/query_encoding.rs":"88d31936327461af1382393117fc07bbdf064c6930aaff3cd8b38d2343e41b51","src/quirks.rs":"a5be1ade22b29e86b432d0340cc5737614b28c7db0df36d7c1b6ea84e60e3c83","src/slicing.rs":"a59ec10a3c3a6b454f66014ca7fd949ea48159a13b100fca2712c784021ccdc3","tests/data.rs":"3c8c1255d86c1d1cfb17d4c979c18dd13d4f7386512b95be9a47dd755502fe68","tests/setters_tests.json":"08ddaa632ad19c81e83b904bfaa94bc971f26e2bdfcef27d2f93fd033ad57340","tests/unit.rs":"6068d8386d56d7a4495eb81bbd9b21bca7696755cf5b14efc3d7b33cfad24a19","tests/urltestdata.json":"1b0c7c727d8d7e79dfb0d0aa347ff05675ddb68bc4ead38f83fd8e89bc59cc32"},"package":"75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61"}
{"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"}

25
third_party/rust/url/Cargo.toml поставляемый
Просмотреть файл

@ -11,30 +11,26 @@
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "url"
version = "2.1.0"
version = "2.2.2"
authors = ["The rust-url developers"]
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"
readme = "../README.md"
keywords = ["url", "parser"]
categories = ["parser-implementations", "web-programming", "encoding"]
license = "MIT/Apache-2.0"
repository = "https://github.com/servo/rust-url"
[lib]
test = false
[[test]]
name = "unit"
[[test]]
name = "data"
harness = false
[[bench]]
name = "parse_url"
path = "benches/parse_url.rs"
harness = false
[dependencies.form_urlencoded]
version = "1.0.0"
[dependencies.idna]
version = "0.2.0"
@ -42,7 +38,7 @@ version = "0.2.0"
version = "0.1"
[dependencies.percent-encoding]
version = "2.0.0"
version = "2.1.0"
[dependencies.serde]
version = "1.0"
@ -51,9 +47,6 @@ optional = true
[dev-dependencies.bencher]
version = "0.1"
[dev-dependencies.rustc-test]
version = "0.3"
[dev-dependencies.serde_json]
version = "1.0"
[badges.appveyor]

10
third_party/rust/url/README.md поставляемый
Просмотреть файл

@ -1,10 +0,0 @@
rust-url
========
[![Travis build Status](https://travis-ci.com/servo/rust-url.svg?branch=master)](https://travis-ci.com/servo/rust-url) [![Appveyor build status](https://ci.appveyor.com/api/projects/status/ulkqx2xcemyod6xa?svg=true)](https://ci.appveyor.com/project/Manishearth/rust-url)
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 0.x to 1.x.

263
third_party/rust/url/UPGRADING.md поставляемый
Просмотреть файл

@ -1,263 +0,0 @@
# Guide to upgrading from url 0.x to 1.x
* The fields of `Url` are now private because the `Url` constructor, parser,
and setters maintain invariants that could be violated if you were to set the fields directly.
Instead of accessing, for example, `url.scheme`, use the getter method, such as `url.scheme()`.
Instead of assigning directly to a field, for example `url.scheme = "https".to_string()`,
use the setter method, such as `url.set_scheme("https").unwrap()`.
(Some setters validate the new value and return a `Result` that must be used).
* The methods of `Url` now return `&str` instead of `String`,
thus reducing allocations and making serialization cheap.
* The `path()` method on `url::Url` instances used to return `Option<&[String]>`;
now it returns `&str`.
If you would like functionality more similar to the old behavior of `path()`,
use `path_segments()` that returns `Option<str::Split<char>>`.
Before upgrading:
```rust
let issue_list_url = Url::parse(
"https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
).unwrap();
assert_eq!(issue_list_url.path(), Some(&["rust-lang".to_string(),
"rust".to_string(),
"issues".to_string()][..]));
```
After upgrading:
```rust
let issue_list_url = Url::parse(
"https://github.com/rust-lang/rust/issues?labels=E-easy&state=open"
).unwrap();
assert_eq!(issue_list_url.path(), "/rust-lang/rust/issues");
assert_eq!(issue_list_url.path_segments().map(|c| c.collect::<Vec<_>>()),
Some(vec!["rust-lang", "rust", "issues"]));
```
* The `path_mut()` method on `url::Url` instances that allowed modification of a URL's path
has been replaced by `path_segments_mut()`.
Before upgrading:
```rust
let mut url = Url::parse("https://github.com/rust-lang/rust").unwrap();
url.path_mut().unwrap().push("issues");
```
After upgrading:
```rust
let mut url = Url::parse("https://github.com/rust-lang/rust").unwrap();
url.path_segments_mut().unwrap().push("issues");
```
* The `domain_mut()` method on `url::Url` instances that allowed modification of a URL's domain
has been replaced by `set_host()` and `set_ip_host()`.
* The `host()` method on `url::Url` instances used to return `Option<&Host>`;
now it returns `Option<Host<&str>>`.
The `serialize_host()` method that returned `Option<String>`
has been replaced by the `host_str()` method that returns `Option<&str>`.
* The `serialize()` method on `url::Url` instances that returned `String`
has been replaced by an `as_str()` method that returns `&str`.
Before upgrading:
```rust
let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
assert_eq!(this_document.serialize(), "http://servo.github.io/rust-url/url/index.html".to_string());
```
After upgrading:
```rust
let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
assert_eq!(this_document.as_str(), "http://servo.github.io/rust-url/url/index.html");
```
* `url::UrlParser` has been replaced by `url::Url::parse()` and `url::Url::join()`.
Before upgrading:
```rust
let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
let css_url = UrlParser::new().base_url(&this_document).parse("../main.css").unwrap();
assert_eq!(css_url.serialize(), "http://servo.github.io/rust-url/main.css".to_string());
```
After upgrading:
```rust
let this_document = Url::parse("http://servo.github.io/rust-url/url/index.html").unwrap();
let css_url = this_document.join("../main.css").unwrap();
assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");
```
* `url::parse_path()` and `url::UrlParser::parse_path()` have been removed without replacement.
As a workaround, you can give a base URL that you then ignore too `url::Url::parse()`.
Before upgrading:
```rust
let (path, query, fragment) = url::parse_path("/foo/bar/../baz?q=42").unwrap();
assert_eq!(path, vec!["foo".to_string(), "baz".to_string()]);
assert_eq!(query, Some("q=42".to_string()));
assert_eq!(fragment, None);
```
After upgrading:
```rust
let base = Url::parse("http://example.com").unwrap();
let with_path = base.join("/foo/bar/../baz?q=42").unwrap();
assert_eq!(with_path.path(), "/foo/baz");
assert_eq!(with_path.query(), Some("q=42"));
assert_eq!(with_path.fragment(), None);
```
* The `url::form_urlencoded::serialize()` method
has been replaced with the `url::form_urlencoded::Serializer` struct.
Instead of calling `serialize()` with key/value pairs,
create a new `Serializer` with a new string,
call the `extend_pairs()` method on the `Serializer` instance with the key/value pairs as the argument,
then call `finish()`.
Before upgrading:
```rust
let form = url::form_urlencoded::serialize(form.iter().map(|(k, v)| {
(&k[..], &v[..])
}));
```
After upgrading:
```rust
let form = url::form_urlencoded::Serializer::new(String::new()).extend_pairs(
form.iter().map(|(k, v)| { (&k[..], &v[..]) })
).finish();
```
* The `set_query_from_pairs()` method on `url::Url` instances that took key/value pairs
has been replaced with `query_pairs_mut()`, which allows you to modify the `url::Url`'s query pairs.
Before upgrading:
```rust
let mut url = Url::parse("https://duckduckgo.com/").unwrap();
let pairs = vec![
("q", "test"),
("ia", "images"),
];
url.set_query_from_pairs(pairs.iter().map(|&(k, v)| {
(&k[..], &v[..])
}));
```
After upgrading:
```rust
let mut url = Url::parse("https://duckduckgo.com/").unwrap();
let pairs = vec![
("q", "test"),
("ia", "images"),
];
url.query_pairs_mut().clear().extend_pairs(
pairs.iter().map(|&(k, v)| { (&k[..], &v[..]) })
);
```
* `url::SchemeData`, its variants `Relative` and `NonRelative`,
and the struct `url::RelativeSchemeData` have been removed.
Instead of matching on these variants
to determine if you have a URL in a relative scheme such as HTTP
versus a URL in a non-relative scheme as data,
use the `cannot_be_a_base()` method to determine which kind you have.
Before upgrading:
```rust
match url.scheme_data {
url::SchemeData::Relative(..) => {}
url::SchemeData::NonRelative(..) => {
return Err(human(format!("`{}` must have relative scheme \
data: {}", field, url)))
}
}
```
After upgrading:
```rust
if url.cannot_be_a_base() {
return Err(human(format!("`{}` must have relative scheme \
data: {}", field, url)))
}
```
* The functions `url::whatwg_scheme_type_mapper()`, the `SchemeType` enum,
and the `scheme_type_mapper()` method on `url::UrlParser` instances have been removed.
`SchemeType` had a method for getting the `default_port()`;
to replicate this functionality, use the method `port_or_known_default()` on `url::Url` instances.
The `port_or_default()` method on `url::Url` instances has been removed;
use `port_or_known_default()` instead.
Before upgrading:
```rust
let port = match whatwg_scheme_type_mapper(&url.scheme) {
SchemeType::Relative(port) => port,
_ => return Err(format!("Invalid special scheme: `{}`",
raw_url.scheme)),
};
```
After upgrading:
```rust
let port = match url.port_or_known_default() {
Some(port) => port,
_ => return Err(format!("Invalid special scheme: `{}`",
url.scheme())),
};
```
* The following formatting utilities have been removed without replacement;
look at their linked previous implementations
if you would like to replicate the functionality in your code:
* [`url::format::PathFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL24)
* [`url::format::UserInfoFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL50)
* [`url::format::UrlNoFragmentFormatter`](https://github.com/servo/rust-url/pull/176/commits/9e759f18726c8e1343162922b87163d4dd08fe3c#diff-0bb16ac13b75e9b568fa4aff61b0e71dL70)
* `url::percent_encoding::percent_decode()` used to have a return type of `Vec<u8>`;
now it returns an iterator of decoded `u8` bytes that also implements `Into<Cow<u8>>`.
Use `.into().to_owned()` to obtain a `Vec<u8>`.
(`.collect()` also works but might not be as efficient.)
* The `url::percent_encoding::EncodeSet` struct and constant instances
used with `url::percent_encoding::percent_encode()`
have been changed to structs that implement the trait `url::percent_encoding::EncodeSet`.
* `SIMPLE_ENCODE_SET`, `QUERY_ENCODE_SET`, `DEFAULT_ENCODE_SET`,
and `USERINFO_ENCODE_SET` have the same behavior.
* `USERNAME_ENCODE_SET` and `PASSWORD_ENCODE_SET` have been removed;
use `USERINFO_ENCODE_SET` instead.
* `HTTP_VALUE_ENCODE_SET` has been removed;
an implementation of it in the new types can be found [in hyper's source](
https://github.com/hyperium/hyper/blob/67436c5bf615cf5a55a71e32b788afef5985570e/src/header/parsing.rs#L131-L138)
if you need to replicate this functionality in your code.
* `FORM_URLENCODED_ENCODE_SET` has been removed;
instead, use the functionality in `url::form_urlencoded`.
* `PATH_SEGMENT_ENCODE_SET` has been added for use on '/'-separated path segments.
* `url::percent_encoding::percent_decode_to()` has been removed.
Use `url::percent_encoding::percent_decode()` which returns an iterator.
You can then use the iterators `collect()` method
or give it to some data structures `extend()` method.
* A number of `ParseError` variants have changed.
[See the documentation for the current set](http://servo.github.io/rust-url/url/enum.ParseError.html).
* `url::OpaqueOrigin::new()` and `url::Origin::UID(OpaqueOrigin)`
have been replaced by `url::Origin::new_opaque()` and `url::Origin::Opaque(OpaqueOrigin)`, respectively.

13
third_party/rust/url/appveyor.yml поставляемый
Просмотреть файл

@ -1,13 +0,0 @@
install:
- ps: Start-FileDownload 'https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe'
- rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- rustc -V
- cargo -V
- git submodule update --init --recursive
build: false
test_script:
- cargo build
- cargo test --verbose

18
third_party/rust/url/benches/parse_url.rs поставляемый
Просмотреть файл

@ -1,18 +0,0 @@
#[macro_use]
extern crate bencher;
extern crate url;
use bencher::{black_box, Bencher};
use url::Url;
fn short(bench: &mut Bencher) {
let url = "https://example.com/bench";
bench.bytes = url.len() as u64;
bench.iter(|| black_box(url).parse::<Url>().unwrap());
}
benchmark_group!(benches, short);
benchmark_main!(benches);

152
third_party/rust/url/src/host.rs поставляемый
Просмотреть файл

@ -6,15 +6,16 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use idna;
use parser::{ParseError, ParseResult};
use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::cmp;
use std::fmt::{self, Formatter};
use std::net::{Ipv4Addr, Ipv6Addr};
use percent_encoding::{percent_decode, utf8_percent_encode, CONTROLS};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::parser::{ParseError, ParseResult};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum HostInternal {
@ -24,9 +25,10 @@ pub(crate) enum HostInternal {
Ipv6(Ipv6Addr),
}
impl<S> From<Host<S>> for HostInternal {
fn from(host: Host<S>) -> HostInternal {
impl From<Host<String>> for HostInternal {
fn from(host: Host<String>) -> HostInternal {
match host {
Host::Domain(ref s) if s.is_empty() => HostInternal::None,
Host::Domain(_) => HostInternal::Domain,
Host::Ipv4(address) => HostInternal::Ipv4(address),
Host::Ipv6(address) => HostInternal::Ipv6(address),
@ -36,7 +38,7 @@ impl<S> From<Host<S>> for HostInternal {
/// The host name of an URL.
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[derive(Clone, Debug, Eq, Ord, PartialOrd, Hash)]
pub enum Host<S = String> {
/// A DNS domain name, as '.' dot-separated labels.
/// Non-ASCII labels are encoded in punycode per IDNA if this is the host of
@ -81,33 +83,38 @@ impl Host<String> {
}
let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
let domain = idna::domain_to_ascii(&domain)?;
if domain
.find(|c| {
matches!(
c,
'\0' | '\t'
| '\n'
| '\r'
| ' '
| '#'
| '%'
| '/'
| ':'
| '?'
| '@'
| '['
| '\\'
| ']'
)
})
.is_some()
{
return Err(ParseError::InvalidDomainCharacter);
if domain.is_empty() {
return Err(ParseError::EmptyHost);
}
if let Some(address) = parse_ipv4addr(&domain)? {
let is_invalid_domain_char = |c| {
matches!(
c,
'\0' | '\t'
| '\n'
| '\r'
| ' '
| '#'
| '%'
| '/'
| ':'
| '<'
| '>'
| '?'
| '@'
| '['
| '\\'
| ']'
| '^'
)
};
if domain.find(is_invalid_domain_char).is_some() {
Err(ParseError::InvalidDomainCharacter)
} else if let Some(address) = parse_ipv4addr(&domain)? {
Ok(Host::Ipv4(address))
} else {
Ok(Host::Domain(domain.into()))
Ok(Host::Domain(domain))
}
}
@ -119,35 +126,40 @@ impl Host<String> {
}
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
}
if input
.find(|c| {
matches!(
c,
'\0' | '\t'
| '\n'
| '\r'
| ' '
| '#'
| '/'
| ':'
| '?'
| '@'
| '['
| '\\'
| ']'
)
})
.is_some()
{
return Err(ParseError::InvalidDomainCharacter);
let is_invalid_host_char = |c| {
matches!(
c,
'\0' | '\t'
| '\n'
| '\r'
| ' '
| '#'
| '/'
| ':'
| '<'
| '>'
| '?'
| '@'
| '['
| '\\'
| ']'
| '^'
)
};
if input.find(is_invalid_host_char).is_some() {
Err(ParseError::InvalidDomainCharacter)
} else {
Ok(Host::Domain(
utf8_percent_encode(input, CONTROLS).to_string(),
))
}
let s = utf8_percent_encode(input, CONTROLS).to_string();
Ok(Host::Domain(s))
}
}
impl<S: AsRef<str>> fmt::Display for Host<S> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match *self {
Host::Domain(ref domain) => domain.as_ref().fmt(f),
Host::Ipv4(ref addr) => addr.fmt(f),
@ -160,7 +172,21 @@ impl<S: AsRef<str>> fmt::Display for Host<S> {
}
}
fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter) -> fmt::Result {
impl<S, T> PartialEq<Host<T>> for Host<S>
where
S: PartialEq<T>,
{
fn eq(&self, other: &Host<T>) -> bool {
match (self, other) {
(Host::Domain(a), Host::Domain(b)) => a == b,
(Host::Ipv4(a), Host::Ipv4(b)) => a == b,
(Host::Ipv6(a), Host::Ipv6(b)) => a == b,
(_, _) => false,
}
}
}
fn write_ipv6(addr: &Ipv6Addr, f: &mut Formatter<'_>) -> fmt::Result {
let segments = addr.segments();
let (compress_start, compress_end) = longest_zero_sequence(&segments);
let mut i = 0;
@ -237,11 +263,11 @@ fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
// So instead we check if the input looks like a real number and only return
// an error when it's an overflow.
let valid_number = match r {
8 => input.chars().all(|c| c >= '0' && c <= '7'),
10 => input.chars().all(|c| c >= '0' && c <= '9'),
16 => input
.chars()
.all(|c| (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')),
8 => input.chars().all(|c| ('0'..='7').contains(&c)),
10 => input.chars().all(|c| ('0'..='9').contains(&c)),
16 => input.chars().all(|c| {
('0'..='9').contains(&c) || ('a'..='f').contains(&c) || ('A'..='F').contains(&c)
}),
_ => false,
};
@ -276,7 +302,7 @@ fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
let mut numbers: Vec<u32> = Vec::new();
let mut overflow = false;
for part in parts {
if part == "" {
if part.is_empty() {
return Ok(None);
}
match parse_ipv4number(part) {

468
third_party/rust/url/src/lib.rs поставляемый
Просмотреть файл

@ -73,6 +73,9 @@ assert!(data_url.fragment() == Some(""));
# run().unwrap();
```
## Serde
Enable the `serde` feature to include `Deserialize` and `Serialize` implementations for `url::Url`.
# Base URL
@ -103,24 +106,34 @@ assert_eq!(css_url.as_str(), "http://servo.github.io/rust-url/main.css");
# Ok(())
# }
# run().unwrap();
```
# Feature: `serde`
If you enable the `serde` feature, [`Url`](struct.Url.html) will implement
[`serde::Serialize`](https://docs.rs/serde/1/serde/trait.Serialize.html) and
[`serde::Deserialize`](https://docs.rs/serde/1/serde/trait.Deserialize.html).
See [serde documentation](https://serde.rs) for more information.
```toml
url = { version = "2", features = ["serde"] }
```
*/
#![doc(html_root_url = "https://docs.rs/url/2.0.0")]
#![doc(html_root_url = "https://docs.rs/url/2.2.2")]
#[macro_use]
extern crate matches;
extern crate idna;
extern crate percent_encoding;
pub use form_urlencoded;
#[cfg(feature = "serde")]
extern crate serde;
use host::HostInternal;
use parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
use crate::host::HostInternal;
use crate::parser::{to_u32, Context, Parser, SchemeType, PATH_SEGMENT, USERINFO};
use percent_encoding::{percent_decode, percent_encode, utf8_percent_encode};
use std::borrow::Borrow;
use std::cmp;
#[cfg(feature = "serde")]
use std::error::Error;
use std::fmt::{self, Write};
use std::hash;
use std::io;
@ -130,21 +143,21 @@ use std::ops::{Range, RangeFrom, RangeTo};
use std::path::{Path, PathBuf};
use std::str;
pub use host::Host;
pub use origin::{OpaqueOrigin, Origin};
pub use parser::{ParseError, SyntaxViolation};
pub use path_segments::PathSegmentsMut;
pub use query_encoding::EncodingOverride;
pub use slicing::Position;
use std::convert::TryFrom;
pub use crate::host::Host;
pub use crate::origin::{OpaqueOrigin, Origin};
pub use crate::parser::{ParseError, SyntaxViolation};
pub use crate::path_segments::PathSegmentsMut;
pub use crate::slicing::Position;
pub use form_urlencoded::EncodingOverride;
mod host;
mod origin;
mod parser;
mod path_segments;
mod query_encoding;
mod slicing;
pub mod form_urlencoded;
#[doc(hidden)]
pub mod quirks;
@ -224,7 +237,7 @@ impl<'a> ParseOptions<'a> {
}
/// Parse an URL string with the configuration so far.
pub fn parse(self, input: &str) -> Result<Url, ::ParseError> {
pub fn parse(self, input: &str) -> Result<Url, crate::ParseError> {
Parser {
serialization: String::with_capacity(input.len()),
base_url: self.base_url,
@ -259,7 +272,7 @@ impl Url {
///
/// [`ParseError`]: enum.ParseError.html
#[inline]
pub fn parse(input: &str) -> Result<Url, ::ParseError> {
pub fn parse(input: &str) -> Result<Url, crate::ParseError> {
Url::options().parse(input)
}
@ -276,6 +289,7 @@ impl Url {
/// # fn run() -> Result<(), ParseError> {
/// let url = Url::parse_with_params("https://example.net?dont=clobberme",
/// &[("lang", "rust"), ("browser", "servo")])?;
/// assert_eq!("https://example.net/?dont=clobberme&lang=rust&browser=servo", url.as_str());
/// # Ok(())
/// # }
/// # run().unwrap();
@ -288,7 +302,7 @@ impl Url {
///
/// [`ParseError`]: enum.ParseError.html
#[inline]
pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, ::ParseError>
pub fn parse_with_params<I, K, V>(input: &str, iter: I) -> Result<Url, crate::ParseError>
where
I: IntoIterator,
I::Item: Borrow<(K, V)>,
@ -306,6 +320,8 @@ impl Url {
/// Parse a string as an URL, with this URL as the base URL.
///
/// The inverse of this is [`make_relative`].
///
/// Note: a trailing slash is significant.
/// Without it, the last path component is considered to be a “file” name
/// to be removed to get at the “directory” that is used as the base:
@ -335,11 +351,144 @@ impl Url {
/// with this URL as the base URL, a [`ParseError`] variant will be returned.
///
/// [`ParseError`]: enum.ParseError.html
/// [`make_relative`]: #method.make_relative
#[inline]
pub fn join(&self, input: &str) -> Result<Url, ::ParseError> {
pub fn join(&self, input: &str) -> Result<Url, crate::ParseError> {
Url::options().base_url(Some(self)).parse(input)
}
/// Creates a relative URL if possible, with this URL as the base URL.
///
/// This is the inverse of [`join`].
///
/// # Examples
///
/// ```rust
/// use url::Url;
/// # use url::ParseError;
///
/// # fn run() -> Result<(), ParseError> {
/// let base = Url::parse("https://example.net/a/b.html")?;
/// let url = Url::parse("https://example.net/a/c.png")?;
/// let relative = base.make_relative(&url);
/// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png"));
///
/// let base = Url::parse("https://example.net/a/b/")?;
/// let url = Url::parse("https://example.net/a/b/c.png")?;
/// let relative = base.make_relative(&url);
/// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("c.png"));
///
/// let base = Url::parse("https://example.net/a/b/")?;
/// let url = Url::parse("https://example.net/a/d/c.png")?;
/// let relative = base.make_relative(&url);
/// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("../d/c.png"));
///
/// let base = Url::parse("https://example.net/a/b.html?c=d")?;
/// let url = Url::parse("https://example.net/a/b.html?e=f")?;
/// let relative = base.make_relative(&url);
/// assert_eq!(relative.as_ref().map(|s| s.as_str()), Some("?e=f"));
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
///
/// # Errors
///
/// If this URL can't be a base for the given URL, `None` is returned.
/// This is for example the case if the scheme, host or port are not the same.
///
/// [`join`]: #method.join
pub fn make_relative(&self, url: &Url) -> Option<String> {
if self.cannot_be_a_base() {
return None;
}
// Scheme, host and port need to be the same
if self.scheme() != url.scheme() || self.host() != url.host() || self.port() != url.port() {
return None;
}
// We ignore username/password at this point
// The path has to be transformed
let mut relative = String::new();
// Extract the filename of both URIs, these need to be handled separately
fn extract_path_filename(s: &str) -> (&str, &str) {
let last_slash_idx = s.rfind('/').unwrap_or(0);
let (path, filename) = s.split_at(last_slash_idx);
if filename.is_empty() {
(path, "")
} else {
(path, &filename[1..])
}
}
let (base_path, base_filename) = extract_path_filename(self.path());
let (url_path, url_filename) = extract_path_filename(url.path());
let mut base_path = base_path.split('/').peekable();
let mut url_path = url_path.split('/').peekable();
// Skip over the common prefix
while base_path.peek().is_some() && base_path.peek() == url_path.peek() {
base_path.next();
url_path.next();
}
// Add `..` segments for the remainder of the base path
for base_path_segment in base_path {
// Skip empty last segments
if base_path_segment.is_empty() {
break;
}
if !relative.is_empty() {
relative.push('/');
}
relative.push_str("..");
}
// Append the remainder of the other URI
for url_path_segment in url_path {
if !relative.is_empty() {
relative.push('/');
}
relative.push_str(url_path_segment);
}
// Add the filename if they are not the same
if base_filename != url_filename {
// If the URIs filename is empty this means that it was a directory
// so we'll have to append a '/'.
//
// Otherwise append it directly as the new filename.
if url_filename.is_empty() {
relative.push('/');
} else {
if !relative.is_empty() {
relative.push('/');
}
relative.push_str(url_filename);
}
}
// Query and fragment are only taken from the other URI
if let Some(query) = url.query() {
relative.push('?');
relative.push_str(query);
}
if let Some(fragment) = url.fragment() {
relative.push('#');
relative.push_str(fragment);
}
Some(relative)
}
/// Return a default `ParseOptions` that can fully configure the URL parser.
///
/// # Examples
@ -403,14 +552,15 @@ impl Url {
/// # fn run() -> Result<(), ParseError> {
/// let url_str = "https://example.net/";
/// let url = Url::parse(url_str)?;
/// assert_eq!(url.into_string(), url_str);
/// assert_eq!(String::from(url), url_str);
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
#[inline]
#[deprecated(since = "2.3.0", note = "use Into<String>")]
pub fn into_string(self) -> String {
self.serialization
self.into()
}
/// For internal testing, not part of the public API.
@ -456,13 +606,15 @@ impl Url {
if self.slice(self.scheme_end + 1..).starts_with("//") {
// URL with authority
match self.byte_at(self.username_end) {
b':' => {
assert!(self.host_start >= self.username_end + 2);
assert_eq!(self.byte_at(self.host_start - 1), b'@');
if self.username_end != self.serialization.len() as u32 {
match self.byte_at(self.username_end) {
b':' => {
assert!(self.host_start >= self.username_end + 2);
assert_eq!(self.byte_at(self.host_start - 1), b'@');
}
b'@' => assert!(self.host_start == self.username_end + 1),
_ => assert_eq!(self.username_end, self.scheme_end + 3),
}
b'@' => assert!(self.host_start == self.username_end + 1),
_ => assert_eq!(self.username_end, self.scheme_end + 3),
}
assert!(self.host_start >= self.username_end);
assert!(self.host_end >= self.host_start);
@ -490,7 +642,10 @@ impl Url {
Some(port_str.parse::<u16>().expect("Couldn't parse port?"))
);
}
assert_eq!(self.byte_at(self.path_start), b'/');
assert!(
self.path_start as usize == self.serialization.len()
|| matches!(self.byte_at(self.path_start), b'/' | b'#' | b'?')
);
} else {
// Anarchist URL (no authority)
assert_eq!(self.username_end, self.scheme_end + 1);
@ -501,11 +656,11 @@ impl Url {
assert_eq!(self.path_start, self.scheme_end + 1);
}
if let Some(start) = self.query_start {
assert!(start > self.path_start);
assert!(start >= self.path_start);
assert_eq!(self.byte_at(start), b'?');
}
if let Some(start) = self.fragment_start {
assert!(start > self.path_start);
assert!(start >= self.path_start);
assert_eq!(self.byte_at(start), b'#');
}
if let (Some(query_start), Some(fragment_start)) = (self.query_start, self.fragment_start) {
@ -685,7 +840,7 @@ impl Url {
/// ```
#[inline]
pub fn cannot_be_a_base(&self) -> bool {
!self.slice(self.path_start..).starts_with('/')
!self.slice(self.scheme_end + 1..).starts_with('/')
}
/// Return the username for this URL (typically the empty string)
@ -711,8 +866,9 @@ impl Url {
/// # run().unwrap();
/// ```
pub fn username(&self) -> &str {
if self.has_authority() {
self.slice(self.scheme_end + ("://".len() as u32)..self.username_end)
let scheme_separator_len = "://".len() as u32;
if self.has_authority() && self.username_end > self.scheme_end + scheme_separator_len {
self.slice(self.scheme_end + scheme_separator_len..self.username_end)
} else {
""
}
@ -745,7 +901,10 @@ impl Url {
pub fn password(&self) -> Option<&str> {
// This ':' is not the one marking a port number since a host can not be empty.
// (Except for file: URLs, which do not have port numbers.)
if self.has_authority() && self.byte_at(self.username_end) == b':' {
if self.has_authority()
&& self.username_end != self.serialization.len() as u32
&& self.byte_at(self.username_end) == b':'
{
debug_assert!(self.byte_at(self.host_start - 1) == b'@');
Some(self.slice(self.username_end + 1..self.host_start - 1))
} else {
@ -780,7 +939,8 @@ impl Url {
/// Return the string representation of the host (domain or IP address) for this URL, if any.
///
/// Non-ASCII domains are punycode-encoded per IDNA.
/// Non-ASCII domains are punycode-encoded per IDNA if this is the host
/// of a special URL, or percent encoded for non-special URLs.
/// IPv6 addresses are given between `[` and `]` brackets.
///
/// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs
@ -819,7 +979,8 @@ impl Url {
}
/// Return the parsed representation of the host for this URL.
/// Non-ASCII domain labels are punycode-encoded per IDNA.
/// Non-ASCII domain labels are punycode-encoded per IDNA if this is the host
/// of a special URL, or percent encoded for non-special URLs.
///
/// Cannot-be-a-base URLs (typical of `data:` and `mailto:`) and some `file:` URLs
/// dont have a host.
@ -858,6 +1019,8 @@ impl Url {
}
/// If this URL has a host and it is a domain name (not an IP address), return it.
/// Non-ASCII domains are punycode-encoded per IDNA if this is the host
/// of a special URL, or percent encoded for non-special URLs.
///
/// # Examples
///
@ -917,7 +1080,7 @@ impl Url {
/// Return the port number for this URL, or the default port number if it is known.
///
/// This method only knows the default port number
/// of the `http`, `https`, `ws`, `wss`, `ftp`, and `gopher` schemes.
/// of the `http`, `https`, `ws`, `wss` and `ftp` schemes.
///
/// For URLs in these schemes, this method always returns `Some(_)`.
/// For other schemes, it is the same as `Url::port()`.
@ -1048,7 +1211,7 @@ impl Url {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let url = Url::parse("https://example.com/foo/bar")?;
/// let mut path_segments = url.path_segments().ok_or_else(|| "cannot be base")?;
/// assert_eq!(path_segments.next(), Some("foo"));
@ -1071,7 +1234,8 @@ impl Url {
/// # }
/// # run().unwrap();
/// ```
pub fn path_segments(&self) -> Option<str::Split<char>> {
#[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('/'))
@ -1143,7 +1307,7 @@ impl Url {
///
#[inline]
pub fn query_pairs(&self) -> form_urlencoded::Parse {
pub fn query_pairs(&self) -> form_urlencoded::Parse<'_> {
form_urlencoded::parse(self.query().unwrap_or("").as_bytes())
}
@ -1186,7 +1350,7 @@ impl Url {
})
}
fn mutate<F: FnOnce(&mut Parser) -> R, R>(&mut self, f: F) -> R {
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 result = f(&mut parser);
self.serialization = parser.serialization;
@ -1226,7 +1390,7 @@ impl Url {
if let Some(input) = fragment {
self.fragment_start = Some(to_u32(self.serialization.len()).unwrap());
self.serialization.push('#');
self.mutate(|parser| parser.parse_fragment(parser::Input::new(input)))
self.mutate(|parser| parser.parse_fragment(parser::Input::no_trim(input)))
} else {
self.fragment_start = None
}
@ -1284,7 +1448,12 @@ impl Url {
let scheme_type = SchemeType::from(self.scheme());
let scheme_end = self.scheme_end;
self.mutate(|parser| {
parser.parse_query(scheme_type, scheme_end, parser::Input::new(input))
let vfn = parser.violation_fn;
parser.parse_query(
scheme_type,
scheme_end,
parser::Input::trim_tab_and_newlines(input, vfn),
)
});
}
@ -1323,7 +1492,7 @@ impl Url {
/// not `url.set_query(None)`.
///
/// The state of `Url` is unspecified if this return value is leaked without being dropped.
pub fn query_pairs_mut(&mut self) -> form_urlencoded::Serializer<UrlQuery> {
pub fn query_pairs_mut(&mut self) -> form_urlencoded::Serializer<'_, UrlQuery<'_>> {
let fragment = self.take_fragment();
let query_start;
@ -1400,7 +1569,8 @@ impl Url {
/// Return an object with methods to manipulate this URLs path segments.
///
/// Return `Err(())` if this URL is cannot-be-a-base.
pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut, ()> {
#[allow(clippy::result_unit_err)]
pub fn path_segments_mut(&mut self) -> Result<PathSegmentsMut<'_>, ()> {
if self.cannot_be_a_base() {
Err(())
} else {
@ -1436,7 +1606,7 @@ impl Url {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("ssh://example.net:2048/")?;
///
/// url.set_port(Some(4096)).map_err(|_| "cannot be base")?;
@ -1455,7 +1625,7 @@ impl Url {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://example.org/")?;
///
/// url.set_port(Some(443)).map_err(|_| "cannot be base")?;
@ -1483,6 +1653,7 @@ impl Url {
/// # }
/// # run().unwrap();
/// ```
#[allow(clippy::result_unit_err)]
pub fn set_port(&mut self, mut port: Option<u16>) -> Result<(), ()> {
// has_host implies !cannot_be_a_base
if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
@ -1622,17 +1793,35 @@ impl Url {
}
if let Some(host) = host {
if host == "" && SchemeType::from(self.scheme()).is_special() {
if host.is_empty() && SchemeType::from(self.scheme()).is_special() {
return Err(ParseError::EmptyHost);
}
let mut host_substr = host;
// Otherwise, if c is U+003A (:) and the [] flag is unset, then
if !host.starts_with('[') || !host.ends_with(']') {
match host.find(':') {
Some(0) => {
// If buffer is the empty string, validation error, return failure.
return Err(ParseError::InvalidDomainCharacter);
}
// Let host be the result of host parsing buffer
Some(colon_index) => {
host_substr = &host[..colon_index];
}
None => {}
}
}
if SchemeType::from(self.scheme()).is_special() {
self.set_host_internal(Host::parse(host)?, None)
self.set_host_internal(Host::parse(host_substr)?, None);
} else {
self.set_host_internal(Host::parse_opaque(host)?, None)
self.set_host_internal(Host::parse_opaque(host_substr)?, None);
}
} else if self.has_host() {
if SchemeType::from(self.scheme()).is_special() {
let scheme_type = SchemeType::from(self.scheme());
if scheme_type.is_special() {
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'/');
@ -1735,6 +1924,7 @@ impl Url {
/// # run().unwrap();
/// ```
///
#[allow(clippy::result_unit_err)]
pub fn set_ip_host(&mut self, address: IpAddr) -> Result<(), ()> {
if self.cannot_be_a_base() {
return Err(());
@ -1774,6 +1964,7 @@ impl Url {
/// # }
/// # run().unwrap();
/// ```
#[allow(clippy::result_unit_err)]
pub fn set_password(&mut self, password: Option<&str>) -> Result<(), ()> {
// has_host implies !cannot_be_a_base
if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
@ -1866,6 +2057,7 @@ impl Url {
/// # }
/// # run().unwrap();
/// ```
#[allow(clippy::result_unit_err)]
pub fn set_username(&mut self, username: &str) -> Result<(), ()> {
// has_host implies !cannot_be_a_base
if !self.has_host() || self.host() == Some(Host::Domain("")) || self.scheme() == "file" {
@ -1919,11 +2111,19 @@ impl Url {
/// Change this URLs scheme.
///
/// Do nothing and return `Err` if:
/// Do nothing and return `Err` under the following circumstances:
///
/// * The new scheme is not in `[a-zA-Z][a-zA-Z0-9+.-]+`
/// * This URL is cannot-be-a-base and the new scheme is one of
/// `http`, `https`, `ws`, `wss`, `ftp`, or `gopher`
/// * If the new scheme is not in `[a-zA-Z][a-zA-Z0-9+.-]+`
/// * If this URL is cannot-be-a-base and the new scheme is one of
/// `http`, `https`, `ws`, `wss` or `ftp`
/// * If either the old or new scheme is `http`, `https`, `ws`,
/// `wss` or `ftp` and the other is not one of these
/// * If the new scheme is `file` and this URL includes credentials
/// or has a non-null port
/// * If this URL's scheme is `file` and its host is empty or null
///
/// See also [the URL specification's section on legal scheme state
/// overrides](https://url.spec.whatwg.org/#scheme-state).
///
/// # Examples
///
@ -1935,14 +2135,28 @@ impl Url {
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("https://example.net")?;
/// let result = url.set_scheme("foo");
/// assert_eq!(url.as_str(), "foo://example.net/");
/// let result = url.set_scheme("http");
/// assert_eq!(url.as_str(), "http://example.net/");
/// assert!(result.is_ok());
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
/// Change the URLs scheme from `foo` to `bar`:
///
/// ```
/// use url::Url;
/// # use url::ParseError;
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("foo://example.net")?;
/// let result = url.set_scheme("bar");
/// assert_eq!(url.as_str(), "bar://example.net");
/// assert!(result.is_ok());
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
///
/// Cannot change URLs scheme from `https` to `foõ`:
///
@ -1975,14 +2189,56 @@ impl Url {
/// # }
/// # run().unwrap();
/// ```
/// Cannot change the URLs scheme from `foo` to `https`:
///
/// ```
/// use url::Url;
/// # use url::ParseError;
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("foo://example.net")?;
/// let result = url.set_scheme("https");
/// assert_eq!(url.as_str(), "foo://example.net");
/// assert!(result.is_err());
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
/// Cannot change the URLs scheme from `http` to `foo`:
///
/// ```
/// use url::Url;
/// # use url::ParseError;
///
/// # fn run() -> Result<(), ParseError> {
/// let mut url = Url::parse("http://example.net")?;
/// let result = url.set_scheme("foo");
/// assert_eq!(url.as_str(), "http://example.net/");
/// assert!(result.is_err());
/// # Ok(())
/// # }
/// # run().unwrap();
/// ```
#[allow(clippy::result_unit_err, clippy::suspicious_operation_groupings)]
pub fn set_scheme(&mut self, scheme: &str) -> Result<(), ()> {
let mut parser = Parser::for_setter(String::new());
let remaining = parser.parse_scheme(parser::Input::new(scheme))?;
if !remaining.is_empty()
|| (!self.has_host() && SchemeType::from(&parser.serialization).is_special())
let new_scheme_type = SchemeType::from(&parser.serialization);
let old_scheme_type = SchemeType::from(self.scheme());
// If urls scheme is a special scheme and buffer is not a special scheme, then return.
if (new_scheme_type.is_special() && !old_scheme_type.is_special()) ||
// If urls scheme is not a special scheme and buffer is a special scheme, then return.
(!new_scheme_type.is_special() && old_scheme_type.is_special()) ||
// If url includes credentials or has a non-null port, and buffer is "file", then return.
// If urls scheme is "file" and its host is an empty host or null, then return.
(new_scheme_type.is_file() && self.has_authority())
{
return Err(());
}
if !remaining.is_empty() || (!self.has_host() && new_scheme_type.is_special()) {
return Err(());
}
let old_scheme_end = self.scheme_end;
let new_scheme_end = to_u32(parser.serialization.len()).unwrap();
let adjust = |index: &mut u32| {
@ -2004,6 +2260,14 @@ impl Url {
parser.serialization.push_str(self.slice(old_scheme_end..));
self.serialization = parser.serialization;
// Update the port so it can be removed
// If it is the scheme's default
// we don't mind it silently failing
// if there was no port in the first place
let previous_port = self.port();
let _ = self.set_port(previous_port);
Ok(())
}
@ -2035,6 +2299,7 @@ impl Url {
/// # }
/// ```
#[cfg(any(unix, windows, target_os = "redox"))]
#[allow(clippy::result_unit_err)]
pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Url, ()> {
let mut serialization = "file://".to_owned();
let host_start = serialization.len() as u32;
@ -2071,6 +2336,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"))]
#[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)?;
if !url.serialization.ends_with('/') {
@ -2187,6 +2453,7 @@ impl Url {
/// for a Windows path, is not UTF-8.)
#[inline]
#[cfg(any(unix, windows, target_os = "redox"))]
#[allow(clippy::result_unit_err)]
pub fn to_file_path(&self) -> Result<PathBuf, ()> {
if let Some(segments) = self.path_segments() {
let host = match self.host() {
@ -2223,24 +2490,50 @@ impl str::FromStr for Url {
type Err = ParseError;
#[inline]
fn from_str(input: &str) -> Result<Url, ::ParseError> {
fn from_str(input: &str) -> Result<Url, crate::ParseError> {
Url::parse(input)
}
}
impl<'a> TryFrom<&'a str> for Url {
type Error = ParseError;
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
Url::parse(s)
}
}
/// Display the serialization of this URL.
impl fmt::Display for Url {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.serialization, formatter)
}
}
/// String converstion.
impl From<Url> for String {
fn from(value: Url) -> String {
value.serialization
}
}
/// Debug the serialization of this URL.
impl fmt::Debug for Url {
#[inline]
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.serialization, formatter)
formatter
.debug_struct("Url")
.field("scheme", &self.scheme())
.field("cannot_be_a_base", &self.cannot_be_a_base())
.field("username", &self.username())
.field("password", &self.password())
.field("host", &self.host())
.field("port", &self.port())
.field("path", &self.path())
.field("query", &self.query())
.field("fragment", &self.fragment())
.finish()
}
}
@ -2352,8 +2645,10 @@ impl<'de> serde::Deserialize<'de> for Url {
where
E: Error,
{
Url::parse(s)
.map_err(|err| Error::invalid_value(Unexpected::Str(s), &err.description()))
Url::parse(s).map_err(|err| {
let err_s = format!("{}", err);
Error::invalid_value(Unexpected::Str(s), &err_s.as_str())
})
}
}
@ -2408,6 +2703,7 @@ fn path_to_file_url_segments_windows(
}
let mut components = path.components();
let host_start = serialization.len() + 1;
let host_end;
let host_internal;
match components.next() {
@ -2434,22 +2730,31 @@ fn path_to_file_url_segments_windows(
_ => return Err(()),
}
let mut path_only_has_prefix = true;
for component in components {
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..])
&& path_only_has_prefix
{
serialization.push('/');
}
Ok((host_end, host_internal))
}
#[cfg(any(unix, target_os = "redox"))]
fn file_url_segments_to_pathbuf(
host: Option<&str>,
segments: str::Split<char>,
segments: str::Split<'_, char>,
) -> Result<PathBuf, ()> {
use std::ffi::OsStr;
use std::os::unix::prelude::OsStrExt;
@ -2467,6 +2772,13 @@ fn file_url_segments_to_pathbuf(
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')
&& matches!(bytes[bytes.len() - 1], b':' | b'|')
{
bytes.push(b'/');
}
let os_str = OsStr::from_bytes(&bytes);
let path = PathBuf::from(os_str);
debug_assert!(
@ -2488,7 +2800,7 @@ fn file_url_segments_to_pathbuf(
#[cfg_attr(not(windows), allow(dead_code))]
fn file_url_segments_to_pathbuf_windows(
host: Option<&str>,
mut segments: str::Split<char>,
mut segments: str::Split<'_, char>,
) -> Result<PathBuf, ()> {
let mut string = if let Some(host) = host {
r"\\".to_owned() + host
@ -2544,6 +2856,30 @@ pub struct UrlQuery<'a> {
fragment: Option<String>,
}
// `as_mut_string` string here exposes the internal serialization of an `Url`,
// which should not be exposed to users.
// We achieve that by not giving users direct access to `UrlQuery`:
// * Its fields are private
// (and so can not be constructed with struct literal syntax outside of this crate),
// * It has no constructor
// * It is only visible (on the type level) to users in the return type of
// `Url::query_pairs_mut` which is `Serializer<UrlQuery>`
// * `Serializer` keeps its target in a private field
// * Unlike in other `Target` impls, `UrlQuery::finished` does not return `Self`.
impl<'a> form_urlencoded::Target for UrlQuery<'a> {
fn as_mut_string(&mut self) -> &mut String {
&mut self.url.as_mut().unwrap().serialization
}
fn finish(mut self) -> &'a mut Url {
let url = self.url.take().unwrap();
url.restore_already_parsed_fragment(self.fragment.take());
url
}
type Finished = &'a mut Url;
}
impl<'a> Drop for UrlQuery<'a> {
fn drop(&mut self) {
if let Some(url) = self.url.take() {

10
third_party/rust/url/src/origin.rs поставляемый
Просмотреть файл

@ -6,11 +6,11 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use host::Host;
use crate::host::Host;
use crate::parser::default_port;
use crate::Url;
use idna::domain_to_unicode;
use parser::default_port;
use std::sync::atomic::{AtomicUsize, Ordering};
use Url;
pub fn url_origin(url: &Url) -> Origin {
let scheme = url.scheme();
@ -22,7 +22,7 @@ pub fn url_origin(url: &Url) -> Origin {
Err(_) => Origin::new_opaque(),
}
}
"ftp" | "gopher" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
"ftp" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
scheme.to_owned(),
url.host().unwrap().to_owned(),
url.port_or_known_default().unwrap(),
@ -44,7 +44,7 @@ pub fn url_origin(url: &Url) -> Origin {
/// - If the scheme is "blob" the origin is the origin of the
/// URL contained in the path component. If parsing fails,
/// it is an opaque origin.
/// - If the scheme is "ftp", "gopher", "http", "https", "ws", or "wss",
/// - If the scheme is "ftp", "http", "https", "ws", or "wss",
/// then the origin is a tuple of the scheme, host, and port.
/// - If the scheme is anything else, the origin is opaque, meaning
/// the URL does not have the same origin as any other URL.

766
third_party/rust/url/src/parser.rs поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

37
third_party/rust/url/src/path_segments.rs поставляемый
Просмотреть файл

@ -6,9 +6,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use parser::{self, to_u32, SchemeType};
use crate::parser::{self, to_u32, SchemeType};
use crate::Url;
use std::str;
use Url;
/// Exposes methods to manipulate the path of an URL that is not cannot-be-base.
///
@ -21,7 +21,7 @@ use Url;
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("mailto:me@example.com")?;
/// assert!(url.path_segments_mut().is_err());
///
@ -42,10 +42,18 @@ pub struct PathSegmentsMut<'a> {
}
// Not re-exported outside the crate
pub fn new(url: &mut Url) -> PathSegmentsMut {
pub fn new(url: &mut Url) -> PathSegmentsMut<'_> {
let after_path = url.take_after_path();
let old_after_path_position = to_u32(url.serialization.len()).unwrap();
debug_assert!(url.byte_at(url.path_start) == b'/');
// Special urls always have a non empty path
if SchemeType::from(url.scheme()).is_special() {
debug_assert!(url.byte_at(url.path_start) == b'/');
} else {
debug_assert!(
url.serialization.len() == url.path_start as usize
|| url.byte_at(url.path_start) == b'/'
);
}
PathSegmentsMut {
after_first_slash: url.path_start as usize + "/".len(),
url,
@ -72,7 +80,7 @@ impl<'a> PathSegmentsMut<'a> {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .clear().push("logout");
@ -100,7 +108,7 @@ impl<'a> PathSegmentsMut<'a> {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .push("pulls");
@ -115,6 +123,9 @@ impl<'a> PathSegmentsMut<'a> {
/// # run().unwrap();
/// ```
pub fn pop_if_empty(&mut self) -> &mut Self {
if self.after_first_slash >= self.url.serialization.len() {
return self;
}
if self.url.serialization[self.after_first_slash..].ends_with('/') {
self.url.serialization.pop();
}
@ -127,6 +138,9 @@ impl<'a> PathSegmentsMut<'a> {
///
/// Returns `&mut Self` so that method calls can be chained.
pub fn pop(&mut self) -> &mut Self {
if self.after_first_slash >= self.url.serialization.len() {
return self;
}
let last_slash = self.url.serialization[self.after_first_slash..]
.rfind('/')
.unwrap_or(0);
@ -169,7 +183,7 @@ impl<'a> PathSegmentsMut<'a> {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/")?;
/// let org = "servo";
/// let repo = "rust-url";
@ -189,7 +203,7 @@ impl<'a> PathSegmentsMut<'a> {
/// use url::Url;
/// # use std::error::Error;
///
/// # fn run() -> Result<(), Box<Error>> {
/// # fn run() -> Result<(), Box<dyn Error>> {
/// let mut url = Url::parse("https://github.com/servo")?;
/// url.path_segments_mut().map_err(|_| "cannot be base")?
/// .extend(&["..", "rust-url", ".", "pulls"]);
@ -212,7 +226,10 @@ impl<'a> PathSegmentsMut<'a> {
if matches!(segment, "." | "..") {
continue;
}
if parser.serialization.len() > path_start + 1 {
if parser.serialization.len() > path_start + 1
// Non special url's path might still be empty
|| parser.serialization.len() == path_start
{
parser.serialization.push('/');
}
let mut has_host = true; // FIXME account for this?

35
third_party/rust/url/src/query_encoding.rs поставляемый
Просмотреть файл

@ -1,35 +0,0 @@
// Copyright 2019 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::borrow::Cow;
pub type EncodingOverride<'a> = Option<&'a dyn Fn(&str) -> Cow<[u8]>>;
pub(crate) fn encode<'a>(encoding_override: EncodingOverride, input: &'a str) -> Cow<'a, [u8]> {
if let Some(o) = encoding_override {
return o(input);
}
input.as_bytes().into()
}
pub(crate) fn decode_utf8_lossy(input: Cow<[u8]>) -> Cow<str> {
match input {
Cow::Borrowed(bytes) => String::from_utf8_lossy(bytes),
Cow::Owned(bytes) => {
let raw_utf8: *const [u8];
match String::from_utf8_lossy(&bytes) {
Cow::Borrowed(utf8) => raw_utf8 = utf8.as_bytes(),
Cow::Owned(s) => return s.into(),
}
// from_utf8_lossy returned a borrow of `bytes` unchanged.
debug_assert!(raw_utf8 == &*bytes as *const [u8]);
// Reuse the existing `Vec` allocation.
unsafe { String::from_utf8_unchecked(bytes) }.into()
}
}
}

105
third_party/rust/url/src/quirks.rs поставляемый
Просмотреть файл

@ -11,8 +11,8 @@
//! Unless you need to be interoperable with web browsers,
//! you probably want to use `Url` method instead.
use parser::{default_port, Context, Input, Parser, SchemeType};
use {idna, Host, ParseError, Position, Url};
use crate::parser::{default_port, Context, Input, Parser, SchemeType};
use crate::{Host, ParseError, Position, Url};
/// https://url.spec.whatwg.org/#dom-url-domaintoascii
pub fn domain_to_ascii(domain: &str) -> String {
@ -56,6 +56,7 @@ pub fn protocol(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-protocol
#[allow(clippy::result_unit_err)]
pub fn set_protocol(url: &mut Url, mut new_protocol: &str) -> Result<(), ()> {
// The scheme state in the spec ignores everything after the first `:`,
// but `set_scheme` errors if there is more.
@ -72,6 +73,7 @@ pub fn username(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-username
#[allow(clippy::result_unit_err)]
pub fn set_username(url: &mut Url, new_username: &str) -> Result<(), ()> {
url.set_username(new_username)
}
@ -83,6 +85,7 @@ pub fn password(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-password
#[allow(clippy::result_unit_err)]
pub fn set_password(url: &mut Url, new_password: &str) -> Result<(), ()> {
url.set_password(if new_password.is_empty() {
None
@ -98,27 +101,50 @@ pub fn host(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-host
#[allow(clippy::result_unit_err)]
pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
// If context objects urls cannot-be-a-base-URL flag is set, then return.
if url.cannot_be_a_base() {
return Err(());
}
// Host parsing rules are strict,
// We don't want to trim the input
let input = Input::no_trim(new_host);
let host;
let opt_port;
{
let scheme = url.scheme();
let result = Parser::parse_host(Input::new(new_host), SchemeType::from(scheme));
match result {
Ok((h, remaining)) => {
host = h;
opt_port = if let Some(remaining) = remaining.split_prefix(':') {
let scheme_type = SchemeType::from(scheme);
if scheme_type == SchemeType::File && new_host.is_empty() {
url.set_host_internal(Host::Domain(String::new()), None);
return Ok(());
}
if let Ok((h, remaining)) = Parser::parse_host(input, scheme_type) {
host = h;
opt_port = if let Some(remaining) = remaining.split_prefix(':') {
if remaining.is_empty() {
None
} else {
Parser::parse_port(remaining, || default_port(scheme), Context::Setter)
.ok()
.map(|(port, _remaining)| port)
} else {
None
};
}
Err(_) => return Err(()),
}
} else {
None
};
} else {
return Err(());
}
}
// 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(());
}
}
url.set_host_internal(host, opt_port);
@ -132,12 +158,34 @@ pub fn hostname(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-hostname
#[allow(clippy::result_unit_err)]
pub fn set_hostname(url: &mut Url, new_hostname: &str) -> Result<(), ()> {
if url.cannot_be_a_base() {
return Err(());
}
let result = Parser::parse_host(Input::new(new_hostname), SchemeType::from(url.scheme()));
if let Ok((host, _remaining)) = result {
// Host parsing rules are strict we don't want to trim the input
let input = Input::no_trim(new_hostname);
let scheme_type = SchemeType::from(url.scheme());
if scheme_type == SchemeType::File && new_hostname.is_empty() {
url.set_host_internal(Host::Domain(String::new()), None);
return Ok(());
}
if let Ok((host, _remaining)) = Parser::parse_host(input, scheme_type) {
if let Host::Domain(h) = &host {
if h.is_empty() {
// Empty host on special not file url
if SchemeType::from(url.scheme()) == SchemeType::SpecialNotFile
// Port with an empty host
||!port(&url).is_empty()
// Empty host that includes credentials
|| !url.username().is_empty()
|| !url.password().unwrap_or(&"").is_empty()
{
return Err(());
}
}
}
url.set_host_internal(host, None);
Ok(())
} else {
@ -152,6 +200,7 @@ pub fn port(url: &Url) -> &str {
}
/// Setter for https://url.spec.whatwg.org/#dom-url-port
#[allow(clippy::result_unit_err)]
pub fn set_port(url: &mut Url, new_port: &str) -> Result<(), ()> {
let result;
{
@ -182,8 +231,19 @@ pub fn pathname(url: &Url) -> &str {
/// Setter for https://url.spec.whatwg.org/#dom-url-pathname
pub fn set_pathname(url: &mut Url, new_pathname: &str) {
if !url.cannot_be_a_base() {
if url.cannot_be_a_base() {
return;
}
if new_pathname.starts_with('/')
|| (SchemeType::from(url.scheme()).is_special()
// \ is a segment delimiter for 'special' URLs"
&& new_pathname.starts_with('\\'))
{
url.set_path(new_pathname)
} else {
let mut path_to_set = String::from("/");
path_to_set.push_str(new_pathname);
url.set_path(&path_to_set)
}
}
@ -208,13 +268,14 @@ pub fn hash(url: &Url) -> &str {
/// Setter for https://url.spec.whatwg.org/#dom-url-hash
pub fn set_hash(url: &mut Url, new_hash: &str) {
if url.scheme() != "javascript" {
url.set_fragment(match new_hash {
"" => None,
_ if new_hash.starts_with('#') => Some(&new_hash[1..]),
_ => Some(new_hash),
})
}
url.set_fragment(match new_hash {
// If the given value is the empty string,
// then set context objects urls fragment to null and return.
"" => None,
// Let input be the given value with a single leading U+0023 (#) removed, if any.
_ if new_hash.starts_with('#') => Some(&new_hash[1..]),
_ => Some(new_hash),
})
}
fn trim(s: &str) -> &str {

2
third_party/rust/url/src/slicing.rs поставляемый
Просмотреть файл

@ -6,8 +6,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::Url;
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
use Url;
impl Index<RangeFull> for Url {
type Output = str;

337
third_party/rust/url/tests/data.rs поставляемый
Просмотреть файл

@ -8,83 +8,146 @@
//! Data-driven tests
extern crate rustc_test as test;
extern crate serde_json;
extern crate url;
use std::ops::Deref;
use std::str::FromStr;
use serde_json::Value;
use std::str::FromStr;
use url::{quirks, Url};
fn check_invariants(url: &Url) {
url.check_invariants().unwrap();
#[test]
fn urltestdata() {
// Copied form https://github.com/w3c/web-platform-tests/blob/master/url/
let mut json = Value::from_str(include_str!("urltestdata.json"))
.expect("JSON parse error in urltestdata.json");
let mut passed = true;
for entry in json.as_array_mut().unwrap() {
if entry.is_string() {
continue; // ignore comments
}
let base = entry.take_string("base");
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;
continue;
}
};
let url = match (base.join(&input), failure) {
(Ok(url), false) => url,
(Err(_), true) => continue,
(Err(message), false) => {
eprint_failure(
format!(" failed: {}", message),
&format!("parse URL for {:?}", input),
None,
);
passed = false;
continue;
}
(Ok(_), true) => {
eprint_failure(
format!(" failed: expected parse error for URL {:?}", input),
&format!("parse URL for {:?}", input),
None,
);
passed = false;
continue;
}
};
passed &= check_invariants(&url, &format!("invariants for {:?}", input), None);
for &attr in ATTRIBS {
passed &= test_eq_eprint(
entry.take_string(attr),
get(&url, attr),
&format!("{:?} - {}", input, attr),
None,
);
}
if let Some(expected_origin) = entry.take_key("origin").map(|s| s.string()) {
passed &= test_eq_eprint(
expected_origin,
&quirks::origin(&url),
&format!("origin for {:?}", input),
None,
);
}
}
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"))
.expect("JSON parse error in setters_tests.json");
let mut passed = true;
for &attr in ATTRIBS {
if attr == "href" {
continue;
}
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());
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());
passed &= check_invariants(&url, &name, comment_ref);
let _ = set(&mut url, attr, &new_value);
for attr in ATTRIBS {
if let Some(value) = expected.take_key(attr) {
passed &= test_eq_eprint(value.string(), get(&url, attr), &name, comment_ref);
};
}
passed &= check_invariants(&url, &name, comment_ref);
}
}
assert!(passed);
}
fn check_invariants(url: &Url, name: &str, comment: Option<&str>) -> bool {
let mut passed = true;
if let Err(e) = url.check_invariants() {
passed = false;
eprint_failure(
format!(" failed: invariants checked -> {:?}", e),
name,
comment,
);
}
#[cfg(feature = "serde")]
{
let bytes = serde_json::to_vec(url).unwrap();
let new_url: Url = serde_json::from_slice(&bytes).unwrap();
assert_eq!(url, &new_url);
}
}
fn run_parsing(input: &str, base: &str, expected: Result<ExpectedAttributes, ()>) {
let base = match Url::parse(&base) {
Ok(base) => base,
Err(_) if expected.is_err() => return,
Err(message) => panic!("Error parsing base {:?}: {}", base, message),
};
let (url, expected) = match (base.join(&input), expected) {
(Ok(url), Ok(expected)) => (url, expected),
(Err(_), Err(())) => return,
(Err(message), Ok(_)) => panic!("Error parsing URL {:?}: {}", input, message),
(Ok(_), Err(())) => panic!("Expected a parse error for URL {:?}", input),
};
check_invariants(&url);
macro_rules! assert_eq {
($expected: expr, $got: expr) => {{
let expected = $expected;
let got = $got;
assert!(
expected == got,
"\n{:?}\n!= {}\n{:?}\nfor URL {:?}\n",
got,
stringify!($expected),
expected,
url
);
}};
passed &= test_eq_eprint(url.to_string(), &new_url.to_string(), name, comment);
}
macro_rules! assert_attributes {
($($attr: ident)+) => {
{
$(
assert_eq!(expected.$attr, quirks::$attr(&url));
)+;
}
}
}
assert_attributes!(href protocol username password host hostname port pathname search hash);
if let Some(expected_origin) = expected.origin {
assert_eq!(expected_origin, quirks::origin(&url));
}
}
struct ExpectedAttributes {
href: String,
origin: Option<String>,
protocol: String,
username: String,
password: String,
host: String,
hostname: String,
port: String,
pathname: String,
search: String,
hash: String,
passed
}
trait JsonExt {
@ -111,100 +174,60 @@ impl JsonExt for Value {
}
}
fn collect_parsing<F: FnMut(String, test::TestFn)>(add_test: &mut F) {
// Copied form https://github.com/w3c/web-platform-tests/blob/master/url/
let mut json = Value::from_str(include_str!("urltestdata.json"))
.expect("JSON parse error in urltestdata.json");
for entry in json.as_array_mut().unwrap() {
if entry.is_string() {
continue; // ignore comments
}
let base = entry.take_string("base");
let input = entry.take_string("input");
let expected = if entry.take_key("failure").is_some() {
Err(())
} else {
Ok(ExpectedAttributes {
href: entry.take_string("href"),
origin: entry.take_key("origin").map(|s| s.string()),
protocol: entry.take_string("protocol"),
username: entry.take_string("username"),
password: entry.take_string("password"),
host: entry.take_string("host"),
hostname: entry.take_string("hostname"),
port: entry.take_string("port"),
pathname: entry.take_string("pathname"),
search: entry.take_string("search"),
hash: entry.take_string("hash"),
})
};
add_test(
format!("{:?} @ base {:?}", input, base),
test::TestFn::dyn_test_fn(move || run_parsing(&input, &base, expected)),
);
fn get<'a>(url: &'a Url, attr: &str) -> &'a str {
match attr {
"href" => quirks::href(url),
"protocol" => quirks::protocol(url),
"username" => quirks::username(url),
"password" => quirks::password(url),
"hostname" => quirks::hostname(url),
"host" => quirks::host(url),
"port" => quirks::port(url),
"pathname" => quirks::pathname(url),
"search" => quirks::search(url),
"hash" => quirks::hash(url),
_ => unreachable!(),
}
}
fn collect_setters<F>(add_test: &mut F)
where
F: FnMut(String, test::TestFn),
{
let mut json = Value::from_str(include_str!("setters_tests.json"))
.expect("JSON parse error in setters_tests.json");
macro_rules! setter {
($attr: expr, $setter: ident) => {{
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())
.unwrap_or(String::new());
let href = test.take_string("href");
let new_value = test.take_string("new_value");
let name = format!("{:?}.{} = {:?} {}", href, $attr, new_value, comment);
let mut expected = test.take_key("expected").unwrap();
add_test(name, test::TestFn::dyn_test_fn(move || {
let mut url = Url::parse(&href).unwrap();
check_invariants(&url);
let _ = quirks::$setter(&mut url, &new_value);
assert_attributes!(url, expected,
href protocol username password host hostname port pathname search hash);
check_invariants(&url);
}))
}
}}
}
macro_rules! assert_attributes {
($url: expr, $expected: expr, $($attr: ident)+) => {
$(
if let Some(value) = $expected.take_key(stringify!($attr)) {
assert_eq!(quirks::$attr(&$url), value.string())
}
)+
}
}
setter!("protocol", set_protocol);
setter!("username", set_username);
setter!("password", set_password);
setter!("hostname", set_hostname);
setter!("host", set_host);
setter!("port", set_port);
setter!("pathname", set_pathname);
setter!("search", set_search);
setter!("hash", set_hash);
#[allow(clippy::unit_arg)]
fn set<'a>(url: &'a mut Url, attr: &str, new: &str) {
let _ = match attr {
"protocol" => quirks::set_protocol(url, new),
"username" => quirks::set_username(url, new),
"password" => quirks::set_password(url, new),
"hostname" => quirks::set_hostname(url, new),
"host" => quirks::set_host(url, new),
"port" => quirks::set_port(url, new),
"pathname" => Ok(quirks::set_pathname(url, new)),
"search" => Ok(quirks::set_search(url, new)),
"hash" => Ok(quirks::set_hash(url, new)),
_ => unreachable!(),
};
}
fn main() {
let mut tests = Vec::new();
{
let mut add_one = |name: String, run: test::TestFn| {
tests.push(test::TestDescAndFn {
desc: test::TestDesc::new(test::DynTestName(name)),
testfn: run,
})
};
collect_parsing(&mut add_one);
collect_setters(&mut add_one);
fn test_eq_eprint(expected: String, actual: &str, name: &str, comment: Option<&str>) -> bool {
if expected == actual {
return true;
}
test::test_main(&std::env::args().collect::<Vec<_>>(), tests)
eprint_failure(
format!("expected: {}\n actual: {}", expected, actual),
name,
comment,
);
false
}
fn eprint_failure(err: String, name: &str, comment: Option<&str>) {
eprintln!(" test: {}\n{}", name, err);
if let Some(comment) = comment {
eprintln!("{}\n", comment);
} else {
eprintln!();
}
}
const ATTRIBS: &[&str] = &[
"href", "protocol", "username", "password", "host", "hostname", "port", "pathname", "search",
"hash",
];

443
third_party/rust/url/tests/setters_tests.json поставляемый
Просмотреть файл

@ -1,5 +1,6 @@
{
"comment": [
"AS OF https://github.com/jsdom/whatwg-url/commit/35f04dfd3048cf6362f4398745bb13375c5020c2",
"## Tests for setters of https://url.spec.whatwg.org/#urlutils-members",
"",
"This file contains a JSON object.",
@ -27,7 +28,7 @@
"href": "a://example.net",
"new_value": "",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
@ -35,16 +36,24 @@
"href": "a://example.net",
"new_value": "b",
"expected": {
"href": "b://example.net/",
"href": "b://example.net",
"protocol": "b:"
}
},
{
"href": "javascript:alert(1)",
"new_value": "defuse",
"expected": {
"href": "defuse:alert(1)",
"protocol": "defuse:"
}
},
{
"comment": "Upper-case ASCII is lower-cased",
"href": "a://example.net",
"new_value": "B",
"expected": {
"href": "b://example.net/",
"href": "b://example.net",
"protocol": "b:"
}
},
@ -53,7 +62,7 @@
"href": "a://example.net",
"new_value": "é",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
@ -62,7 +71,7 @@
"href": "a://example.net",
"new_value": "0b",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
@ -71,7 +80,7 @@
"href": "a://example.net",
"new_value": "+b",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
@ -79,7 +88,7 @@
"href": "a://example.net",
"new_value": "bC0+-.",
"expected": {
"href": "bc0+-.://example.net/",
"href": "bc0+-.://example.net",
"protocol": "bc0+-.:"
}
},
@ -88,7 +97,7 @@
"href": "a://example.net",
"new_value": "b,c",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
@ -97,10 +106,35 @@
"href": "a://example.net",
"new_value": "bé",
"expected": {
"href": "a://example.net/",
"href": "a://example.net",
"protocol": "a:"
}
},
{
"comment": "Cant switch from URL containing username/password/port to file",
"href": "http://test@example.net",
"new_value": "file",
"expected": {
"href": "http://test@example.net/",
"protocol": "http:"
}
},
{
"href": "gopher://example.net:1234",
"new_value": "file",
"expected": {
"href": "gopher://example.net:1234",
"protocol": "gopher:"
}
},
{
"href": "wss://x:x@example.net:1234",
"new_value": "file",
"expected": {
"href": "wss://x:x@example.net:1234/",
"protocol": "wss:"
}
},
{
"comment": "Cant switch from file URL with no host",
"href": "file://localhost/",
@ -127,12 +161,36 @@
}
},
{
"comment": "Spec deviation: from special scheme to not is not problematic. https://github.com/whatwg/url/issues/104",
"comment": "Cant switch from special scheme to non-special",
"href": "http://example.net",
"new_value": "b",
"expected": {
"href": "b://example.net/",
"protocol": "b:"
"href": "http://example.net/",
"protocol": "http:"
}
},
{
"href": "file://hi/path",
"new_value": "s",
"expected": {
"href": "file://hi/path",
"protocol": "file:"
}
},
{
"href": "https://example.net",
"new_value": "s",
"expected": {
"href": "https://example.net/",
"protocol": "https:"
}
},
{
"href": "ftp://example.net",
"new_value": "test",
"expected": {
"href": "ftp://example.net/",
"protocol": "ftp:"
}
},
{
@ -145,12 +203,44 @@
}
},
{
"comment": "Spec deviation: from non-special scheme with a host to special is not problematic. https://github.com/whatwg/url/issues/104",
"comment": "Cant switch from non-special scheme to special",
"href": "ssh://me@example.net",
"new_value": "http",
"expected": {
"href": "http://me@example.net/",
"protocol": "http:"
"href": "ssh://me@example.net",
"protocol": "ssh:"
}
},
{
"href": "ssh://me@example.net",
"new_value": "https",
"expected": {
"href": "ssh://me@example.net",
"protocol": "ssh:"
}
},
{
"href": "ssh://me@example.net",
"new_value": "file",
"expected": {
"href": "ssh://me@example.net",
"protocol": "ssh:"
}
},
{
"href": "ssh://example.net",
"new_value": "file",
"expected": {
"href": "ssh://example.net",
"protocol": "ssh:"
}
},
{
"href": "nonsense:///test",
"new_value": "https",
"expected": {
"href": "nonsense:///test",
"protocol": "nonsense:"
}
},
{
@ -170,6 +260,16 @@
"href": "view-source+data:text/html,<p>Test",
"protocol": "view-source+data:"
}
},
{
"comment": "Port is set to null if it is the default for new scheme.",
"href": "http://foo.com:443/",
"new_value": "https",
"expected": {
"href": "https://foo.com/",
"protocol": "https:",
"port": ""
}
}
],
"username": [
@ -266,14 +366,6 @@
"username": ""
}
},
{
"href": "file://test/",
"new_value": "test",
"expected": {
"href": "file://test/",
"username": ""
}
},
{
"href": "javascript://x/",
"new_value": "wario",
@ -281,6 +373,14 @@
"href": "javascript://wario@x/",
"username": "wario"
}
},
{
"href": "file://test/",
"new_value": "test",
"expected": {
"href": "file://test/",
"username": ""
}
}
],
"password": [
@ -369,14 +469,6 @@
"password": ""
}
},
{
"href": "file://test/",
"new_value": "test",
"expected": {
"href": "file://test/",
"password": ""
}
},
{
"href": "javascript://x/",
"new_value": "bowser",
@ -384,9 +476,27 @@
"href": "javascript://:bowser@x/",
"password": "bowser"
}
},
{
"href": "file://test/",
"new_value": "test",
"expected": {
"href": "file://test/",
"password": ""
}
}
],
"host": [
{
"comment": "Non-special scheme",
"href": "sc://x/",
"new_value": "\u0000",
"expected": {
"href": "sc://x/",
"host": "x",
"hostname": "x"
}
},
{
"href": "sc://x/",
"new_value": "\u0009",
@ -414,6 +524,15 @@
"hostname": ""
}
},
{
"href": "sc://x/",
"new_value": " ",
"expected": {
"href": "sc://x/",
"host": "x",
"hostname": "x"
}
},
{
"href": "sc://x/",
"new_value": "#",
@ -459,6 +578,16 @@
"hostname": "%C3%9F"
}
},
{
"comment": "IDNA Nontransitional_Processing",
"href": "https://x/",
"new_value": "ß",
"expected": {
"href": "https://xn--zca/",
"host": "xn--zca",
"hostname": "xn--zca"
}
},
{
"comment": "Cannot-be-a-base means no host",
"href": "mailto:me@example.net",
@ -469,7 +598,7 @@
}
},
{
"comment": "Cannot-be-a-base means no password",
"comment": "Cannot-be-a-base means no host",
"href": "data:text/plain,Stuff",
"new_value": "example.net",
"expected": {
@ -499,14 +628,14 @@
}
},
{
"comment": "Port number is removed if empty in the new value: https://github.com/whatwg/url/pull/113",
"comment": "Port number is unchanged if not specified",
"href": "http://example.net:8080",
"new_value": "example.com:",
"expected": {
"href": "http://example.com/",
"host": "example.com",
"href": "http://example.com:8080/",
"host": "example.com:8080",
"hostname": "example.com",
"port": ""
"port": "8080"
}
},
{
@ -558,6 +687,17 @@
"port": "2"
}
},
{
"comment": "IPv6 literal address with port, crbug.com/1012416",
"href": "http://example.net",
"new_value": "[2001:db8::2]:4002",
"expected": {
"href": "http://[2001:db8::2]:4002/",
"host": "[2001:db8::2]:4002",
"hostname": "[2001:db8::2]",
"port": "4002"
}
},
{
"comment": "Default port number is removed",
"href": "http://example.net",
@ -591,6 +731,17 @@
"port": "80"
}
},
{
"comment": "Port number is removed if new port is scheme default and existing URL has a non-default port",
"href": "http://example.net:8080",
"new_value": "example.com:80",
"expected": {
"href": "http://example.com/",
"host": "example.com",
"hostname": "example.com",
"port": ""
}
},
{
"comment": "Stuff after a / delimiter is ignored",
"href": "http://example.net/path",
@ -790,9 +941,59 @@
"host": "example.net",
"hostname": "example.net"
}
},
{
"href": "file://y/",
"new_value": "x:123",
"expected": {
"href": "file://y/",
"host": "y",
"hostname": "y",
"port": ""
}
},
{
"href": "file://y/",
"new_value": "loc%41lhost",
"expected": {
"href": "file:///",
"host": "",
"hostname": "",
"port": ""
}
},
{
"href": "sc://test@test/",
"new_value": "",
"expected": {
"href": "sc://test@test/",
"host": "test",
"hostname": "test",
"username": "test"
}
},
{
"href": "sc://test:12/",
"new_value": "",
"expected": {
"href": "sc://test:12/",
"host": "test:12",
"hostname": "test",
"port": "12"
}
}
],
"hostname": [
{
"comment": "Non-special scheme",
"href": "sc://x/",
"new_value": "\u0000",
"expected": {
"href": "sc://x/",
"host": "x",
"hostname": "x"
}
},
{
"href": "sc://x/",
"new_value": "\u0009",
@ -820,6 +1021,15 @@
"hostname": ""
}
},
{
"href": "sc://x/",
"new_value": " ",
"expected": {
"href": "sc://x/",
"host": "x",
"hostname": "x"
}
},
{
"href": "sc://x/",
"new_value": "#",
@ -866,7 +1076,7 @@
}
},
{
"comment": "Cannot-be-a-base means no password",
"comment": "Cannot-be-a-base means no host",
"href": "data:text/plain,Stuff",
"new_value": "example.net",
"expected": {
@ -1055,6 +1265,46 @@
"host": "example.net",
"hostname": "example.net"
}
},
{
"href": "file://y/",
"new_value": "x:123",
"expected": {
"href": "file://y/",
"host": "y",
"hostname": "y",
"port": ""
}
},
{
"href": "file://y/",
"new_value": "loc%41lhost",
"expected": {
"href": "file:///",
"host": "",
"hostname": "",
"port": ""
}
},
{
"href": "sc://test@test/",
"new_value": "",
"expected": {
"href": "sc://test@test/",
"host": "test",
"hostname": "test",
"username": "test"
}
},
{
"href": "sc://test:12/",
"new_value": "",
"expected": {
"href": "sc://test:12/",
"host": "test:12",
"hostname": "test",
"port": "12"
}
}
],
"port": [
@ -1324,12 +1574,12 @@
}
},
{
"comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed. Leading or training C0 controls and space are removed.",
"comment": "UTF-8 percent encoding with the default encode set. Tabs and newlines are removed.",
"href": "a:/",
"new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"expected": {
"href": "a:/!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9",
"pathname": "/!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
"href": "a:/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9",
"pathname": "/%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E%3F@AZ[\\]^_%60az%7B|%7D~%7F%C2%80%C2%81%C3%89%C3%A9"
}
},
{
@ -1376,6 +1626,33 @@
"href": "sc://example.net/%23",
"pathname": "/%23"
}
},
{
"comment": "File URLs and (back)slashes",
"href": "file://monkey/",
"new_value": "\\\\",
"expected": {
"href": "file://monkey/",
"pathname": "/"
}
},
{
"comment": "File URLs and (back)slashes",
"href": "file:///unicorn",
"new_value": "//\\/",
"expected": {
"href": "file:///",
"pathname": "/"
}
},
{
"comment": "File URLs and (back)slashes",
"href": "file:///unicorn",
"new_value": "//monkey/..//",
"expected": {
"href": "file:///",
"pathname": "/"
}
}
],
"search": [
@ -1444,12 +1721,12 @@
}
},
{
"comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed. Leading or training C0 controls and space are removed.",
"comment": "UTF-8 percent encoding with the query encode set. Tabs and newlines are removed.",
"href": "a:/",
"new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"expected": {
"href": "a:/?!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
"search": "?!%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
"href": "a:/?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
"search": "?%00%01%1F%20!%22%23$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
}
},
{
@ -1512,12 +1789,70 @@
}
},
{
"comment": "Simple percent-encoding; nuls, tabs, and newlines are removed",
"href": "a:/",
"new_value": "\u0000\u0001\t\n\r\u001f !\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"href": "http://example.net",
"new_value": "#foo bar",
"expected": {
"href": "a:/#!%01%1F !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
"hash": "#!%01%1F !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
"href": "http://example.net/#foo%20bar",
"hash": "#foo%20bar"
}
},
{
"href": "http://example.net",
"new_value": "#foo\"bar",
"expected": {
"href": "http://example.net/#foo%22bar",
"hash": "#foo%22bar"
}
},
{
"href": "http://example.net",
"new_value": "#foo<bar",
"expected": {
"href": "http://example.net/#foo%3Cbar",
"hash": "#foo%3Cbar"
}
},
{
"href": "http://example.net",
"new_value": "#foo>bar",
"expected": {
"href": "http://example.net/#foo%3Ebar",
"hash": "#foo%3Ebar"
}
},
{
"href": "http://example.net",
"new_value": "#foo`bar",
"expected": {
"href": "http://example.net/#foo%60bar",
"hash": "#foo%60bar"
}
},
{
"comment": "Simple percent-encoding; tabs and newlines are removed",
"href": "a:/",
"new_value": "\u0000\u0001\t\n\r\u001f !\"#$%&'()*+,-./09:;<=>?@AZ[\\]^_`az{|}~\u007f\u0080\u0081Éé",
"expected": {
"href": "a:/#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9",
"hash": "#%00%01%1F%20!%22#$%&'()*+,-./09:;%3C=%3E?@AZ[\\]^_%60az{|}~%7F%C2%80%C2%81%C3%89%C3%A9"
}
},
{
"comment": "Percent-encode NULLs in fragment",
"href": "http://example.net",
"new_value": "a\u0000b",
"expected": {
"href": "http://example.net/#a%00b",
"hash": "#a%00b"
}
},
{
"comment": "Percent-encode NULLs in fragment",
"href": "non-spec:/",
"new_value": "a\u0000b",
"expected": {
"href": "non-spec:/#a%00b",
"hash": "#a%00b"
}
},
{
@ -1528,6 +1863,14 @@
"href": "http://example.net/#%c3%89t%C3%A9",
"hash": "#%c3%89t%C3%A9"
}
},
{
"href": "javascript:alert(1)",
"new_value": "castle",
"expected": {
"href": "javascript:alert(1)#castle",
"hash": "#castle"
}
}
]
}

584
third_party/rust/url/tests/unit.rs поставляемый
Просмотреть файл

@ -8,14 +8,11 @@
//! Unit tests
extern crate percent_encoding;
extern crate url;
use std::borrow::Cow;
use std::cell::{Cell, RefCell};
use std::net::{Ipv4Addr, Ipv6Addr};
use std::path::{Path, PathBuf};
use url::{form_urlencoded, Host, Url};
use url::{form_urlencoded, Host, Origin, Url};
#[test]
fn size() {
@ -23,6 +20,49 @@ fn size() {
assert_eq!(size_of::<Url>(), size_of::<Option<Url>>());
}
#[test]
fn test_relative() {
let base: Url = "sc://%C3%B1".parse().unwrap();
let url = base.join("/resources/testharness.js").unwrap();
assert_eq!(url.as_str(), "sc://%C3%B1/resources/testharness.js");
}
#[test]
fn test_relative_empty() {
let base: Url = "sc://%C3%B1".parse().unwrap();
let url = base.join("").unwrap();
assert_eq!(url.as_str(), "sc://%C3%B1");
}
#[test]
fn test_set_empty_host() {
let mut base: Url = "moz://foo:bar@servo/baz".parse().unwrap();
base.set_username("").unwrap();
assert_eq!(base.as_str(), "moz://:bar@servo/baz");
base.set_host(None).unwrap();
assert_eq!(base.as_str(), "moz:/baz");
base.set_host(Some("servo")).unwrap();
assert_eq!(base.as_str(), "moz://servo/baz");
}
#[test]
fn test_set_empty_hostname() {
use url::quirks;
let mut base: Url = "moz://foo@servo/baz".parse().unwrap();
assert!(
quirks::set_hostname(&mut base, "").is_err(),
"setting an empty hostname to a url with a username should fail"
);
base = "moz://:pass@servo/baz".parse().unwrap();
assert!(
quirks::set_hostname(&mut base, "").is_err(),
"setting an empty hostname to a url with a password should fail"
);
base = "moz://servo/baz".parse().unwrap();
quirks::set_hostname(&mut base, "").unwrap();
assert_eq!(base.as_str(), "moz:///baz");
}
macro_rules! assert_from_file_path {
($path: expr) => {
assert_from_file_path!($path, $path)
@ -234,6 +274,9 @@ fn host() {
assert_host("http://2..2.3", Host::Domain("2..2.3"));
assert!(Url::parse("http://42.0x1232131").is_err());
assert!(Url::parse("http://192.168.0.257").is_err());
assert_eq!(Host::Domain("foo"), Host::Domain("foo").to_owned());
assert_ne!(Host::Domain("foo"), Host::Domain("bar").to_owned());
}
#[test]
@ -294,7 +337,7 @@ fn test_serialization() {
#[test]
fn test_form_urlencoded() {
let pairs: &[(Cow<str>, Cow<str>)] = &[
let pairs: &[(Cow<'_, str>, Cow<'_, str>)] = &[
("foo".into(), "é&".into()),
("bar".into(), "".into()),
("foo".into(), "#".into()),
@ -315,8 +358,9 @@ fn test_form_serialize() {
.append_pair("foo", "é&")
.append_pair("bar", "")
.append_pair("foo", "#")
.append_key_only("json")
.finish();
assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23");
assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23&json");
}
#[test]
@ -324,8 +368,9 @@ fn form_urlencoded_encoding_override() {
let encoded = form_urlencoded::Serializer::new(String::new())
.encoding_override(Some(&|s| s.as_bytes().to_ascii_uppercase().into()))
.append_pair("foo", "bar")
.append_key_only("xml")
.finish();
assert_eq!(encoded, "FOO=BAR");
assert_eq!(encoded, "FOO=BAR&XML");
}
#[test]
@ -413,9 +458,9 @@ fn test_set_host() {
assert_eq!(url.as_str(), "foobar:/hello");
let mut url = Url::parse("foo://ș").unwrap();
assert_eq!(url.as_str(), "foo://%C8%99/");
assert_eq!(url.as_str(), "foo://%C8%99");
url.set_host(Some("goșu.ro")).unwrap();
assert_eq!(url.as_str(), "foo://go%C8%99u.ro/");
assert_eq!(url.as_str(), "foo://go%C8%99u.ro");
}
#[test]
@ -472,6 +517,209 @@ fn test_origin_hash() {
assert_ne!(hash(&opaque_origin), hash(&other_opaque_origin));
}
#[test]
fn test_origin_blob_equality() {
let origin = &Url::parse("http://example.net/").unwrap().origin();
let blob_origin = &Url::parse("blob:http://example.net/").unwrap().origin();
assert_eq!(origin, blob_origin);
}
#[test]
fn test_origin_opaque() {
assert!(!Origin::new_opaque().is_tuple());
assert!(!&Url::parse("blob:malformed//").unwrap().origin().is_tuple())
}
#[test]
fn test_origin_unicode_serialization() {
let data = [
("http://😅.com", "http://😅.com"),
("ftp://😅:🙂@🙂.com", "ftp://🙂.com"),
("https://user@😅.com", "https://😅.com"),
("http://😅.🙂:40", "http://😅.🙂:40"),
];
for &(unicode_url, expected_serialization) in &data {
let origin = Url::parse(unicode_url).unwrap().origin();
assert_eq!(origin.unicode_serialization(), *expected_serialization);
}
let ascii_origins = [
Url::parse("http://example.net/").unwrap().origin(),
Url::parse("http://example.net:80/").unwrap().origin(),
Url::parse("http://example.net:81/").unwrap().origin(),
Url::parse("http://example.net").unwrap().origin(),
Url::parse("http://example.net/hello").unwrap().origin(),
Url::parse("https://example.net").unwrap().origin(),
Url::parse("ftp://example.net").unwrap().origin(),
Url::parse("file://example.net").unwrap().origin(),
Url::parse("http://user@example.net/").unwrap().origin(),
Url::parse("http://user:pass@example.net/")
.unwrap()
.origin(),
Url::parse("http://127.0.0.1").unwrap().origin(),
];
for ascii_origin in &ascii_origins {
assert_eq!(
ascii_origin.ascii_serialization(),
ascii_origin.unicode_serialization()
);
}
}
#[test]
fn test_socket_addrs() {
use std::net::ToSocketAddrs;
let data = [
("https://127.0.0.1/", "127.0.0.1", 443),
("https://127.0.0.1:9742/", "127.0.0.1", 9742),
("custom-protocol://127.0.0.1:9742/", "127.0.0.1", 9742),
("custom-protocol://127.0.0.1/", "127.0.0.1", 9743),
("https://[::1]/", "::1", 443),
("https://[::1]:9742/", "::1", 9742),
("custom-protocol://[::1]:9742/", "::1", 9742),
("custom-protocol://[::1]/", "::1", 9743),
("https://localhost/", "localhost", 443),
("https://localhost:9742/", "localhost", 9742),
("custom-protocol://localhost:9742/", "localhost", 9742),
("custom-protocol://localhost/", "localhost", 9743),
];
for (url_string, host, port) in &data {
let url = url::Url::parse(url_string).unwrap();
let addrs = url
.socket_addrs(|| match url.scheme() {
"custom-protocol" => Some(9743),
_ => None,
})
.unwrap();
assert_eq!(
Some(addrs[0]),
(*host, *port).to_socket_addrs().unwrap().next()
);
}
}
#[test]
fn test_no_base_url() {
let mut no_base_url = Url::parse("mailto:test@example.net").unwrap();
assert!(no_base_url.cannot_be_a_base());
assert!(no_base_url.path_segments().is_none());
assert!(no_base_url.path_segments_mut().is_err());
assert!(no_base_url.set_host(Some("foo")).is_err());
assert!(no_base_url
.set_ip_host("127.0.0.1".parse().unwrap())
.is_err());
no_base_url.set_path("/foo");
assert_eq!(no_base_url.path(), "%2Ffoo");
}
#[test]
fn test_domain() {
let url = Url::parse("https://127.0.0.1/").unwrap();
assert_eq!(url.domain(), None);
let url = Url::parse("mailto:test@example.net").unwrap();
assert_eq!(url.domain(), None);
let url = Url::parse("https://example.com/").unwrap();
assert_eq!(url.domain(), Some("example.com"));
}
#[test]
fn test_query() {
let url = Url::parse("https://example.com/products?page=2#fragment").unwrap();
assert_eq!(url.query(), Some("page=2"));
assert_eq!(
url.query_pairs().next(),
Some((Cow::Borrowed("page"), Cow::Borrowed("2")))
);
let url = Url::parse("https://example.com/products").unwrap();
assert!(url.query().is_none());
assert_eq!(url.query_pairs().count(), 0);
let url = Url::parse("https://example.com/?country=español").unwrap();
assert_eq!(url.query(), Some("country=espa%C3%B1ol"));
assert_eq!(
url.query_pairs().next(),
Some((Cow::Borrowed("country"), Cow::Borrowed("español")))
);
let url = Url::parse("https://example.com/products?page=2&sort=desc").unwrap();
assert_eq!(url.query(), Some("page=2&sort=desc"));
let mut pairs = url.query_pairs();
assert_eq!(pairs.count(), 2);
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("page"), Cow::Borrowed("2")))
);
assert_eq!(
pairs.next(),
Some((Cow::Borrowed("sort"), Cow::Borrowed("desc")))
);
}
#[test]
fn test_fragment() {
let url = Url::parse("https://example.com/#fragment").unwrap();
assert_eq!(url.fragment(), Some("fragment"));
let url = Url::parse("https://example.com/").unwrap();
assert_eq!(url.fragment(), None);
}
#[test]
fn test_set_ip_host() {
let mut url = Url::parse("http://example.com").unwrap();
url.set_ip_host("127.0.0.1".parse().unwrap()).unwrap();
assert_eq!(url.host_str(), Some("127.0.0.1"));
url.set_ip_host("::1".parse().unwrap()).unwrap();
assert_eq!(url.host_str(), Some("[::1]"));
}
#[test]
fn test_set_href() {
use url::quirks::set_href;
let mut url = Url::parse("https://existing.url").unwrap();
assert!(set_href(&mut url, "mal//formed").is_err());
assert!(set_href(
&mut url,
"https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment"
)
.is_ok());
assert_eq!(
url,
Url::parse("https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment")
.unwrap()
);
}
#[test]
fn test_domain_encoding_quirks() {
use url::quirks::{domain_to_ascii, domain_to_unicode};
let data = [
("http://example.com", "", ""),
("😅.🙂", "xn--j28h.xn--938h", "😅.🙂"),
("example.com", "example.com", "example.com"),
("mailto:test@example.net", "", ""),
];
for url in &data {
assert_eq!(domain_to_ascii(url.0), url.1);
assert_eq!(domain_to_unicode(url.0), url.2);
}
}
#[test]
fn test_windows_unc_path() {
if !cfg!(windows) {
@ -536,6 +784,38 @@ fn test_syntax_violation_callback_lifetimes() {
assert_eq!(violation.take(), Some(Backslash));
}
#[test]
fn test_syntax_violation_callback_types() {
use url::SyntaxViolation::*;
let data = [
("http://mozilla.org/\\foo", Backslash, "backslash"),
(" http://mozilla.org", C0SpaceIgnored, "leading or trailing control or space character are ignored in URLs"),
("http://user:pass@mozilla.org", EmbeddedCredentials, "embedding authentication information (username or password) in an URL is not recommended"),
("http:///mozilla.org", ExpectedDoubleSlash, "expected //"),
("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/%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")
];
for test_case in &data {
let violation = Cell::new(None);
Url::options()
.syntax_violation_callback(Some(&|v| violation.set(Some(v))))
.parse(test_case.0)
.unwrap();
let v = violation.take();
assert_eq!(v, Some(test_case.1));
assert_eq!(v.unwrap().description(), test_case.2);
assert_eq!(v.unwrap().to_string(), test_case.2);
}
}
#[test]
fn test_options_reuse() {
use url::SyntaxViolation::*;
@ -550,3 +830,289 @@ fn test_options_reuse() {
assert_eq!(url.as_str(), "http://mozilla.org/sub/path");
assert_eq!(*violations.borrow(), vec!(ExpectedDoubleSlash, Backslash));
}
/// https://github.com/servo/rust-url/issues/505
#[cfg(windows)]
#[test]
fn test_url_from_file_path() {
use std::path::PathBuf;
use url::Url;
let p = PathBuf::from("c:///");
let u = Url::from_file_path(p).unwrap();
let path = u.to_file_path().unwrap();
assert_eq!("C:\\", path.to_str().unwrap());
}
/// https://github.com/servo/rust-url/issues/505
#[cfg(not(windows))]
#[test]
fn test_url_from_file_path() {
use std::path::PathBuf;
use url::Url;
let p = PathBuf::from("/c:/");
let u = Url::from_file_path(p).unwrap();
let path = u.to_file_path().unwrap();
assert_eq!("/c:/", path.to_str().unwrap());
}
#[test]
fn test_non_special_path() {
let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
db_url.set_path("diesel_foo");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/diesel_foo");
assert_eq!(db_url.path(), "/diesel_foo");
}
#[test]
fn test_non_special_path2() {
let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
db_url.set_path("");
assert_eq!(db_url.path(), "");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost");
db_url.set_path("foo");
assert_eq!(db_url.path(), "/foo");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
db_url.set_path("/bar");
assert_eq!(db_url.path(), "/bar");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/bar");
}
#[test]
fn test_non_special_path3() {
let mut db_url = url::Url::parse("postgres://postgres@localhost/").unwrap();
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
db_url.set_path("/");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/");
assert_eq!(db_url.path(), "/");
db_url.set_path("/foo");
assert_eq!(db_url.as_str(), "postgres://postgres@localhost/foo");
assert_eq!(db_url.path(), "/foo");
}
#[test]
fn test_set_scheme_to_file_with_host() {
let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
let result = url.set_scheme("file");
assert_eq!(url.to_string(), "http://localhost:6767/foo/bar");
assert_eq!(result, Err(()));
}
#[test]
fn no_panic() {
let mut url = Url::parse("arhttpsps:/.//eom/dae.com/\\\\t\\:").unwrap();
url::quirks::set_hostname(&mut url, "//eom/datcom/\\\\t\\://eom/data.cs").unwrap();
}
#[test]
fn pop_if_empty_in_bounds() {
let mut url = Url::parse("m://").unwrap();
let mut segments = url.path_segments_mut().unwrap();
segments.pop_if_empty();
segments.pop();
}
#[test]
fn test_slicing() {
use url::Position::*;
#[derive(Default)]
struct ExpectedSlices<'a> {
full: &'a str,
scheme: &'a str,
username: &'a str,
password: &'a str,
host: &'a str,
port: &'a str,
path: &'a str,
query: &'a str,
fragment: &'a str,
}
let data = [
ExpectedSlices {
full: "https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment",
scheme: "https",
username: "user",
password: "pass",
host: "domain.com",
port: "9742",
path: "/path/file.ext",
query: "key=val&key2=val2",
fragment: "fragment",
},
ExpectedSlices {
full: "https://domain.com:9742/path/file.ext#fragment",
scheme: "https",
host: "domain.com",
port: "9742",
path: "/path/file.ext",
fragment: "fragment",
..Default::default()
},
ExpectedSlices {
full: "https://domain.com:9742/path/file.ext",
scheme: "https",
host: "domain.com",
port: "9742",
path: "/path/file.ext",
..Default::default()
},
ExpectedSlices {
full: "blob:blob-info",
scheme: "blob",
path: "blob-info",
..Default::default()
},
];
for expected_slices in &data {
let url = Url::parse(expected_slices.full).unwrap();
assert_eq!(&url[..], expected_slices.full);
assert_eq!(&url[BeforeScheme..AfterScheme], expected_slices.scheme);
assert_eq!(
&url[BeforeUsername..AfterUsername],
expected_slices.username
);
assert_eq!(
&url[BeforePassword..AfterPassword],
expected_slices.password
);
assert_eq!(&url[BeforeHost..AfterHost], expected_slices.host);
assert_eq!(&url[BeforePort..AfterPort], expected_slices.port);
assert_eq!(&url[BeforePath..AfterPath], expected_slices.path);
assert_eq!(&url[BeforeQuery..AfterQuery], expected_slices.query);
assert_eq!(
&url[BeforeFragment..AfterFragment],
expected_slices.fragment
);
assert_eq!(&url[..AfterFragment], expected_slices.full);
}
}
#[test]
fn test_make_relative() {
let tests = [
(
"http://127.0.0.1:8080/test",
"http://127.0.0.1:8080/test",
"",
),
(
"http://127.0.0.1:8080/test",
"http://127.0.0.1:8080/test/",
"test/",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test",
"../test",
),
(
"http://127.0.0.1:8080/",
"http://127.0.0.1:8080/?foo=bar#123",
"?foo=bar#123",
),
(
"http://127.0.0.1:8080/",
"http://127.0.0.1:8080/test/video",
"test/video",
),
(
"http://127.0.0.1:8080/test",
"http://127.0.0.1:8080/test/video",
"test/video",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test/video",
"video",
),
(
"http://127.0.0.1:8080/test",
"http://127.0.0.1:8080/test2/video",
"test2/video",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test2/video",
"../test2/video",
),
(
"http://127.0.0.1:8080/test/bla",
"http://127.0.0.1:8080/test2/video",
"../test2/video",
),
(
"http://127.0.0.1:8080/test/bla/",
"http://127.0.0.1:8080/test2/video",
"../../test2/video",
),
(
"http://127.0.0.1:8080/test/?foo=bar#123",
"http://127.0.0.1:8080/test/video",
"video",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test/video?baz=meh#456",
"video?baz=meh#456",
),
(
"http://127.0.0.1:8080/test",
"http://127.0.0.1:8080/test?baz=meh#456",
"?baz=meh#456",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test?baz=meh#456",
"../test?baz=meh#456",
),
(
"http://127.0.0.1:8080/test/",
"http://127.0.0.1:8080/test/?baz=meh#456",
"?baz=meh#456",
),
(
"http://127.0.0.1:8080/test/?foo=bar#123",
"http://127.0.0.1:8080/test/video?baz=meh#456",
"video?baz=meh#456",
),
];
for (base, uri, relative) in &tests {
let base_uri = url::Url::parse(base).unwrap();
let relative_uri = url::Url::parse(uri).unwrap();
let make_relative = base_uri.make_relative(&relative_uri).unwrap();
assert_eq!(
make_relative, *relative,
"base: {}, uri: {}, relative: {}",
base, uri, relative
);
assert_eq!(
base_uri.join(&relative).unwrap().as_str(),
*uri,
"base: {}, uri: {}, relative: {}",
base,
uri,
relative
);
}
let error_tests = [
("http://127.0.0.1:8080/", "https://127.0.0.1:8080/test/"),
("http://127.0.0.1:8080/", "http://127.0.0.1:8081/test/"),
("http://127.0.0.1:8080/", "http://127.0.0.2:8080/test/"),
("mailto:a@example.com", "mailto:b@example.com"),
];
for (base, uri) in &error_tests {
let base_uri = url::Url::parse(base).unwrap();
let relative_uri = url::Url::parse(uri).unwrap();
let make_relative = base_uri.make_relative(&relative_uri);
assert_eq!(make_relative, None, "base: {}, uri: {}", base, uri);
}
}

991
third_party/rust/url/tests/urltestdata.json поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -215,7 +215,7 @@ impl<'s> Store<'s> {
let title = String::from_utf16(&*raw_title)?;
url.map(|url| Content::Bookmark {
title,
url_href: url.into_string(),
url_href: url.into(),
})
}
Kind::Folder | Kind::Livemark => {