Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/url/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/url/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"Cargo.toml":"33e77fef5d9e5592daeff71b551f983f19ddd9d31a7c002e642a3c40d8b08123","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"76e972ac0f4ddb116e86e10100132a783931a596e7b9872eaa31be15cd4d751d","README.md":"71b01ec6f2f4ce47235ee430ba0c41afac563403a9dbcda23a584c3e915395ac","src/host.rs":"5e25476aaec0153b64d35b53940a72a1ec58e29a0e1fde36944f52eeb945c5f6","src/lib.rs":"e017abe9c33881a96b5daafeee65b1814b4418f5fb0c96d0aaea65a14c9292c9","src/origin.rs":"19a4b451e8615bfef7239d2fc719c489398fe5044edb0df7c84b54eef4ceba1b","src/parser.rs":"5427cd15caedc8e3c1418cc576a7263e96df26a51ad3ce88f8c32d3eb7d6dd2c","src/path_segments.rs":"29db87b6902da4ab1ae925b3874afdeff42b8ddfb46356af6a83b86f34e03b14","src/quirks.rs":"c9311e3dd6f701fb4b8e438b3e3960ff6f8c78a67ae763f3640b178f15c60e45","src/slicing.rs":"3b1aaad36ba7e89f50c90d1ceddda1f8ba52a364c153541ac5c9ce54dacb6724","tests/expected_failures.txt":"f222a5e2f7bdfbd724cf7fb8e35e71a0fe1f3ac9c2771919d7ff5ba9e51c5769","tests/setters_tests.json":"a3a4cbd7b798bc2c4d9656dc50be7397a5a5ed1f0b52daa1da1ad654d38c1dcd","tests/unit.rs":"1abe0a410c5078e1ad9de8c93f2f2ae660ddb47b7efaac9047e952457b068ded","tests/urltestdata.json":"58d67bea710d5f46324fe6841df5fd82090fe4ec2d882bc0fc7c1784d4771884","tests/wpt.rs":"6302c008cde6e7c0df8626701cc825731363722d02e35804bb370c385b455145"},"package":"31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"}
|
||||
83
third-party/vendor/url/Cargo.toml
vendored
Normal file
83
third-party/vendor/url/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# 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 are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
rust-version = "1.56"
|
||||
name = "url"
|
||||
version = "2.5.0"
|
||||
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"
|
||||
keywords = [
|
||||
"url",
|
||||
"parser",
|
||||
]
|
||||
categories = [
|
||||
"parser-implementations",
|
||||
"web-programming",
|
||||
"encoding",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/servo/rust-url"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
features = ["serde"]
|
||||
rustdoc-args = ["--generate-link-to-definition"]
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["serde"]
|
||||
|
||||
[[test]]
|
||||
name = "url_wpt"
|
||||
path = "tests/wpt.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "parse_url"
|
||||
path = "benches/parse_url.rs"
|
||||
harness = false
|
||||
|
||||
[dependencies.form_urlencoded]
|
||||
version = "1.2.1"
|
||||
|
||||
[dependencies.idna]
|
||||
version = "0.5.0"
|
||||
|
||||
[dependencies.percent-encoding]
|
||||
version = "2.3.1"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
optional = true
|
||||
|
||||
[dev-dependencies.bencher]
|
||||
version = "0.1"
|
||||
|
||||
[dev-dependencies.serde]
|
||||
version = "1.0"
|
||||
features = ["derive"]
|
||||
|
||||
[dev-dependencies.serde_json]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
debugger_visualizer = []
|
||||
default = []
|
||||
expose_internals = []
|
||||
201
third-party/vendor/url/LICENSE-APACHE
vendored
Normal file
201
third-party/vendor/url/LICENSE-APACHE
vendored
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/vendor/url/LICENSE-MIT
vendored
Normal file
25
third-party/vendor/url/LICENSE-MIT
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2013-2022 The rust-url developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
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.
|
||||
14
third-party/vendor/url/README.md
vendored
Normal file
14
third-party/vendor/url/README.md
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
rust-url
|
||||
========
|
||||
|
||||
[](https://github.com/servo/rust-url/actions?query=workflow%3ACI)
|
||||
[](https://codecov.io/gh/servo/rust-url)
|
||||
[](https://matrix.to/#/#rust-url:mozilla.org)
|
||||
[](LICENSE-MIT)
|
||||
[](LICENSE-APACHE)
|
||||
|
||||
URL library for Rust, based on the [URL Standard](https://url.spec.whatwg.org/).
|
||||
|
||||
[Documentation](https://docs.rs/url/)
|
||||
|
||||
Please see [UPGRADING.md](https://github.com/servo/rust-url/blob/master/UPGRADING.md) if you are upgrading from a previous version.
|
||||
496
third-party/vendor/url/src/host.rs
vendored
Normal file
496
third-party/vendor/url/src/host.rs
vendored
Normal file
|
|
@ -0,0 +1,496 @@
|
|||
// Copyright 2013-2016 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::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 {
|
||||
None,
|
||||
Domain,
|
||||
Ipv4(Ipv4Addr),
|
||||
Ipv6(Ipv6Addr),
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The host name of an URL.
|
||||
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
|
||||
#[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
|
||||
/// a special URL, or percent encoded for non-special URLs. Hosts for
|
||||
/// non-special URLs are also called opaque hosts.
|
||||
Domain(S),
|
||||
|
||||
/// An IPv4 address.
|
||||
/// `Url::host_str` returns the serialization of this address,
|
||||
/// as four decimal integers separated by `.` dots.
|
||||
Ipv4(Ipv4Addr),
|
||||
|
||||
/// An IPv6 address.
|
||||
/// `Url::host_str` returns the serialization of that address between `[` and `]` brackets,
|
||||
/// in the format per [RFC 5952 *A Recommendation
|
||||
/// for IPv6 Address Text Representation*](https://tools.ietf.org/html/rfc5952):
|
||||
/// lowercase hexadecimal with maximal `::` compression.
|
||||
Ipv6(Ipv6Addr),
|
||||
}
|
||||
|
||||
impl<'a> Host<&'a str> {
|
||||
/// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
|
||||
pub fn to_owned(&self) -> Host<String> {
|
||||
match *self {
|
||||
Host::Domain(domain) => Host::Domain(domain.to_owned()),
|
||||
Host::Ipv4(address) => Host::Ipv4(address),
|
||||
Host::Ipv6(address) => Host::Ipv6(address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Host<String> {
|
||||
/// Parse a host: either an IPv6 address in [] square brackets, or a domain.
|
||||
///
|
||||
/// <https://url.spec.whatwg.org/#host-parsing>
|
||||
pub fn parse(input: &str) -> Result<Self, ParseError> {
|
||||
if input.starts_with('[') {
|
||||
if !input.ends_with(']') {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
|
||||
}
|
||||
let domain = percent_decode(input.as_bytes()).decode_utf8_lossy();
|
||||
|
||||
let domain = Self::domain_to_ascii(&domain)?;
|
||||
|
||||
if domain.is_empty() {
|
||||
return Err(ParseError::EmptyHost);
|
||||
}
|
||||
|
||||
let is_invalid_domain_char = |c| {
|
||||
matches!(
|
||||
c,
|
||||
'\0'..='\u{001F}'
|
||||
| ' '
|
||||
| '#'
|
||||
| '%'
|
||||
| '/'
|
||||
| ':'
|
||||
| '<'
|
||||
| '>'
|
||||
| '?'
|
||||
| '@'
|
||||
| '['
|
||||
| '\\'
|
||||
| ']'
|
||||
| '^'
|
||||
| '\u{007F}'
|
||||
| '|'
|
||||
)
|
||||
};
|
||||
|
||||
if domain.find(is_invalid_domain_char).is_some() {
|
||||
Err(ParseError::InvalidDomainCharacter)
|
||||
} else if ends_in_a_number(&domain) {
|
||||
let address = parse_ipv4addr(&domain)?;
|
||||
Ok(Host::Ipv4(address))
|
||||
} else {
|
||||
Ok(Host::Domain(domain))
|
||||
}
|
||||
}
|
||||
|
||||
// <https://url.spec.whatwg.org/#concept-opaque-host-parser>
|
||||
pub fn parse_opaque(input: &str) -> Result<Self, ParseError> {
|
||||
if input.starts_with('[') {
|
||||
if !input.ends_with(']') {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
return parse_ipv6addr(&input[1..input.len() - 1]).map(Host::Ipv6);
|
||||
}
|
||||
|
||||
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(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// convert domain with idna
|
||||
fn domain_to_ascii(domain: &str) -> Result<String, ParseError> {
|
||||
idna::domain_to_ascii(domain).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> fmt::Display for Host<S> {
|
||||
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),
|
||||
Host::Ipv6(ref addr) => {
|
||||
f.write_str("[")?;
|
||||
write_ipv6(addr, f)?;
|
||||
f.write_str("]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
while i < 8 {
|
||||
if i == compress_start {
|
||||
f.write_str(":")?;
|
||||
if i == 0 {
|
||||
f.write_str(":")?;
|
||||
}
|
||||
if compress_end < 8 {
|
||||
i = compress_end;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
write!(f, "{:x}", segments[i as usize])?;
|
||||
if i < 7 {
|
||||
f.write_str(":")?;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#concept-ipv6-serializer step 2 and 3
|
||||
fn longest_zero_sequence(pieces: &[u16; 8]) -> (isize, isize) {
|
||||
let mut longest = -1;
|
||||
let mut longest_length = -1;
|
||||
let mut start = -1;
|
||||
macro_rules! finish_sequence(
|
||||
($end: expr) => {
|
||||
if start >= 0 {
|
||||
let length = $end - start;
|
||||
if length > longest_length {
|
||||
longest = start;
|
||||
longest_length = length;
|
||||
}
|
||||
}
|
||||
};
|
||||
);
|
||||
for i in 0..8 {
|
||||
if pieces[i as usize] == 0 {
|
||||
if start < 0 {
|
||||
start = i;
|
||||
}
|
||||
} else {
|
||||
finish_sequence!(i);
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
finish_sequence!(8);
|
||||
// https://url.spec.whatwg.org/#concept-ipv6-serializer
|
||||
// step 3: ignore lone zeroes
|
||||
if longest_length < 2 {
|
||||
(-1, -2)
|
||||
} else {
|
||||
(longest, longest + longest_length)
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#ends-in-a-number-checker>
|
||||
fn ends_in_a_number(input: &str) -> bool {
|
||||
let mut parts = input.rsplit('.');
|
||||
let last = parts.next().unwrap();
|
||||
let last = if last.is_empty() {
|
||||
if let Some(last) = parts.next() {
|
||||
last
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
last
|
||||
};
|
||||
if !last.is_empty() && last.as_bytes().iter().all(|c| c.is_ascii_digit()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
parse_ipv4number(last).is_ok()
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#ipv4-number-parser>
|
||||
/// Ok(None) means the input is a valid number, but it overflows a `u32`.
|
||||
fn parse_ipv4number(mut input: &str) -> Result<Option<u32>, ()> {
|
||||
if input.is_empty() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut r = 10;
|
||||
if input.starts_with("0x") || input.starts_with("0X") {
|
||||
input = &input[2..];
|
||||
r = 16;
|
||||
} else if input.len() >= 2 && input.starts_with('0') {
|
||||
input = &input[1..];
|
||||
r = 8;
|
||||
}
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Some(0));
|
||||
}
|
||||
|
||||
let valid_number = match r {
|
||||
8 => input.as_bytes().iter().all(|c| (b'0'..=b'7').contains(c)),
|
||||
10 => input.as_bytes().iter().all(|c| c.is_ascii_digit()),
|
||||
16 => input.as_bytes().iter().all(|c| c.is_ascii_hexdigit()),
|
||||
_ => false,
|
||||
};
|
||||
if !valid_number {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
match u32::from_str_radix(input, r) {
|
||||
Ok(num) => Ok(Some(num)),
|
||||
Err(_) => Ok(None), // The only possible error kind here is an integer overflow.
|
||||
// The validity of the chars in the input is checked above.
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#concept-ipv4-parser>
|
||||
fn parse_ipv4addr(input: &str) -> ParseResult<Ipv4Addr> {
|
||||
let mut parts: Vec<&str> = input.split('.').collect();
|
||||
if parts.last() == Some(&"") {
|
||||
parts.pop();
|
||||
}
|
||||
if parts.len() > 4 {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
let mut numbers: Vec<u32> = Vec::new();
|
||||
for part in parts {
|
||||
match parse_ipv4number(part) {
|
||||
Ok(Some(n)) => numbers.push(n),
|
||||
Ok(None) => return Err(ParseError::InvalidIpv4Address), // u32 overflow
|
||||
Err(()) => return Err(ParseError::InvalidIpv4Address),
|
||||
};
|
||||
}
|
||||
let mut ipv4 = numbers.pop().expect("a non-empty list of numbers");
|
||||
// Equivalent to: ipv4 >= 256 ** (4 − numbers.len())
|
||||
if ipv4 > u32::max_value() >> (8 * numbers.len() as u32) {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
if numbers.iter().any(|x| *x > 255) {
|
||||
return Err(ParseError::InvalidIpv4Address);
|
||||
}
|
||||
for (counter, n) in numbers.iter().enumerate() {
|
||||
ipv4 += n << (8 * (3 - counter as u32))
|
||||
}
|
||||
Ok(Ipv4Addr::from(ipv4))
|
||||
}
|
||||
|
||||
/// <https://url.spec.whatwg.org/#concept-ipv6-parser>
|
||||
fn parse_ipv6addr(input: &str) -> ParseResult<Ipv6Addr> {
|
||||
let input = input.as_bytes();
|
||||
let len = input.len();
|
||||
let mut is_ip_v4 = false;
|
||||
let mut pieces = [0, 0, 0, 0, 0, 0, 0, 0];
|
||||
let mut piece_pointer = 0;
|
||||
let mut compress_pointer = None;
|
||||
let mut i = 0;
|
||||
|
||||
if len < 2 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
|
||||
if input[0] == b':' {
|
||||
if input[1] != b':' {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i = 2;
|
||||
piece_pointer = 1;
|
||||
compress_pointer = Some(1);
|
||||
}
|
||||
|
||||
while i < len {
|
||||
if piece_pointer == 8 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
if input[i] == b':' {
|
||||
if compress_pointer.is_some() {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i += 1;
|
||||
piece_pointer += 1;
|
||||
compress_pointer = Some(piece_pointer);
|
||||
continue;
|
||||
}
|
||||
let start = i;
|
||||
let end = cmp::min(len, start + 4);
|
||||
let mut value = 0u16;
|
||||
while i < end {
|
||||
match (input[i] as char).to_digit(16) {
|
||||
Some(digit) => {
|
||||
value = value * 0x10 + digit as u16;
|
||||
i += 1;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
if i < len {
|
||||
match input[i] {
|
||||
b'.' => {
|
||||
if i == start {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
i = start;
|
||||
if piece_pointer > 6 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
is_ip_v4 = true;
|
||||
}
|
||||
b':' => {
|
||||
i += 1;
|
||||
if i == len {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
_ => return Err(ParseError::InvalidIpv6Address),
|
||||
}
|
||||
}
|
||||
if is_ip_v4 {
|
||||
break;
|
||||
}
|
||||
pieces[piece_pointer] = value;
|
||||
piece_pointer += 1;
|
||||
}
|
||||
|
||||
if is_ip_v4 {
|
||||
if piece_pointer > 6 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
let mut numbers_seen = 0;
|
||||
while i < len {
|
||||
if numbers_seen > 0 {
|
||||
if numbers_seen < 4 && (i < len && input[i] == b'.') {
|
||||
i += 1
|
||||
} else {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
|
||||
let mut ipv4_piece = None;
|
||||
while i < len {
|
||||
let digit = match input[i] {
|
||||
c @ b'0'..=b'9' => c - b'0',
|
||||
_ => break,
|
||||
};
|
||||
match ipv4_piece {
|
||||
None => ipv4_piece = Some(digit as u16),
|
||||
Some(0) => return Err(ParseError::InvalidIpv6Address), // No leading zero
|
||||
Some(ref mut v) => {
|
||||
*v = *v * 10 + digit as u16;
|
||||
if *v > 255 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
pieces[piece_pointer] = if let Some(v) = ipv4_piece {
|
||||
pieces[piece_pointer] * 0x100 + v
|
||||
} else {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
};
|
||||
numbers_seen += 1;
|
||||
|
||||
if numbers_seen == 2 || numbers_seen == 4 {
|
||||
piece_pointer += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if numbers_seen != 4 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
|
||||
if i < len {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
|
||||
match compress_pointer {
|
||||
Some(compress_pointer) => {
|
||||
let mut swaps = piece_pointer - compress_pointer;
|
||||
piece_pointer = 7;
|
||||
while swaps > 0 {
|
||||
pieces.swap(piece_pointer, compress_pointer + swaps - 1);
|
||||
swaps -= 1;
|
||||
piece_pointer -= 1;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if piece_pointer != 8 {
|
||||
return Err(ParseError::InvalidIpv6Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Ipv6Addr::new(
|
||||
pieces[0], pieces[1], pieces[2], pieces[3], pieces[4], pieces[5], pieces[6], pieces[7],
|
||||
))
|
||||
}
|
||||
3044
third-party/vendor/url/src/lib.rs
vendored
Normal file
3044
third-party/vendor/url/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
112
third-party/vendor/url/src/origin.rs
vendored
Normal file
112
third-party/vendor/url/src/origin.rs
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// Copyright 2016 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 crate::host::Host;
|
||||
use crate::parser::default_port;
|
||||
use crate::Url;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub fn url_origin(url: &Url) -> Origin {
|
||||
let scheme = url.scheme();
|
||||
match scheme {
|
||||
"blob" => {
|
||||
let result = Url::parse(url.path());
|
||||
match result {
|
||||
Ok(ref url) => url_origin(url),
|
||||
Err(_) => Origin::new_opaque(),
|
||||
}
|
||||
}
|
||||
"ftp" | "http" | "https" | "ws" | "wss" => Origin::Tuple(
|
||||
scheme.to_owned(),
|
||||
url.host().unwrap().to_owned(),
|
||||
url.port_or_known_default().unwrap(),
|
||||
),
|
||||
// TODO: Figure out what to do if the scheme is a file
|
||||
"file" => Origin::new_opaque(),
|
||||
_ => Origin::new_opaque(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The origin of an URL
|
||||
///
|
||||
/// Two URLs with the same origin are considered
|
||||
/// to originate from the same entity and can therefore trust
|
||||
/// each other.
|
||||
///
|
||||
/// The origin is determined based on the scheme as follows:
|
||||
///
|
||||
/// - 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", "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.
|
||||
///
|
||||
/// For more information see <https://url.spec.whatwg.org/#origin>
|
||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||
pub enum Origin {
|
||||
/// A globally unique identifier
|
||||
Opaque(OpaqueOrigin),
|
||||
|
||||
/// Consists of the URL's scheme, host and port
|
||||
Tuple(String, Host<String>, u16),
|
||||
}
|
||||
|
||||
impl Origin {
|
||||
/// Creates a new opaque origin that is only equal to itself.
|
||||
pub fn new_opaque() -> Origin {
|
||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
||||
Origin::Opaque(OpaqueOrigin(COUNTER.fetch_add(1, Ordering::SeqCst)))
|
||||
}
|
||||
|
||||
/// Return whether this origin is a (scheme, host, port) tuple
|
||||
/// (as opposed to an opaque origin).
|
||||
pub fn is_tuple(&self) -> bool {
|
||||
matches!(*self, Origin::Tuple(..))
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
|
||||
pub fn ascii_serialization(&self) -> String {
|
||||
match *self {
|
||||
Origin::Opaque(_) => "null".to_owned(),
|
||||
Origin::Tuple(ref scheme, ref host, port) => {
|
||||
if default_port(scheme) == Some(port) {
|
||||
format!("{}://{}", scheme, host)
|
||||
} else {
|
||||
format!("{}://{}:{}", scheme, host, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin>
|
||||
pub fn unicode_serialization(&self) -> String {
|
||||
match *self {
|
||||
Origin::Opaque(_) => "null".to_owned(),
|
||||
Origin::Tuple(ref scheme, ref host, port) => {
|
||||
let host = match *host {
|
||||
Host::Domain(ref domain) => {
|
||||
let (domain, _errors) = idna::domain_to_unicode(domain);
|
||||
Host::Domain(domain)
|
||||
}
|
||||
_ => host.clone(),
|
||||
};
|
||||
if default_port(scheme) == Some(port) {
|
||||
format!("{}://{}", scheme, host)
|
||||
} else {
|
||||
format!("{}://{}:{}", scheme, host, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque identifier for URLs that have file or other schemes
|
||||
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
|
||||
pub struct OpaqueOrigin(usize);
|
||||
1641
third-party/vendor/url/src/parser.rs
vendored
Normal file
1641
third-party/vendor/url/src/parser.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
246
third-party/vendor/url/src/path_segments.rs
vendored
Normal file
246
third-party/vendor/url/src/path_segments.rs
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
// Copyright 2016 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 crate::parser::{self, to_u32, SchemeType};
|
||||
use crate::Url;
|
||||
use std::str;
|
||||
|
||||
/// Exposes methods to manipulate the path of an URL that is not cannot-be-base.
|
||||
///
|
||||
/// The path always starts with a `/` slash, and is made of slash-separated segments.
|
||||
/// There is always at least one segment (which may be the empty string).
|
||||
///
|
||||
/// Examples:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("mailto:me@example.com")?;
|
||||
/// assert!(url.path_segments_mut().is_err());
|
||||
///
|
||||
/// let mut url = Url::parse("http://example.net/foo/index.html")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .pop().push("img").push("2/100%.png");
|
||||
/// assert_eq!(url.as_str(), "http://example.net/foo/img/2%2F100%25.png");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct PathSegmentsMut<'a> {
|
||||
url: &'a mut Url,
|
||||
after_first_slash: usize,
|
||||
after_path: String,
|
||||
old_after_path_position: u32,
|
||||
}
|
||||
|
||||
// Not re-exported outside the crate
|
||||
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();
|
||||
// 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,
|
||||
old_after_path_position,
|
||||
after_path,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PathSegmentsMut<'a> {
|
||||
fn drop(&mut self) {
|
||||
self.url
|
||||
.restore_after_path(self.old_after_path_position, &self.after_path)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PathSegmentsMut<'a> {
|
||||
/// Remove all segments in the path, leaving the minimal `url.path() == "/"`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::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");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/logout");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.url.serialization.truncate(self.after_first_slash);
|
||||
self
|
||||
}
|
||||
|
||||
/// Remove the last segment of this URL’s path if it is empty,
|
||||
/// except if these was only one segment to begin with.
|
||||
///
|
||||
/// In other words, remove one path trailing slash, if any,
|
||||
/// unless it is also the initial slash (so this does nothing if `url.path() == "/")`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::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");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url//pulls");
|
||||
///
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/")?;
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .pop_if_empty().push("pulls");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # 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();
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Remove the last segment of this URL’s path.
|
||||
///
|
||||
/// If the path only has one segment, make it empty such that `url.path() == "/"`.
|
||||
///
|
||||
/// 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);
|
||||
self.url
|
||||
.serialization
|
||||
.truncate(self.after_first_slash + last_slash);
|
||||
self
|
||||
}
|
||||
|
||||
/// Append the given segment at the end of this URL’s path.
|
||||
///
|
||||
/// See the documentation for `.extend()`.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
pub fn push(&mut self, segment: &str) -> &mut Self {
|
||||
self.extend(Some(segment))
|
||||
}
|
||||
|
||||
/// Append each segment from the given iterator at the end of this URL’s path.
|
||||
///
|
||||
/// Each segment is percent-encoded like in `Url::parse` or `Url::join`,
|
||||
/// except that `%` and `/` characters are also encoded (to `%25` and `%2F`).
|
||||
/// This is unlike `Url::parse` where `%` is left as-is in case some of the input
|
||||
/// is already percent-encoded, and `/` denotes a path segment separator.)
|
||||
///
|
||||
/// Note that, in addition to slashes between new segments,
|
||||
/// this always adds a slash between the existing path and the new segments
|
||||
/// *except* if the existing path is `"/"`.
|
||||
/// If the previous last segment was empty (if the path had a trailing slash)
|
||||
/// the path after `.extend()` will contain two consecutive slashes.
|
||||
/// If that is undesired, call `.pop_if_empty()` first.
|
||||
///
|
||||
/// To obtain a behavior similar to `Url::join`, call `.pop()` unconditionally first.
|
||||
///
|
||||
/// Returns `&mut Self` so that method calls can be chained.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::Error;
|
||||
///
|
||||
/// # fn run() -> Result<(), Box<dyn Error>> {
|
||||
/// let mut url = Url::parse("https://github.com/")?;
|
||||
/// let org = "servo";
|
||||
/// let repo = "rust-url";
|
||||
/// let issue_number = "188";
|
||||
/// url.path_segments_mut().map_err(|_| "cannot be base")?
|
||||
/// .extend(&[org, repo, "issues", issue_number]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/issues/188");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
///
|
||||
/// In order to make sure that parsing the serialization of an URL gives the same URL,
|
||||
/// a segment is ignored if it is `"."` or `".."`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::Url;
|
||||
/// # use std::error::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"]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// # run().unwrap();
|
||||
/// ```
|
||||
pub fn extend<I>(&mut self, segments: I) -> &mut Self
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: AsRef<str>,
|
||||
{
|
||||
let scheme_type = SchemeType::from(self.url.scheme());
|
||||
let path_start = self.url.path_start as usize;
|
||||
self.url.mutate(|parser| {
|
||||
parser.context = parser::Context::PathSegmentSetter;
|
||||
for segment in segments {
|
||||
let segment = segment.as_ref();
|
||||
if matches!(segment, "." | "..") {
|
||||
continue;
|
||||
}
|
||||
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?
|
||||
parser.parse_path(
|
||||
scheme_type,
|
||||
&mut has_host,
|
||||
path_start,
|
||||
parser::Input::new_no_trim(segment),
|
||||
);
|
||||
}
|
||||
});
|
||||
self
|
||||
}
|
||||
}
|
||||
326
third-party/vendor/url/src/quirks.rs
vendored
Normal file
326
third-party/vendor/url/src/quirks.rs
vendored
Normal file
|
|
@ -0,0 +1,326 @@
|
|||
// Copyright 2016 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.
|
||||
|
||||
//! Getters and setters for URL components implemented per https://url.spec.whatwg.org/#api
|
||||
//!
|
||||
//! Unless you need to be interoperable with web browsers,
|
||||
//! you probably want to use `Url` method instead.
|
||||
|
||||
use crate::parser::{default_port, Context, Input, Parser, SchemeType};
|
||||
use crate::{Host, ParseError, Position, Url};
|
||||
|
||||
/// Internal components / offsets of a URL.
|
||||
///
|
||||
/// https://user@pass:example.com:1234/foo/bar?baz#quux
|
||||
/// | | | | ^^^^| | |
|
||||
/// | | | | | | | `----- fragment_start
|
||||
/// | | | | | | `--------- query_start
|
||||
/// | | | | | `----------------- path_start
|
||||
/// | | | | `--------------------- port
|
||||
/// | | | `----------------------- host_end
|
||||
/// | | `---------------------------------- host_start
|
||||
/// | `--------------------------------------- username_end
|
||||
/// `---------------------------------------------- scheme_end
|
||||
#[derive(Copy, Clone)]
|
||||
#[cfg(feature = "expose_internals")]
|
||||
pub struct InternalComponents {
|
||||
pub scheme_end: u32,
|
||||
pub username_end: u32,
|
||||
pub host_start: u32,
|
||||
pub host_end: u32,
|
||||
pub port: Option<u16>,
|
||||
pub path_start: u32,
|
||||
pub query_start: Option<u32>,
|
||||
pub fragment_start: Option<u32>,
|
||||
}
|
||||
|
||||
/// Internal component / parsed offsets of the URL.
|
||||
///
|
||||
/// This can be useful for implementing efficient serialization
|
||||
/// for the URL.
|
||||
#[cfg(feature = "expose_internals")]
|
||||
pub fn internal_components(url: &Url) -> InternalComponents {
|
||||
InternalComponents {
|
||||
scheme_end: url.scheme_end,
|
||||
username_end: url.username_end,
|
||||
host_start: url.host_start,
|
||||
host_end: url.host_end,
|
||||
port: url.port,
|
||||
path_start: url.path_start,
|
||||
query_start: url.query_start,
|
||||
fragment_start: url.fragment_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// https://url.spec.whatwg.org/#dom-url-domaintoascii
|
||||
pub fn domain_to_ascii(domain: &str) -> String {
|
||||
match Host::parse(domain) {
|
||||
Ok(Host::Domain(domain)) => domain,
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// https://url.spec.whatwg.org/#dom-url-domaintounicode
|
||||
pub fn domain_to_unicode(domain: &str) -> String {
|
||||
match Host::parse(domain) {
|
||||
Ok(Host::Domain(ref domain)) => {
|
||||
let (unicode, _errors) = idna::domain_to_unicode(domain);
|
||||
unicode
|
||||
}
|
||||
_ => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-href
|
||||
pub fn href(url: &Url) -> &str {
|
||||
url.as_str()
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-href
|
||||
pub fn set_href(url: &mut Url, value: &str) -> Result<(), ParseError> {
|
||||
*url = Url::parse(value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-origin
|
||||
pub fn origin(url: &Url) -> String {
|
||||
url.origin().ascii_serialization()
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-protocol
|
||||
#[inline]
|
||||
pub fn protocol(url: &Url) -> &str {
|
||||
&url.as_str()[..url.scheme().len() + ":".len()]
|
||||
}
|
||||
|
||||
/// 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.
|
||||
if let Some(position) = new_protocol.find(':') {
|
||||
new_protocol = &new_protocol[..position];
|
||||
}
|
||||
url.set_scheme(new_protocol)
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-username
|
||||
#[inline]
|
||||
pub fn username(url: &Url) -> &str {
|
||||
url.username()
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-password
|
||||
#[inline]
|
||||
pub fn password(url: &Url) -> &str {
|
||||
url.password().unwrap_or("")
|
||||
}
|
||||
|
||||
/// 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
|
||||
} else {
|
||||
Some(new_password)
|
||||
})
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-host
|
||||
#[inline]
|
||||
pub fn host(url: &Url) -> &str {
|
||||
&url[Position::BeforeHost..Position::AfterPort]
|
||||
}
|
||||
|
||||
/// 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 object’s url’s 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::new_no_trim(new_host);
|
||||
let host;
|
||||
let opt_port;
|
||||
{
|
||||
let scheme = url.scheme();
|
||||
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
|
||||
};
|
||||
} 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())
|
||||
&& (!username(url).is_empty() || matches!(opt_port, Some(Some(_))) || url.port().is_some())
|
||||
{
|
||||
return Err(());
|
||||
}
|
||||
url.set_host_internal(host, opt_port);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-hostname
|
||||
#[inline]
|
||||
pub fn hostname(url: &Url) -> &str {
|
||||
url.host_str().unwrap_or("")
|
||||
}
|
||||
|
||||
/// 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(());
|
||||
}
|
||||
// Host parsing rules are strict we don't want to trim the input
|
||||
let input = Input::new_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 {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-port
|
||||
#[inline]
|
||||
pub fn port(url: &Url) -> &str {
|
||||
&url[Position::BeforePort..Position::AfterPort]
|
||||
}
|
||||
|
||||
/// 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;
|
||||
{
|
||||
// has_host implies !cannot_be_a_base
|
||||
let scheme = url.scheme();
|
||||
if !url.has_host() || url.host() == Some(Host::Domain("")) || scheme == "file" {
|
||||
return Err(());
|
||||
}
|
||||
result = Parser::parse_port(
|
||||
Input::new_no_trim(new_port),
|
||||
|| default_port(scheme),
|
||||
Context::Setter,
|
||||
)
|
||||
}
|
||||
if let Ok((new_port, _remaining)) = result {
|
||||
url.set_port_internal(new_port);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-pathname
|
||||
#[inline]
|
||||
pub fn pathname(url: &Url) -> &str {
|
||||
url.path()
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-search
|
||||
pub fn search(url: &Url) -> &str {
|
||||
trim(&url[Position::AfterPath..Position::AfterQuery])
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-search
|
||||
pub fn set_search(url: &mut Url, new_search: &str) {
|
||||
url.set_query(match new_search {
|
||||
"" => None,
|
||||
_ if new_search.starts_with('?') => Some(&new_search[1..]),
|
||||
_ => Some(new_search),
|
||||
})
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-hash
|
||||
pub fn hash(url: &Url) -> &str {
|
||||
trim(&url[Position::AfterQuery..])
|
||||
}
|
||||
|
||||
/// Setter for https://url.spec.whatwg.org/#dom-url-hash
|
||||
pub fn set_hash(url: &mut Url, new_hash: &str) {
|
||||
url.set_fragment(match new_hash {
|
||||
// If the given value is the empty string,
|
||||
// then set context object’s url’s 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 {
|
||||
if s.len() == 1 {
|
||||
""
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
217
third-party/vendor/url/src/slicing.rs
vendored
Normal file
217
third-party/vendor/url/src/slicing.rs
vendored
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
// Copyright 2016 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 crate::Url;
|
||||
use std::ops::{Index, Range, RangeFrom, RangeFull, RangeTo};
|
||||
|
||||
impl Index<RangeFull> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, _: RangeFull) -> &str {
|
||||
&self.serialization
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<RangeFrom<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: RangeFrom<Position>) -> &str {
|
||||
&self.serialization[self.index(range.start)..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<RangeTo<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: RangeTo<Position>) -> &str {
|
||||
&self.serialization[..self.index(range.end)]
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Range<Position>> for Url {
|
||||
type Output = str;
|
||||
fn index(&self, range: Range<Position>) -> &str {
|
||||
&self.serialization[self.index(range.start)..self.index(range.end)]
|
||||
}
|
||||
}
|
||||
|
||||
// Counts how many base-10 digits are required to represent n in the given base
|
||||
fn count_digits(n: u16) -> usize {
|
||||
match n {
|
||||
0..=9 => 1,
|
||||
10..=99 => 2,
|
||||
100..=999 => 3,
|
||||
1000..=9999 => 4,
|
||||
10000..=65535 => 5,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_count_digits() {
|
||||
assert_eq!(count_digits(0), 1);
|
||||
assert_eq!(count_digits(1), 1);
|
||||
assert_eq!(count_digits(9), 1);
|
||||
assert_eq!(count_digits(10), 2);
|
||||
assert_eq!(count_digits(99), 2);
|
||||
assert_eq!(count_digits(100), 3);
|
||||
assert_eq!(count_digits(9999), 4);
|
||||
assert_eq!(count_digits(65535), 5);
|
||||
}
|
||||
|
||||
/// Indicates a position within a URL based on its components.
|
||||
///
|
||||
/// A range of positions can be used for slicing `Url`:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use url::{Url, Position};
|
||||
/// # fn something(some_url: Url) {
|
||||
/// let serialization: &str = &some_url[..];
|
||||
/// let serialization_without_fragment: &str = &some_url[..Position::AfterQuery];
|
||||
/// let authority: &str = &some_url[Position::BeforeUsername..Position::AfterPort];
|
||||
/// let data_url_payload: &str = &some_url[Position::BeforePath..Position::AfterQuery];
|
||||
/// let scheme_relative: &str = &some_url[Position::BeforeUsername..];
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// In a pseudo-grammar (where `[`…`]?` makes a sub-sequence optional),
|
||||
/// URL components and delimiters that separate them are:
|
||||
///
|
||||
/// ```notrust
|
||||
/// url =
|
||||
/// scheme ":"
|
||||
/// [ "//" [ username [ ":" password ]? "@" ]? host [ ":" port ]? ]?
|
||||
/// path [ "?" query ]? [ "#" fragment ]?
|
||||
/// ```
|
||||
///
|
||||
/// When a given component is not present,
|
||||
/// its "before" and "after" position are the same
|
||||
/// (so that `&some_url[BeforeFoo..AfterFoo]` is the empty string)
|
||||
/// and component ordering is preserved
|
||||
/// (so that a missing query "is between" a path and a fragment).
|
||||
///
|
||||
/// The end of a component and the start of the next are either the same or separate
|
||||
/// by a delimiter.
|
||||
/// (Note that the initial `/` of a path is considered part of the path here, not a delimiter.)
|
||||
/// For example, `&url[..BeforeFragment]` would include a `#` delimiter (if present in `url`),
|
||||
/// so `&url[..AfterQuery]` might be desired instead.
|
||||
///
|
||||
/// `BeforeScheme` and `AfterFragment` are always the start and end of the entire URL,
|
||||
/// so `&url[BeforeScheme..X]` is the same as `&url[..X]`
|
||||
/// and `&url[X..AfterFragment]` is the same as `&url[X..]`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum Position {
|
||||
BeforeScheme,
|
||||
AfterScheme,
|
||||
BeforeUsername,
|
||||
AfterUsername,
|
||||
BeforePassword,
|
||||
AfterPassword,
|
||||
BeforeHost,
|
||||
AfterHost,
|
||||
BeforePort,
|
||||
AfterPort,
|
||||
BeforePath,
|
||||
AfterPath,
|
||||
BeforeQuery,
|
||||
AfterQuery,
|
||||
BeforeFragment,
|
||||
AfterFragment,
|
||||
}
|
||||
|
||||
impl Url {
|
||||
#[inline]
|
||||
fn index(&self, position: Position) -> usize {
|
||||
match position {
|
||||
Position::BeforeScheme => 0,
|
||||
|
||||
Position::AfterScheme => self.scheme_end as usize,
|
||||
|
||||
Position::BeforeUsername => {
|
||||
if self.has_authority() {
|
||||
self.scheme_end as usize + "://".len()
|
||||
} else {
|
||||
debug_assert!(self.byte_at(self.scheme_end) == b':');
|
||||
debug_assert!(self.scheme_end + ":".len() as u32 == self.username_end);
|
||||
self.scheme_end as usize + ":".len()
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterUsername => self.username_end as usize,
|
||||
|
||||
Position::BeforePassword => {
|
||||
if self.has_authority() && self.byte_at(self.username_end) == b':' {
|
||||
self.username_end as usize + ":".len()
|
||||
} else {
|
||||
debug_assert!(self.username_end == self.host_start);
|
||||
self.username_end as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterPassword => {
|
||||
if self.has_authority() && self.byte_at(self.username_end) == b':' {
|
||||
debug_assert!(self.byte_at(self.host_start - "@".len() as u32) == b'@');
|
||||
self.host_start as usize - "@".len()
|
||||
} else {
|
||||
debug_assert!(self.username_end == self.host_start);
|
||||
self.host_start as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::BeforeHost => self.host_start as usize,
|
||||
|
||||
Position::AfterHost => self.host_end as usize,
|
||||
|
||||
Position::BeforePort => {
|
||||
if self.port.is_some() {
|
||||
debug_assert!(self.byte_at(self.host_end) == b':');
|
||||
self.host_end as usize + ":".len()
|
||||
} else {
|
||||
self.host_end as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::AfterPort => {
|
||||
if let Some(port) = self.port {
|
||||
debug_assert!(self.byte_at(self.host_end) == b':');
|
||||
self.host_end as usize + ":".len() + count_digits(port)
|
||||
} else {
|
||||
self.host_end as usize
|
||||
}
|
||||
}
|
||||
|
||||
Position::BeforePath => self.path_start as usize,
|
||||
|
||||
Position::AfterPath => match (self.query_start, self.fragment_start) {
|
||||
(Some(q), _) => q as usize,
|
||||
(None, Some(f)) => f as usize,
|
||||
(None, None) => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::BeforeQuery => match (self.query_start, self.fragment_start) {
|
||||
(Some(q), _) => {
|
||||
debug_assert!(self.byte_at(q) == b'?');
|
||||
q as usize + "?".len()
|
||||
}
|
||||
(None, Some(f)) => f as usize,
|
||||
(None, None) => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::AfterQuery => match self.fragment_start {
|
||||
None => self.serialization.len(),
|
||||
Some(f) => f as usize,
|
||||
},
|
||||
|
||||
Position::BeforeFragment => match self.fragment_start {
|
||||
Some(f) => {
|
||||
debug_assert!(self.byte_at(f) == b'#');
|
||||
f as usize + "#".len()
|
||||
}
|
||||
None => self.serialization.len(),
|
||||
},
|
||||
|
||||
Position::AfterFragment => self.serialization.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
51
third-party/vendor/url/tests/expected_failures.txt
vendored
Normal file
51
third-party/vendor/url/tests/expected_failures.txt
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
</> against <file://h/C:/a/b>
|
||||
<file:\\\\//>
|
||||
<file:\\\\\\\\>
|
||||
<file:\\\\\\\\?fox>
|
||||
<file:\\\\\\\\#guppy>
|
||||
<file://spider///>
|
||||
<file:\\\\localhost//>
|
||||
<file://\\/localhost//cat>
|
||||
<file://localhost//a//../..//>
|
||||
</////mouse> against <file:///elephant>
|
||||
<\\/localhost//pig> against <file://lion/>
|
||||
<//localhost//pig> against <file://lion/>
|
||||
</..//localhost//pig> against <file://lion/>
|
||||
<C|> against <file://host/dir/file>
|
||||
<C|> against <file://host/D:/dir1/dir2/file>
|
||||
<C|#> against <file://host/dir/file>
|
||||
<C|?> against <file://host/dir/file>
|
||||
<C|/> against <file://host/dir/file>
|
||||
<C|\n/> against <file://host/dir/file>
|
||||
<C|\\> against <file://host/dir/file>
|
||||
</c:/foo/bar> against <file://host/path>
|
||||
<file://example.net/C:/>
|
||||
<file://1.2.3.4/C:/>
|
||||
<file://[1::8]/C:/>
|
||||
<C|/> against <file://host/>
|
||||
</C:/> against <file://host/>
|
||||
<file:C:/> against <file://host/>
|
||||
<file:/C:/> against <file://host/>
|
||||
<file://localhost//a//../..//foo>
|
||||
<file://localhost////foo>
|
||||
<file:////foo>
|
||||
<file:////one/two> against <file:///>
|
||||
<////one/two> against <file:///>
|
||||
<file:///.//> against <file:////>
|
||||
<file:.//p>
|
||||
<file:/.//p>
|
||||
<http://example.net/path> set hostname to <example.com:8080>
|
||||
<http://example.net:8080/path> set hostname to <example.com:>
|
||||
<non-spec:/.//p> set hostname to <h>
|
||||
<non-spec:/.//p> set hostname to <>
|
||||
<foo://somehost/some/path> set pathname to <>
|
||||
<foo:///some/path> set pathname to <>
|
||||
<http://example.net:8080/path> set port to <randomstring>
|
||||
<file:///var/log/system.log> set href to <http://0300.168.0xF0>
|
||||
<file://monkey/> set pathname to <\\\\>
|
||||
<file:///unicorn> set pathname to <//\\/>
|
||||
<file:///unicorn> set pathname to <//monkey/..//>
|
||||
<non-spec:/> set pathname to </.//p>
|
||||
<non-spec:/> set pathname to </..//p>
|
||||
<non-spec:/> set pathname to <//p>
|
||||
<non-spec:/.//> set pathname to <p>
|
||||
2381
third-party/vendor/url/tests/setters_tests.json
vendored
Normal file
2381
third-party/vendor/url/tests/setters_tests.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
1308
third-party/vendor/url/tests/unit.rs
vendored
Normal file
1308
third-party/vendor/url/tests/unit.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
9519
third-party/vendor/url/tests/urltestdata.json
vendored
Normal file
9519
third-party/vendor/url/tests/urltestdata.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
477
third-party/vendor/url/tests/wpt.rs
vendored
Normal file
477
third-party/vendor/url/tests/wpt.rs
vendored
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
// Copyright 2013-2014 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.
|
||||
|
||||
//! Data-driven tests imported from web-platform-tests
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
use std::panic;
|
||||
|
||||
use serde_json::Value;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct UrlTest {
|
||||
input: String,
|
||||
base: Option<String>,
|
||||
#[serde(flatten)]
|
||||
result: UrlTestResult,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
#[serde(untagged)]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
enum UrlTestResult {
|
||||
Ok(UrlTestOk),
|
||||
Fail(UrlTestFail),
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct UrlTestOk {
|
||||
href: String,
|
||||
protocol: String,
|
||||
username: String,
|
||||
password: String,
|
||||
host: String,
|
||||
hostname: String,
|
||||
port: String,
|
||||
pathname: String,
|
||||
search: String,
|
||||
hash: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct UrlTestFail {
|
||||
failure: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct SetterTest {
|
||||
href: String,
|
||||
new_value: String,
|
||||
expected: SetterTestExpected,
|
||||
}
|
||||
|
||||
#[derive(Debug, serde::Deserialize)]
|
||||
struct SetterTestExpected {
|
||||
href: Option<String>,
|
||||
protocol: Option<String>,
|
||||
username: Option<String>,
|
||||
password: Option<String>,
|
||||
host: Option<String>,
|
||||
hostname: Option<String>,
|
||||
port: Option<String>,
|
||||
pathname: Option<String>,
|
||||
search: Option<String>,
|
||||
hash: Option<String>,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut filter = None;
|
||||
let mut args = std::env::args().skip(1);
|
||||
while filter.is_none() {
|
||||
if let Some(arg) = args.next() {
|
||||
if arg == "--test-threads" {
|
||||
args.next();
|
||||
continue;
|
||||
}
|
||||
filter = Some(arg);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut expected_failures = include_str!("expected_failures.txt")
|
||||
.lines()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut errors = vec![];
|
||||
|
||||
// Copied from https://github.com/web-platform-tests/wpt/blob/master/url/
|
||||
let url_json: Vec<Value> = serde_json::from_str(include_str!("urltestdata.json"))
|
||||
.expect("JSON parse error in urltestdata.json");
|
||||
let url_tests = url_json
|
||||
.into_iter()
|
||||
.filter(|val| val.is_object())
|
||||
.map(|val| serde_json::from_value::<UrlTest>(val).expect("parsing failed"))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let setter_json: HashMap<String, Value> =
|
||||
serde_json::from_str(include_str!("setters_tests.json"))
|
||||
.expect("JSON parse error in setters_tests.json");
|
||||
let setter_tests = setter_json
|
||||
.into_iter()
|
||||
.filter(|(k, _)| k != "comment")
|
||||
.map(|(k, v)| {
|
||||
let test = serde_json::from_value::<Vec<SetterTest>>(v).expect("parsing failed");
|
||||
(k, test)
|
||||
})
|
||||
.collect::<HashMap<_, _>>();
|
||||
|
||||
for url_test in url_tests {
|
||||
let mut name = format!("<{}>", url_test.input.escape_default());
|
||||
if let Some(base) = &url_test.base {
|
||||
write!(&mut name, " against <{}>", base.escape_default()).unwrap();
|
||||
}
|
||||
if should_skip(&name, filter.as_deref()) {
|
||||
continue;
|
||||
}
|
||||
print!("{} ... ", name);
|
||||
|
||||
let res = run_url_test(url_test);
|
||||
report(name, res, &mut errors, &mut expected_failures);
|
||||
}
|
||||
|
||||
for (kind, tests) in setter_tests {
|
||||
for test in tests {
|
||||
let name = format!(
|
||||
"<{}> set {} to <{}>",
|
||||
test.href.escape_default(),
|
||||
kind,
|
||||
test.new_value.escape_default()
|
||||
);
|
||||
if should_skip(&name, filter.as_deref()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
print!("{} ... ", name);
|
||||
|
||||
let res = run_setter_test(&kind, test);
|
||||
report(name, res, &mut errors, &mut expected_failures);
|
||||
}
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("====================");
|
||||
println!();
|
||||
|
||||
if !errors.is_empty() {
|
||||
println!("errors:");
|
||||
println!();
|
||||
|
||||
for (name, err) in errors {
|
||||
println!(" name: {}", name);
|
||||
println!(" err: {}", err);
|
||||
println!();
|
||||
}
|
||||
|
||||
std::process::exit(1);
|
||||
} else {
|
||||
println!("all tests passed");
|
||||
}
|
||||
|
||||
if !expected_failures.is_empty() && filter.is_none() {
|
||||
println!();
|
||||
println!("====================");
|
||||
println!();
|
||||
println!("tests were expected to fail but did not run:");
|
||||
println!();
|
||||
|
||||
for name in expected_failures {
|
||||
println!(" {}", name);
|
||||
}
|
||||
|
||||
println!();
|
||||
println!("if these tests were removed, update expected_failures.txt");
|
||||
println!();
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn should_skip(name: &str, filter: Option<&str>) -> bool {
|
||||
match filter {
|
||||
Some(filter) => !name.contains(filter),
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn report(
|
||||
name: String,
|
||||
res: Result<(), String>,
|
||||
errors: &mut Vec<(String, String)>,
|
||||
expected_failures: &mut Vec<&str>,
|
||||
) {
|
||||
let expected_failure = expected_failures.contains(&&*name);
|
||||
expected_failures.retain(|&s| s != &*name);
|
||||
match res {
|
||||
Ok(()) => {
|
||||
if expected_failure {
|
||||
println!("🟠 (unexpected success)");
|
||||
errors.push((name, "unexpected success".to_string()));
|
||||
} else {
|
||||
println!("✅");
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if expected_failure {
|
||||
println!("✅ (expected fail)");
|
||||
} else {
|
||||
println!("❌");
|
||||
errors.push((name, err));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_url_test(
|
||||
UrlTest {
|
||||
base,
|
||||
input,
|
||||
result,
|
||||
}: UrlTest,
|
||||
) -> Result<(), String> {
|
||||
let base = match base {
|
||||
Some(base) => {
|
||||
let base = panic::catch_unwind(|| Url::parse(&base))
|
||||
.map_err(|_| "panicked while parsing base".to_string())?
|
||||
.map_err(|e| format!("errored while parsing base: {}", e))?;
|
||||
Some(base)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let res = panic::catch_unwind(move || Url::options().base_url(base.as_ref()).parse(&input))
|
||||
.map_err(|_| "panicked while parsing input".to_string())?
|
||||
.map_err(|e| format!("errored while parsing input: {}", e));
|
||||
|
||||
match result {
|
||||
UrlTestResult::Ok(ok) => check_url_ok(res, ok),
|
||||
UrlTestResult::Fail(fail) => {
|
||||
assert!(fail.failure);
|
||||
if res.is_ok() {
|
||||
return Err("expected failure, but parsed successfully".to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_url_ok(res: Result<Url, String>, ok: UrlTestOk) -> Result<(), String> {
|
||||
let url = match res {
|
||||
Ok(url) => url,
|
||||
Err(err) => {
|
||||
return Err(format!("expected success, but errored: {:?}", err));
|
||||
}
|
||||
};
|
||||
|
||||
let href = url::quirks::href(&url);
|
||||
if href != ok.href {
|
||||
return Err(format!("expected href {:?}, but got {:?}", ok.href, href));
|
||||
}
|
||||
|
||||
let protocol = url::quirks::protocol(&url);
|
||||
if protocol != ok.protocol {
|
||||
return Err(format!(
|
||||
"expected protocol {:?}, but got {:?}",
|
||||
ok.protocol, protocol
|
||||
));
|
||||
}
|
||||
|
||||
let username = url::quirks::username(&url);
|
||||
if username != ok.username {
|
||||
return Err(format!(
|
||||
"expected username {:?}, but got {:?}",
|
||||
ok.username, username
|
||||
));
|
||||
}
|
||||
|
||||
let password = url::quirks::password(&url);
|
||||
if password != ok.password {
|
||||
return Err(format!(
|
||||
"expected password {:?}, but got {:?}",
|
||||
ok.password, password
|
||||
));
|
||||
}
|
||||
|
||||
let host = url::quirks::host(&url);
|
||||
if host != ok.host {
|
||||
return Err(format!("expected host {:?}, but got {:?}", ok.host, host));
|
||||
}
|
||||
|
||||
let hostname = url::quirks::hostname(&url);
|
||||
if hostname != ok.hostname {
|
||||
return Err(format!(
|
||||
"expected hostname {:?}, but got {:?}",
|
||||
ok.hostname, hostname
|
||||
));
|
||||
}
|
||||
|
||||
let port = url::quirks::port(&url);
|
||||
if port != ok.port {
|
||||
return Err(format!("expected port {:?}, but got {:?}", ok.port, port));
|
||||
}
|
||||
|
||||
let pathname = url::quirks::pathname(&url);
|
||||
if pathname != ok.pathname {
|
||||
return Err(format!(
|
||||
"expected pathname {:?}, but got {:?}",
|
||||
ok.pathname, pathname
|
||||
));
|
||||
}
|
||||
|
||||
let search = url::quirks::search(&url);
|
||||
if search != ok.search {
|
||||
return Err(format!(
|
||||
"expected search {:?}, but got {:?}",
|
||||
ok.search, search
|
||||
));
|
||||
}
|
||||
|
||||
let hash = url::quirks::hash(&url);
|
||||
if hash != ok.hash {
|
||||
return Err(format!("expected hash {:?}, but got {:?}", ok.hash, hash));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run_setter_test(
|
||||
kind: &str,
|
||||
SetterTest {
|
||||
href,
|
||||
new_value,
|
||||
expected,
|
||||
}: SetterTest,
|
||||
) -> Result<(), String> {
|
||||
let mut url = panic::catch_unwind(|| Url::parse(&href))
|
||||
.map_err(|_| "panicked while parsing href".to_string())?
|
||||
.map_err(|e| format!("errored while parsing href: {}", e))?;
|
||||
|
||||
let url = panic::catch_unwind(move || {
|
||||
match kind {
|
||||
"protocol" => {
|
||||
url::quirks::set_protocol(&mut url, &new_value).ok();
|
||||
}
|
||||
"username" => {
|
||||
url::quirks::set_username(&mut url, &new_value).ok();
|
||||
}
|
||||
"password" => {
|
||||
url::quirks::set_password(&mut url, &new_value).ok();
|
||||
}
|
||||
"host" => {
|
||||
url::quirks::set_host(&mut url, &new_value).ok();
|
||||
}
|
||||
"hostname" => {
|
||||
url::quirks::set_hostname(&mut url, &new_value).ok();
|
||||
}
|
||||
"port" => {
|
||||
url::quirks::set_port(&mut url, &new_value).ok();
|
||||
}
|
||||
"pathname" => url::quirks::set_pathname(&mut url, &new_value),
|
||||
"search" => url::quirks::set_search(&mut url, &new_value),
|
||||
"hash" => url::quirks::set_hash(&mut url, &new_value),
|
||||
_ => panic!("unknown setter kind: {:?}", kind),
|
||||
};
|
||||
url
|
||||
})
|
||||
.map_err(|_| "panicked while setting value".to_string())?;
|
||||
|
||||
if let Some(expected_href) = expected.href {
|
||||
let href = url::quirks::href(&url);
|
||||
if href != expected_href {
|
||||
return Err(format!(
|
||||
"expected href {:?}, but got {:?}",
|
||||
expected_href, href
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_protocol) = expected.protocol {
|
||||
let protocol = url::quirks::protocol(&url);
|
||||
if protocol != expected_protocol {
|
||||
return Err(format!(
|
||||
"expected protocol {:?}, but got {:?}",
|
||||
expected_protocol, protocol
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_username) = expected.username {
|
||||
let username = url::quirks::username(&url);
|
||||
if username != expected_username {
|
||||
return Err(format!(
|
||||
"expected username {:?}, but got {:?}",
|
||||
expected_username, username
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_password) = expected.password {
|
||||
let password = url::quirks::password(&url);
|
||||
if password != expected_password {
|
||||
return Err(format!(
|
||||
"expected password {:?}, but got {:?}",
|
||||
expected_password, password
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_host) = expected.host {
|
||||
let host = url::quirks::host(&url);
|
||||
if host != expected_host {
|
||||
return Err(format!(
|
||||
"expected host {:?}, but got {:?}",
|
||||
expected_host, host
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_hostname) = expected.hostname {
|
||||
let hostname = url::quirks::hostname(&url);
|
||||
if hostname != expected_hostname {
|
||||
return Err(format!(
|
||||
"expected hostname {:?}, but got {:?}",
|
||||
expected_hostname, hostname
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_port) = expected.port {
|
||||
let port = url::quirks::port(&url);
|
||||
if port != expected_port {
|
||||
return Err(format!(
|
||||
"expected port {:?}, but got {:?}",
|
||||
expected_port, port
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_pathname) = expected.pathname {
|
||||
let pathname = url::quirks::pathname(&url);
|
||||
if pathname != expected_pathname {
|
||||
return Err(format!(
|
||||
"expected pathname {:?}, but got {:?}",
|
||||
expected_pathname, pathname
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_search) = expected.search {
|
||||
let search = url::quirks::search(&url);
|
||||
if search != expected_search {
|
||||
return Err(format!(
|
||||
"expected search {:?}, but got {:?}",
|
||||
expected_search, search
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(expected_hash) = expected.hash {
|
||||
let hash = url::quirks::hash(&url);
|
||||
if hash != expected_hash {
|
||||
return Err(format!(
|
||||
"expected hash {:?}, but got {:?}",
|
||||
expected_hash, hash
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue