mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-26 00:57:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
1
third_party/rust/url/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/url/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"e20a03cb660e0039506f8828204fd93815ebfe051ef62194f8dcf3fc8b7d0e5a",".travis.yml":"2a3033f9edf86bc829de486fc1e74fe0bbe3166ed99b0139754ea29772c19b06","Cargo.toml":"30055a6d4ff3d1bf29a9fa99401e4ffffeecc4b5bade830843a9a5f1634fefd1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"20c7855c364d57ea4c97889a5e8d98470a9952dade37bd9248b9a54431670e5e","Makefile":"92b64915724e3b5fec95ec96c5af738bd219b80709e18bf8639d784bc3d9a600","README.md":"eb3f4694003f408cbe3c7f3e9fbbc71241defb940cc55a816981f0f0f144c8eb","UPGRADING.md":"fbcc2d39bdf17db0745793db6626fcd5c909dddd4ce13b27566cfabece22c368","appveyor.yml":"c78486dbfbe6ebbf3d808afb9a19f7ec18c4704ce451c6305f0716999b70a1a6","docs/.nojekyll":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","docs/404.html":"f61e6271c1ea1aa113b64b356e994595fa548f0433f89948d747503ad22195cd","docs/index.html":"f61e6271c1ea1aa113b64b356e994595fa548f0433f89948d747503ad22195cd","github.png":"b432fd855efe7c430fe6a57ccf83935c1996f03a7cdc8d6e1b34154b8c43f6ec","rust-url-todo":"1192cee7b6cedf2133d97dc6074b593a1d19b0ee13fff6f28d6329855044e575","src/encoding.rs":"7fb43e1c109bf9f2a80a05525082f90e79dba8e8056547571c49fba074406d39","src/form_urlencoded.rs":"172922f2c51eb8dae0182d70e5e0c2969f1b5b7aac026720ced9f84059465999","src/host.rs":"a654dc3c9ce9024b98698e000bc97abaac7f10c49b1635be8125cf72376fd5d1","src/lib.rs":"c3542aabc733f76a3b9c7d24b5c41e60e9eb84d2712660611300d1de0e7c2072","src/origin.rs":"d52010a280d363aed6832f7de5e52c1a62815302e59dcbc9cdc2574e2ac884b9","src/parser.rs":"8ca331ada66f309e91fcc375d8467c929b453979fe0ac5abe190eb8a32074db8","src/path_segments.rs":"0414985c441d0c0292ccc6f56a144b84728ae03382476e6cae1a766f8c333ef8","src/percent_encoding.rs":"44d3321eaa1c77715e9ea1421519289ca73612a31c3d6fce04ff489dfa7db3c5","src/quirks.rs":"3249d1a1f73dd29ec06d626ea2ea4d61e7b2a782543742a5bee422b2f3864b19","src/slicing.rs":"4e539886b23945a92094625f3e531a4bff40daa44240b5d19ee8577478c4f7fe","tests/data.rs":"d36f0ee509fb00524635a7968e336bb89674a82a4fcb06be189155e4b9d43db5","tests/setters_tests.json":"ebb439306ea748be6d0f93132cb59220c5805afd56357d6017f1eb1e4f0f80b3","tests/unit.rs":"9a05f55c7b381b58ee67ef8ef145220e5df88a403225a1a324a36afc9bb6a7d7","tests/urltestdata.json":"11abe4a459566c3929e3326828f8576cb59ec8fab1d191714c34eac28d5f15f9"},"package":"8527c62d9869a08325c38272b3f85668df22a65890c61a639d233dc0ed0b23a2"}
|
||||
0
third_party/rust/url/.cargo-ok
vendored
Normal file
0
third_party/rust/url/.cargo-ok
vendored
Normal file
3
third_party/rust/url/.gitignore
vendored
Normal file
3
third_party/rust/url/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
target
|
||||
Cargo.lock
|
||||
/.cargo/config
|
||||
8
third_party/rust/url/.travis.yml
vendored
Normal file
8
third_party/rust/url/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
language: rust
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
script: make test
|
||||
notifications:
|
||||
webhooks: http://build.servo.org:54856/travis
|
||||
38
third_party/rust/url/Cargo.toml
vendored
Normal file
38
third_party/rust/url/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
[package]
|
||||
|
||||
name = "url"
|
||||
version = "1.2.1"
|
||||
authors = ["The rust-url developers"]
|
||||
|
||||
description = "URL library for Rust, based on the WHATWG URL Standard"
|
||||
documentation = "http://servo.github.io/rust-url/url/index.html"
|
||||
repository = "https://github.com/servo/rust-url"
|
||||
readme = "README.md"
|
||||
keywords = ["url", "parser"]
|
||||
license = "MIT/Apache-2.0"
|
||||
|
||||
[[test]]
|
||||
name = "unit"
|
||||
|
||||
[[test]]
|
||||
name = "data"
|
||||
harness = false
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
|
||||
[dev-dependencies]
|
||||
rustc-test = "0.1"
|
||||
rustc-serialize = "0.3"
|
||||
|
||||
[features]
|
||||
query_encoding = ["encoding"]
|
||||
heap_size = ["heapsize"]
|
||||
|
||||
[dependencies]
|
||||
encoding = {version = "0.2", optional = true}
|
||||
heapsize = {version = ">=0.1.1, <0.4", optional = true}
|
||||
idna = { version = "0.1.0", path = "./idna" }
|
||||
matches = "0.1"
|
||||
rustc-serialize = {version = "0.3", optional = true}
|
||||
serde = {version = ">=0.6.1, <0.9", optional = true}
|
||||
201
third_party/rust/url/LICENSE-APACHE
vendored
Normal file
201
third_party/rust/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/rust/url/LICENSE-MIT
vendored
Normal file
25
third_party/rust/url/LICENSE-MIT
vendored
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.
|
||||
5
third_party/rust/url/Makefile
vendored
Normal file
5
third_party/rust/url/Makefile
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
test:
|
||||
cargo test --features "query_encoding serde rustc-serialize"
|
||||
[ x$$TRAVIS_RUST_VERSION != xnightly ] || cargo test --features heapsize
|
||||
|
||||
.PHONY: test
|
||||
10
third_party/rust/url/README.md
vendored
Normal file
10
third_party/rust/url/README.md
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
rust-url
|
||||
========
|
||||
|
||||
[](https://travis-ci.org/servo/rust-url) [](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
vendored
Normal file
263
third_party/rust/url/UPGRADING.md
vendored
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
# 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 iterator’s `collect()` method
|
||||
or give it to some data structure’s `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
vendored
Normal file
13
third_party/rust/url/appveyor.yml
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
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
|
||||
0
third_party/rust/url/docs/.nojekyll
vendored
Normal file
0
third_party/rust/url/docs/.nojekyll
vendored
Normal file
3
third_party/rust/url/docs/404.html
vendored
Normal file
3
third_party/rust/url/docs/404.html
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<meta http-equiv="refresh" content="0; url=https://docs.rs/url/">
|
||||
<link rel="canonical" href="https://docs.rs/url/">
|
||||
<a href="https://docs.rs/url/">Moved to docs.rs</a>
|
||||
3
third_party/rust/url/docs/index.html
vendored
Normal file
3
third_party/rust/url/docs/index.html
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
<meta http-equiv="refresh" content="0; url=https://docs.rs/url/">
|
||||
<link rel="canonical" href="https://docs.rs/url/">
|
||||
<a href="https://docs.rs/url/">Moved to docs.rs</a>
|
||||
BIN
third_party/rust/url/github.png
vendored
Normal file
BIN
third_party/rust/url/github.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.6 KiB |
14
third_party/rust/url/rust-url-todo
vendored
Normal file
14
third_party/rust/url/rust-url-todo
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
* standalone path parsing?
|
||||
* Test setters
|
||||
* Test trim C0/space
|
||||
* Test remove tab & newline
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_path_segments() {
|
||||
let mut url = Url::parse("http://example.net").unwrap();
|
||||
url.push_path_segment("foo").unwrap();
|
||||
url.extend_path_segments(&["bar", "b/az"]).unwrap();
|
||||
assert_eq!(url.as_str(), "http://example.net/foo");
|
||||
}
|
||||
135
third_party/rust/url/src/encoding.rs
vendored
Normal file
135
third_party/rust/url/src/encoding.rs
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
// 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.
|
||||
|
||||
|
||||
//! Abstraction that conditionally compiles either to rust-encoding,
|
||||
//! or to only support UTF-8.
|
||||
|
||||
#[cfg(feature = "query_encoding")] extern crate encoding;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
#[cfg(feature = "query_encoding")] use self::encoding::types::{DecoderTrap, EncoderTrap};
|
||||
#[cfg(feature = "query_encoding")] use self::encoding::label::encoding_from_whatwg_label;
|
||||
#[cfg(feature = "query_encoding")] pub use self::encoding::types::EncodingRef;
|
||||
|
||||
#[cfg(feature = "query_encoding")]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct EncodingOverride {
|
||||
/// `None` means UTF-8.
|
||||
encoding: Option<EncodingRef>
|
||||
}
|
||||
|
||||
#[cfg(feature = "query_encoding")]
|
||||
impl EncodingOverride {
|
||||
pub fn from_opt_encoding(encoding: Option<EncodingRef>) -> Self {
|
||||
encoding.map(Self::from_encoding).unwrap_or_else(Self::utf8)
|
||||
}
|
||||
|
||||
pub fn from_encoding(encoding: EncodingRef) -> Self {
|
||||
EncodingOverride {
|
||||
encoding: if encoding.name() == "utf-8" { None } else { Some(encoding) }
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn utf8() -> Self {
|
||||
EncodingOverride { encoding: None }
|
||||
}
|
||||
|
||||
pub fn lookup(label: &[u8]) -> Option<Self> {
|
||||
// Don't use String::from_utf8_lossy since no encoding label contains U+FFFD
|
||||
// https://encoding.spec.whatwg.org/#names-and-labels
|
||||
::std::str::from_utf8(label)
|
||||
.ok()
|
||||
.and_then(encoding_from_whatwg_label)
|
||||
.map(Self::from_encoding)
|
||||
}
|
||||
|
||||
/// https://encoding.spec.whatwg.org/#get-an-output-encoding
|
||||
pub fn to_output_encoding(self) -> Self {
|
||||
if let Some(encoding) = self.encoding {
|
||||
if matches!(encoding.name(), "utf-16le" | "utf-16be") {
|
||||
return Self::utf8()
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn is_utf8(&self) -> bool {
|
||||
self.encoding.is_none()
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self.encoding {
|
||||
Some(encoding) => encoding.name(),
|
||||
None => "utf-8",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decode<'a>(&self, input: Cow<'a, [u8]>) -> Cow<'a, str> {
|
||||
match self.encoding {
|
||||
// `encoding.decode` never returns `Err` when called with `DecoderTrap::Replace`
|
||||
Some(encoding) => encoding.decode(&input, DecoderTrap::Replace).unwrap().into(),
|
||||
None => decode_utf8_lossy(input),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode<'a>(&self, input: Cow<'a, str>) -> Cow<'a, [u8]> {
|
||||
match self.encoding {
|
||||
// `encoding.encode` never returns `Err` when called with `EncoderTrap::NcrEscape`
|
||||
Some(encoding) => Cow::Owned(encoding.encode(&input, EncoderTrap::NcrEscape).unwrap()),
|
||||
None => encode_utf8(input)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "query_encoding"))]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct EncodingOverride;
|
||||
|
||||
#[cfg(not(feature = "query_encoding"))]
|
||||
impl EncodingOverride {
|
||||
#[inline]
|
||||
pub fn utf8() -> Self {
|
||||
EncodingOverride
|
||||
}
|
||||
|
||||
pub fn decode<'a>(&self, input: Cow<'a, [u8]>) -> Cow<'a, str> {
|
||||
decode_utf8_lossy(input)
|
||||
}
|
||||
|
||||
pub fn encode<'a>(&self, input: Cow<'a, str>) -> Cow<'a, [u8]> {
|
||||
encode_utf8(input)
|
||||
}
|
||||
}
|
||||
|
||||
pub 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode_utf8(input: Cow<str>) -> Cow<[u8]> {
|
||||
match input {
|
||||
Cow::Borrowed(s) => Cow::Borrowed(s.as_bytes()),
|
||||
Cow::Owned(s) => Cow::Owned(s.into_bytes())
|
||||
}
|
||||
}
|
||||
364
third_party/rust/url/src/form_urlencoded.rs
vendored
Normal file
364
third_party/rust/url/src/form_urlencoded.rs
vendored
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
// 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.
|
||||
|
||||
//! Parser and serializer for the [`application/x-www-form-urlencoded` syntax](
|
||||
//! http://url.spec.whatwg.org/#application/x-www-form-urlencoded),
|
||||
//! as used by HTML forms.
|
||||
//!
|
||||
//! Converts between a string (such as an URL’s query string)
|
||||
//! and a sequence of (name, value) pairs.
|
||||
|
||||
use encoding::EncodingOverride;
|
||||
use percent_encoding::{percent_encode_byte, percent_decode};
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::str;
|
||||
|
||||
|
||||
/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
|
||||
/// into a iterator of (name, value) pairs.
|
||||
///
|
||||
/// Use `parse(input.as_bytes())` to parse a `&str` string.
|
||||
///
|
||||
/// 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 {
|
||||
Parse {
|
||||
input: input,
|
||||
encoding: EncodingOverride::utf8(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Convert a byte string in the `application/x-www-form-urlencoded` syntax
|
||||
/// into a iterator of (name, value) pairs.
|
||||
///
|
||||
/// Use `parse(input.as_bytes())` to parse a `&str` string.
|
||||
///
|
||||
/// This function is only available if the `query_encoding` Cargo feature is enabled.
|
||||
///
|
||||
/// Arguments:
|
||||
///
|
||||
/// * `encoding_override`: The character encoding each name and values is decoded as
|
||||
/// after percent-decoding. Defaults to UTF-8.
|
||||
/// * `use_charset`: The *use _charset_ flag*. If in doubt, set to `false`.
|
||||
#[cfg(feature = "query_encoding")]
|
||||
pub fn parse_with_encoding<'a>(input: &'a [u8],
|
||||
encoding_override: Option<::encoding::EncodingRef>,
|
||||
use_charset: bool)
|
||||
-> Result<Parse<'a>, ()> {
|
||||
use std::ascii::AsciiExt;
|
||||
|
||||
let mut encoding = EncodingOverride::from_opt_encoding(encoding_override);
|
||||
if !(encoding.is_utf8() || input.is_ascii()) {
|
||||
return Err(())
|
||||
}
|
||||
if use_charset {
|
||||
for sequence in input.split(|&b| b == b'&') {
|
||||
// No '+' in "_charset_" to replace with ' '.
|
||||
if sequence.starts_with(b"_charset_=") {
|
||||
let value = &sequence[b"_charset_=".len()..];
|
||||
// Skip replacing '+' with ' ' in value since no encoding label contains either:
|
||||
// https://encoding.spec.whatwg.org/#names-and-labels
|
||||
if let Some(e) = EncodingOverride::lookup(value) {
|
||||
encoding = e;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(Parse {
|
||||
input: input,
|
||||
encoding: encoding,
|
||||
})
|
||||
}
|
||||
|
||||
/// The return type of `parse()`.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Parse<'a> {
|
||||
input: &'a [u8],
|
||||
encoding: EncodingOverride,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for Parse<'a> {
|
||||
type Item = (Cow<'a, str>, Cow<'a, str>);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if self.input.is_empty() {
|
||||
return None
|
||||
}
|
||||
let mut split2 = self.input.splitn(2, |&b| b == b'&');
|
||||
let sequence = split2.next().unwrap();
|
||||
self.input = split2.next().unwrap_or(&[][..]);
|
||||
if sequence.is_empty() {
|
||||
continue
|
||||
}
|
||||
let mut split2 = sequence.splitn(2, |&b| b == b'=');
|
||||
let name = split2.next().unwrap();
|
||||
let value = split2.next().unwrap_or(&[][..]);
|
||||
return Some((
|
||||
decode(name, self.encoding),
|
||||
decode(value, self.encoding),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decode(input: &[u8], encoding: EncodingOverride) -> Cow<str> {
|
||||
let replaced = replace_plus(input);
|
||||
encoding.decode(match percent_decode(&replaced).if_any() {
|
||||
Some(vec) => Cow::Owned(vec),
|
||||
None => replaced,
|
||||
})
|
||||
}
|
||||
|
||||
/// Replace b'+' with b' '
|
||||
fn replace_plus<'a>(input: &'a [u8]) -> Cow<'a, [u8]> {
|
||||
match input.iter().position(|&b| b == b'+') {
|
||||
None => Cow::Borrowed(input),
|
||||
Some(first_position) => {
|
||||
let mut replaced = input.to_owned();
|
||||
replaced[first_position] = b' ';
|
||||
for byte in &mut replaced[first_position + 1..] {
|
||||
if *byte == b'+' {
|
||||
*byte = b' ';
|
||||
}
|
||||
}
|
||||
Cow::Owned(replaced)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Parse<'a> {
|
||||
/// Return a new iterator that yields pairs of `String` instead of pairs of `Cow<str>`.
|
||||
pub fn into_owned(self) -> ParseIntoOwned<'a> {
|
||||
ParseIntoOwned { inner: self }
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `Parse`, but yields pairs of `String` instead of pairs of `Cow<str>`.
|
||||
pub struct ParseIntoOwned<'a> {
|
||||
inner: Parse<'a>
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ParseIntoOwned<'a> {
|
||||
type Item = (String, String);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().map(|(k, v)| (k.into_owned(), v.into_owned()))
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`application/x-www-form-urlencoded` byte serializer](
|
||||
/// https://url.spec.whatwg.org/#concept-urlencoded-byte-serializer).
|
||||
///
|
||||
/// Return an iterator of `&str` slices.
|
||||
pub fn byte_serialize(input: &[u8]) -> ByteSerialize {
|
||||
ByteSerialize {
|
||||
bytes: input,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return value of `byte_serialize()`.
|
||||
pub struct ByteSerialize<'a> {
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
|
||||
fn byte_serialized_unchanged(byte: u8) -> bool {
|
||||
matches!(byte, b'*' | b'-' | b'.' | b'0' ... b'9' | b'A' ... b'Z' | b'_' | b'a' ... b'z')
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ByteSerialize<'a> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
if let Some((&first, tail)) = self.bytes.split_first() {
|
||||
if !byte_serialized_unchanged(first) {
|
||||
self.bytes = tail;
|
||||
return Some(if first == b' ' { "+" } else { percent_encode_byte(first) })
|
||||
}
|
||||
let position = tail.iter().position(|&b| !byte_serialized_unchanged(b));
|
||||
let (unchanged_slice, remaining) = match position {
|
||||
// 1 for first_byte + i unchanged in tail
|
||||
Some(i) => self.bytes.split_at(1 + i),
|
||||
None => (self.bytes, &[][..]),
|
||||
};
|
||||
self.bytes = remaining;
|
||||
Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
if self.bytes.is_empty() {
|
||||
(0, Some(0))
|
||||
} else {
|
||||
(1, Some(self.bytes.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`application/x-www-form-urlencoded` serializer](
|
||||
/// https://url.spec.whatwg.org/#concept-urlencoded-serializer).
|
||||
pub struct Serializer<T: Target> {
|
||||
target: Option<T>,
|
||||
start_position: usize,
|
||||
encoding: EncodingOverride,
|
||||
}
|
||||
|
||||
pub trait Target {
|
||||
fn as_mut_string(&mut self) -> &mut String;
|
||||
fn finish(self) -> Self::Finished;
|
||||
type Finished;
|
||||
}
|
||||
|
||||
impl Target for String {
|
||||
fn as_mut_string(&mut self) -> &mut String { self }
|
||||
fn finish(self) -> Self { self }
|
||||
type Finished = Self;
|
||||
}
|
||||
|
||||
impl<'a> Target for &'a mut String {
|
||||
fn as_mut_string(&mut self) -> &mut String { &mut **self }
|
||||
fn finish(self) -> Self { self }
|
||||
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.serialization }
|
||||
fn finish(self) -> &'a mut ::Url { self.url }
|
||||
type Finished = &'a mut ::Url;
|
||||
}
|
||||
|
||||
impl<T: Target> Serializer<T> {
|
||||
/// Create a new `application/x-www-form-urlencoded` serializer for the given target.
|
||||
///
|
||||
/// If the target is non-empty,
|
||||
/// its content is assumed to already be in `application/x-www-form-urlencoded` syntax.
|
||||
pub fn new(target: T) -> Self {
|
||||
Self::for_suffix(target, 0)
|
||||
}
|
||||
|
||||
/// Create a new `application/x-www-form-urlencoded` serializer
|
||||
/// for a suffix of the given target.
|
||||
///
|
||||
/// 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
|
||||
Serializer {
|
||||
target: Some(target),
|
||||
start_position: start_position,
|
||||
encoding: EncodingOverride::utf8(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove any existing name/value pair.
|
||||
///
|
||||
/// Panics if called after `.finish()`.
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
string(&mut self.target).truncate(self.start_position);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the character encoding to be used for names and values before percent-encoding.
|
||||
#[cfg(feature = "query_encoding")]
|
||||
pub fn encoding_override(&mut self, new: Option<::encoding::EncodingRef>) -> &mut Self {
|
||||
self.encoding = EncodingOverride::from_opt_encoding(new).to_output_encoding();
|
||||
self
|
||||
}
|
||||
|
||||
/// Serialize and append a name/value pair.
|
||||
///
|
||||
/// Panics if called after `.finish()`.
|
||||
pub fn append_pair(&mut self, name: &str, value: &str) -> &mut Self {
|
||||
append_pair(string(&mut self.target), self.start_position, self.encoding, name, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Serialize and append a number of name/value pairs.
|
||||
///
|
||||
/// This simply calls `append_pair` repeatedly.
|
||||
/// This can be more convenient, so the user doesn’t need to introduce a block
|
||||
/// to limit the scope of `Serializer`’s borrow of its string.
|
||||
///
|
||||
/// Panics if called after `.finish()`.
|
||||
pub fn extend_pairs<I, K, V>(&mut self, iter: I) -> &mut Self
|
||||
where I: IntoIterator, I::Item: Borrow<(K, V)>, K: AsRef<str>, V: AsRef<str> {
|
||||
{
|
||||
let string = string(&mut self.target);
|
||||
for pair in iter {
|
||||
let &(ref k, ref v) = pair.borrow();
|
||||
append_pair(string, self.start_position, self.encoding, k.as_ref(), v.as_ref());
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// Add a name/value pair whose name is `_charset_`
|
||||
/// and whose value is the character encoding’s name.
|
||||
/// (See the `encoding_override()` method.)
|
||||
///
|
||||
/// Panics if called after `.finish()`.
|
||||
#[cfg(feature = "query_encoding")]
|
||||
pub fn append_charset(&mut self) -> &mut Self {
|
||||
{
|
||||
let string = string(&mut self.target);
|
||||
append_separator_if_needed(string, self.start_position);
|
||||
string.push_str("_charset_=");
|
||||
string.push_str(self.encoding.name());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// If this serializer was constructed with a string, take and return that string.
|
||||
///
|
||||
/// ```rust
|
||||
/// use url::form_urlencoded;
|
||||
/// let encoded: String = form_urlencoded::Serializer::new(String::new())
|
||||
/// .append_pair("foo", "bar & baz")
|
||||
/// .append_pair("saison", "Été+hiver")
|
||||
/// .finish();
|
||||
/// assert_eq!(encoded, "foo=bar+%26+baz&saison=%C3%89t%C3%A9%2Bhiver");
|
||||
/// ```
|
||||
///
|
||||
/// Panics if called more than once.
|
||||
pub fn finish(&mut self) -> T::Finished {
|
||||
self.target.take().expect("url::form_urlencoded::Serializer double finish").finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn append_separator_if_needed(string: &mut String, start_position: usize) {
|
||||
if string.len() > start_position {
|
||||
string.push('&')
|
||||
}
|
||||
}
|
||||
|
||||
fn string<T: Target>(target: &mut Option<T>) -> &mut String {
|
||||
target.as_mut().expect("url::form_urlencoded::Serializer finished").as_mut_string()
|
||||
}
|
||||
|
||||
fn append_pair(string: &mut String, start_position: usize, encoding: EncodingOverride,
|
||||
name: &str, value: &str) {
|
||||
append_separator_if_needed(string, start_position);
|
||||
string.extend(byte_serialize(&encoding.encode(name.into())));
|
||||
string.push('=');
|
||||
string.extend(byte_serialize(&encoding.encode(value.into())));
|
||||
}
|
||||
418
third_party/rust/url/src/host.rs
vendored
Normal file
418
third_party/rust/url/src/host.rs
vendored
Normal file
|
|
@ -0,0 +1,418 @@
|
|||
// 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.
|
||||
|
||||
#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
|
||||
use std::cmp;
|
||||
use std::fmt::{self, Formatter};
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs};
|
||||
use std::vec;
|
||||
use parser::{ParseResult, ParseError};
|
||||
use percent_encoding::percent_decode;
|
||||
use idna;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum HostInternal {
|
||||
None,
|
||||
Domain,
|
||||
Ipv4(Ipv4Addr),
|
||||
Ipv6(Ipv6Addr),
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapsize")]
|
||||
known_heap_size!(0, HostInternal);
|
||||
|
||||
impl<S> From<Host<S>> for HostInternal {
|
||||
fn from(host: Host<S>) -> HostInternal {
|
||||
match host {
|
||||
Host::Domain(_) => HostInternal::Domain,
|
||||
Host::Ipv4(address) => HostInternal::Ipv4(address),
|
||||
Host::Ipv6(address) => HostInternal::Ipv6(address),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The host name of an URL.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, 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.
|
||||
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),
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapsize")]
|
||||
impl<S: HeapSizeOf> HeapSizeOf for Host<S> {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
match *self {
|
||||
Host::Domain(ref s) => s.heap_size_of_children(),
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = try!(idna::domain_to_ascii(&domain));
|
||||
if domain.find(|c| matches!(c,
|
||||
'\0' | '\t' | '\n' | '\r' | ' ' | '#' | '%' | '/' | ':' | '?' | '@' | '[' | '\\' | ']'
|
||||
)).is_some() {
|
||||
return Err(ParseError::InvalidDomainCharacter)
|
||||
}
|
||||
if let Some(address) = try!(parse_ipv4addr(&domain)) {
|
||||
Ok(Host::Ipv4(address))
|
||||
} else {
|
||||
Ok(Host::Domain(domain.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) => {
|
||||
try!(f.write_str("["));
|
||||
try!(write_ipv6(addr, f));
|
||||
f.write_str("]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This mostly exists because coherence rules don’t allow us to implement
|
||||
/// `ToSocketAddrs for (Host<S>, u16)`.
|
||||
pub struct HostAndPort<S=String> {
|
||||
pub host: Host<S>,
|
||||
pub port: u16,
|
||||
}
|
||||
|
||||
impl<'a> HostAndPort<&'a str> {
|
||||
/// Return a copy of `self` that owns an allocated `String` but does not borrow an `&Url`.
|
||||
pub fn to_owned(&self) -> HostAndPort<String> {
|
||||
HostAndPort {
|
||||
host: self.host.to_owned(),
|
||||
port: self.port
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> ToSocketAddrs for HostAndPort<S> {
|
||||
type Iter = SocketAddrs;
|
||||
|
||||
fn to_socket_addrs(&self) -> io::Result<Self::Iter> {
|
||||
let port = self.port;
|
||||
match self.host {
|
||||
Host::Domain(ref domain) => Ok(SocketAddrs {
|
||||
// FIXME: use std::net::lookup_host when it’s stable.
|
||||
state: SocketAddrsState::Domain(try!((domain.as_ref(), port).to_socket_addrs()))
|
||||
}),
|
||||
Host::Ipv4(address) => Ok(SocketAddrs {
|
||||
state: SocketAddrsState::One(SocketAddr::V4(SocketAddrV4::new(address, port)))
|
||||
}),
|
||||
Host::Ipv6(address) => Ok(SocketAddrs {
|
||||
state: SocketAddrsState::One(SocketAddr::V6(SocketAddrV6::new(address, port, 0, 0)))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Socket addresses for an URL.
|
||||
pub struct SocketAddrs {
|
||||
state: SocketAddrsState
|
||||
}
|
||||
|
||||
enum SocketAddrsState {
|
||||
Domain(vec::IntoIter<SocketAddr>),
|
||||
One(SocketAddr),
|
||||
Done,
|
||||
}
|
||||
|
||||
impl Iterator for SocketAddrs {
|
||||
type Item = SocketAddr;
|
||||
fn next(&mut self) -> Option<SocketAddr> {
|
||||
match self.state {
|
||||
SocketAddrsState::Domain(ref mut iter) => iter.next(),
|
||||
SocketAddrsState::One(s) => {
|
||||
self.state = SocketAddrsState::Done;
|
||||
Some(s)
|
||||
}
|
||||
SocketAddrsState::Done => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
try!(f.write_str(":"));
|
||||
if i == 0 {
|
||||
try!(f.write_str(":"));
|
||||
}
|
||||
if compress_end < 8 {
|
||||
i = compress_end;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try!(write!(f, "{:x}", segments[i as usize]));
|
||||
if i < 7 {
|
||||
try!(f.write_str(":"));
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
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);
|
||||
(longest, longest + longest_length)
|
||||
}
|
||||
|
||||
|
||||
fn parse_ipv4number(mut input: &str) -> Result<u32, ()> {
|
||||
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(0);
|
||||
}
|
||||
if input.starts_with("+") {
|
||||
return Err(())
|
||||
}
|
||||
match u32::from_str_radix(&input, r) {
|
||||
Ok(number) => Ok(number),
|
||||
Err(_) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_ipv4addr(input: &str) -> ParseResult<Option<Ipv4Addr>> {
|
||||
if input.is_empty() {
|
||||
return Ok(None)
|
||||
}
|
||||
let mut parts: Vec<&str> = input.split('.').collect();
|
||||
if parts.last() == Some(&"") {
|
||||
parts.pop();
|
||||
}
|
||||
if parts.len() > 4 {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut numbers: Vec<u32> = Vec::new();
|
||||
for part in parts {
|
||||
if part == "" {
|
||||
return Ok(None);
|
||||
}
|
||||
if let Ok(n) = parse_ipv4number(part) {
|
||||
numbers.push(n);
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
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(Some(Ipv4Addr::from(ipv4)))
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
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 dots_seen = 0;
|
||||
while i < len {
|
||||
// FIXME: https://github.com/whatwg/url/commit/1c22aa119c354e0020117e02571cec53f7c01064
|
||||
let mut value = 0u16;
|
||||
while i < len {
|
||||
let digit = match input[i] {
|
||||
c @ b'0' ... b'9' => c - b'0',
|
||||
_ => break
|
||||
};
|
||||
value = value * 10 + digit as u16;
|
||||
if value == 0 || value > 255 {
|
||||
return Err(ParseError::InvalidIpv6Address)
|
||||
}
|
||||
}
|
||||
if dots_seen < 3 && !(i < len && input[i] == b'.') {
|
||||
return Err(ParseError::InvalidIpv6Address)
|
||||
}
|
||||
pieces[piece_pointer] = pieces[piece_pointer] * 0x100 + value;
|
||||
if dots_seen == 0 || dots_seen == 2 {
|
||||
piece_pointer += 1;
|
||||
}
|
||||
i += 1;
|
||||
if dots_seen == 3 && i < len {
|
||||
return Err(ParseError::InvalidIpv6Address)
|
||||
}
|
||||
dots_seen += 1;
|
||||
}
|
||||
}
|
||||
|
||||
match compress_pointer {
|
||||
Some(compress_pointer) => {
|
||||
let mut swaps = piece_pointer - compress_pointer;
|
||||
piece_pointer = 7;
|
||||
while swaps > 0 {
|
||||
pieces[piece_pointer] = pieces[compress_pointer + swaps - 1];
|
||||
pieces[compress_pointer + swaps - 1] = 0;
|
||||
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]))
|
||||
}
|
||||
1456
third_party/rust/url/src/lib.rs
vendored
Normal file
1456
third_party/rust/url/src/lib.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
114
third_party/rust/url/src/origin.rs
vendored
Normal file
114
third_party/rust/url/src/origin.rs
vendored
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// 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.
|
||||
|
||||
#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
|
||||
use host::Host;
|
||||
use idna::domain_to_unicode;
|
||||
use parser::default_port;
|
||||
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
|
||||
use Url;
|
||||
|
||||
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" | "gopher" | "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
|
||||
#[derive(PartialEq, Eq, 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)
|
||||
}
|
||||
|
||||
#[cfg(feature = "heapsize")]
|
||||
impl HeapSizeOf for Origin {
|
||||
fn heap_size_of_children(&self) -> usize {
|
||||
match *self {
|
||||
Origin::Tuple(ref scheme, ref host, _) => {
|
||||
scheme.heap_size_of_children() +
|
||||
host.heap_size_of_children()
|
||||
},
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Origin {
|
||||
/// Creates a new opaque origin that is only equal to itself.
|
||||
pub fn new_opaque() -> Origin {
|
||||
static COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
|
||||
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) = 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, Clone, Debug)]
|
||||
pub struct OpaqueOrigin(usize);
|
||||
|
||||
#[cfg(feature = "heapsize")]
|
||||
known_heap_size!(0, OpaqueOrigin);
|
||||
1179
third_party/rust/url/src/parser.rs
vendored
Normal file
1179
third_party/rust/url/src/parser.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
187
third_party/rust/url/src/path_segments.rs
vendored
Normal file
187
third_party/rust/url/src/path_segments.rs
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
// 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 parser::{self, SchemeType, to_u32};
|
||||
use std::str;
|
||||
use Url;
|
||||
|
||||
/// 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;
|
||||
/// let mut url = Url::parse("mailto:me@example.com").unwrap();
|
||||
/// assert!(url.path_segments_mut().is_err());
|
||||
///
|
||||
/// let mut url = Url::parse("http://example.net/foo/index.html").unwrap();
|
||||
/// url.path_segments_mut().unwrap().pop().push("img").push("2/100%.png");
|
||||
/// assert_eq!(url.as_str(), "http://example.net/foo/img/2%2F100%25.png");
|
||||
/// ```
|
||||
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();
|
||||
debug_assert!(url.byte_at(url.path_start) == b'/');
|
||||
PathSegmentsMut {
|
||||
after_first_slash: url.path_start as usize + "/".len(),
|
||||
url: url,
|
||||
old_after_path_position: old_after_path_position,
|
||||
after_path: 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;
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/").unwrap();
|
||||
/// url.path_segments_mut().unwrap().clear().push("logout");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/logout");
|
||||
/// ```
|
||||
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;
|
||||
/// let mut url = Url::parse("https://github.com/servo/rust-url/").unwrap();
|
||||
/// url.path_segments_mut().unwrap().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/").unwrap();
|
||||
/// url.path_segments_mut().unwrap().pop_if_empty().push("pulls");
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// ```
|
||||
pub fn pop_if_empty(&mut self) -> &mut 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 {
|
||||
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;
|
||||
/// let mut url = Url::parse("https://github.com/").unwrap();
|
||||
/// let org = "servo";
|
||||
/// let repo = "rust-url";
|
||||
/// let issue_number = "188";
|
||||
/// url.path_segments_mut().unwrap().extend(&[org, repo, "issues", issue_number]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/issues/188");
|
||||
/// ```
|
||||
///
|
||||
/// 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;
|
||||
/// let mut url = Url::parse("https://github.com/servo").unwrap();
|
||||
/// url.path_segments_mut().unwrap().extend(&["..", "rust-url", ".", "pulls"]);
|
||||
/// assert_eq!(url.as_str(), "https://github.com/servo/rust-url/pulls");
|
||||
/// ```
|
||||
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 {
|
||||
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(segment));
|
||||
}
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
/// For internal testing, not part of the public API.
|
||||
#[doc(hidden)]
|
||||
pub fn assert_url_invariants(&mut self) -> &mut Self {
|
||||
self.url.assert_invariants();
|
||||
self
|
||||
}
|
||||
}
|
||||
344
third_party/rust/url/src/percent_encoding.rs
vendored
Normal file
344
third_party/rust/url/src/percent_encoding.rs
vendored
Normal file
|
|
@ -0,0 +1,344 @@
|
|||
// 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 encoding;
|
||||
use std::ascii::AsciiExt;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::slice;
|
||||
use std::str;
|
||||
|
||||
/// Represents a set of characters / bytes that should be percent-encoded.
|
||||
///
|
||||
/// See [encode sets specification](http://url.spec.whatwg.org/#simple-encode-set).
|
||||
///
|
||||
/// Different characters need to be encoded in different parts of an URL.
|
||||
/// For example, a literal `?` question mark in an URL’s path would indicate
|
||||
/// the start of the query string.
|
||||
/// A question mark meant to be part of the path therefore needs to be percent-encoded.
|
||||
/// In the query string however, a question mark does not have any special meaning
|
||||
/// and does not need to be percent-encoded.
|
||||
///
|
||||
/// A few sets are defined in this module.
|
||||
/// Use the [`define_encode_set!`](../macro.define_encode_set!.html) macro to define different ones.
|
||||
pub trait EncodeSet: Clone {
|
||||
/// Called with UTF-8 bytes rather than code points.
|
||||
/// Should return true for all non-ASCII bytes.
|
||||
fn contains(&self, byte: u8) -> bool;
|
||||
}
|
||||
|
||||
/// Define a new struct
|
||||
/// that implements the [`EncodeSet`](percent_encoding/trait.EncodeSet.html) trait,
|
||||
/// for use in [`percent_decode()`](percent_encoding/fn.percent_encode.html)
|
||||
/// and related functions.
|
||||
///
|
||||
/// Parameters are characters to include in the set in addition to those of the base set.
|
||||
/// See [encode sets specification](http://url.spec.whatwg.org/#simple-encode-set).
|
||||
///
|
||||
/// Example
|
||||
/// =======
|
||||
///
|
||||
/// ```rust
|
||||
/// #[macro_use] extern crate url;
|
||||
/// use url::percent_encoding::{utf8_percent_encode, SIMPLE_ENCODE_SET};
|
||||
/// define_encode_set! {
|
||||
/// /// This encode set is used in the URL parser for query strings.
|
||||
/// pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
|
||||
/// }
|
||||
/// # fn main() {
|
||||
/// assert_eq!(utf8_percent_encode("foo bar", QUERY_ENCODE_SET).collect::<String>(), "foo%20bar");
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_encode_set {
|
||||
($(#[$attr: meta])* pub $name: ident = [$base_set: expr] | {$($ch: pat),*}) => {
|
||||
$(#[$attr])*
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct $name;
|
||||
|
||||
impl $crate::percent_encoding::EncodeSet for $name {
|
||||
#[inline]
|
||||
fn contains(&self, byte: u8) -> bool {
|
||||
match byte as char {
|
||||
$(
|
||||
$ch => true,
|
||||
)*
|
||||
_ => $base_set.contains(byte)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This encode set is used for the path of cannot-be-a-base URLs.
|
||||
#[derive(Copy, Clone)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct SIMPLE_ENCODE_SET;
|
||||
|
||||
impl EncodeSet for SIMPLE_ENCODE_SET {
|
||||
#[inline]
|
||||
fn contains(&self, byte: u8) -> bool {
|
||||
byte < 0x20 || byte > 0x7E
|
||||
}
|
||||
}
|
||||
|
||||
define_encode_set! {
|
||||
/// This encode set is used in the URL parser for query strings.
|
||||
pub QUERY_ENCODE_SET = [SIMPLE_ENCODE_SET] | {' ', '"', '#', '<', '>'}
|
||||
}
|
||||
|
||||
define_encode_set! {
|
||||
/// This encode set is used for path components.
|
||||
pub DEFAULT_ENCODE_SET = [QUERY_ENCODE_SET] | {'`', '?', '{', '}'}
|
||||
}
|
||||
|
||||
define_encode_set! {
|
||||
/// This encode set is used for on '/'-separated path segment
|
||||
pub PATH_SEGMENT_ENCODE_SET = [DEFAULT_ENCODE_SET] | {'%', '/'}
|
||||
}
|
||||
|
||||
define_encode_set! {
|
||||
/// This encode set is used for username and password.
|
||||
pub USERINFO_ENCODE_SET = [DEFAULT_ENCODE_SET] | {
|
||||
'/', ':', ';', '=', '@', '[', '\\', ']', '^', '|'
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the percent-encoding of the given bytes.
|
||||
///
|
||||
/// This is unconditional, unlike `percent_encode()` which uses an encode set.
|
||||
pub fn percent_encode_byte(byte: u8) -> &'static str {
|
||||
let index = usize::from(byte) * 3;
|
||||
&"\
|
||||
%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F\
|
||||
%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F\
|
||||
%20%21%22%23%24%25%26%27%28%29%2A%2B%2C%2D%2E%2F\
|
||||
%30%31%32%33%34%35%36%37%38%39%3A%3B%3C%3D%3E%3F\
|
||||
%40%41%42%43%44%45%46%47%48%49%4A%4B%4C%4D%4E%4F\
|
||||
%50%51%52%53%54%55%56%57%58%59%5A%5B%5C%5D%5E%5F\
|
||||
%60%61%62%63%64%65%66%67%68%69%6A%6B%6C%6D%6E%6F\
|
||||
%70%71%72%73%74%75%76%77%78%79%7A%7B%7C%7D%7E%7F\
|
||||
%80%81%82%83%84%85%86%87%88%89%8A%8B%8C%8D%8E%8F\
|
||||
%90%91%92%93%94%95%96%97%98%99%9A%9B%9C%9D%9E%9F\
|
||||
%A0%A1%A2%A3%A4%A5%A6%A7%A8%A9%AA%AB%AC%AD%AE%AF\
|
||||
%B0%B1%B2%B3%B4%B5%B6%B7%B8%B9%BA%BB%BC%BD%BE%BF\
|
||||
%C0%C1%C2%C3%C4%C5%C6%C7%C8%C9%CA%CB%CC%CD%CE%CF\
|
||||
%D0%D1%D2%D3%D4%D5%D6%D7%D8%D9%DA%DB%DC%DD%DE%DF\
|
||||
%E0%E1%E2%E3%E4%E5%E6%E7%E8%E9%EA%EB%EC%ED%EE%EF\
|
||||
%F0%F1%F2%F3%F4%F5%F6%F7%F8%F9%FA%FB%FC%FD%FE%FF\
|
||||
"[index..index + 3]
|
||||
}
|
||||
|
||||
/// Percent-encode the given bytes with the given encode set.
|
||||
///
|
||||
/// The encode set define which bytes (in addition to non-ASCII and controls)
|
||||
/// need to be percent-encoded.
|
||||
/// The choice of this set depends on context.
|
||||
/// For example, `?` needs to be encoded in an URL path but not in a query string.
|
||||
///
|
||||
/// The return value is an iterator of `&str` slices (so it has a `.collect::<String>()` method)
|
||||
/// that also implements `Display` and `Into<Cow<str>>`.
|
||||
/// The latter returns `Cow::Borrowed` when none of the bytes in `input`
|
||||
/// are in the given encode set.
|
||||
#[inline]
|
||||
pub fn percent_encode<E: EncodeSet>(input: &[u8], encode_set: E) -> PercentEncode<E> {
|
||||
PercentEncode {
|
||||
bytes: input,
|
||||
encode_set: encode_set,
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-encode the UTF-8 encoding of the given string.
|
||||
///
|
||||
/// See `percent_encode()` for how to use the return value.
|
||||
#[inline]
|
||||
pub fn utf8_percent_encode<E: EncodeSet>(input: &str, encode_set: E) -> PercentEncode<E> {
|
||||
percent_encode(input.as_bytes(), encode_set)
|
||||
}
|
||||
|
||||
/// The return type of `percent_encode()` and `utf8_percent_encode()`.
|
||||
#[derive(Clone)]
|
||||
pub struct PercentEncode<'a, E: EncodeSet> {
|
||||
bytes: &'a [u8],
|
||||
encode_set: E,
|
||||
}
|
||||
|
||||
impl<'a, E: EncodeSet> Iterator for PercentEncode<'a, E> {
|
||||
type Item = &'a str;
|
||||
|
||||
fn next(&mut self) -> Option<&'a str> {
|
||||
if let Some((&first_byte, remaining)) = self.bytes.split_first() {
|
||||
if self.encode_set.contains(first_byte) {
|
||||
self.bytes = remaining;
|
||||
Some(percent_encode_byte(first_byte))
|
||||
} else {
|
||||
assert!(first_byte.is_ascii());
|
||||
for (i, &byte) in remaining.iter().enumerate() {
|
||||
if self.encode_set.contains(byte) {
|
||||
// 1 for first_byte + i for previous iterations of this loop
|
||||
let (unchanged_slice, remaining) = self.bytes.split_at(1 + i);
|
||||
self.bytes = remaining;
|
||||
return Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
|
||||
} else {
|
||||
assert!(byte.is_ascii());
|
||||
}
|
||||
}
|
||||
let unchanged_slice = self.bytes;
|
||||
self.bytes = &[][..];
|
||||
Some(unsafe { str::from_utf8_unchecked(unchanged_slice) })
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
if self.bytes.is_empty() {
|
||||
(0, Some(0))
|
||||
} else {
|
||||
(1, Some(self.bytes.len()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: EncodeSet> fmt::Display for PercentEncode<'a, E> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
for c in (*self).clone() {
|
||||
try!(formatter.write_str(c))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E: EncodeSet> From<PercentEncode<'a, E>> for Cow<'a, str> {
|
||||
fn from(mut iter: PercentEncode<'a, E>) -> Self {
|
||||
match iter.next() {
|
||||
None => "".into(),
|
||||
Some(first) => {
|
||||
match iter.next() {
|
||||
None => first.into(),
|
||||
Some(second) => {
|
||||
let mut string = first.to_owned();
|
||||
string.push_str(second);
|
||||
string.extend(iter);
|
||||
string.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Percent-decode the given bytes.
|
||||
///
|
||||
/// The return value is an iterator of decoded `u8` bytes
|
||||
/// that also implements `Into<Cow<u8>>`
|
||||
/// (which returns `Cow::Borrowed` when `input` contains no percent-encoded sequence)
|
||||
/// and has `decode_utf8()` and `decode_utf8_lossy()` methods.
|
||||
#[inline]
|
||||
pub fn percent_decode<'a>(input: &'a [u8]) -> PercentDecode<'a> {
|
||||
PercentDecode {
|
||||
bytes: input.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// The return type of `percent_decode()`.
|
||||
#[derive(Clone)]
|
||||
pub struct PercentDecode<'a> {
|
||||
bytes: slice::Iter<'a, u8>,
|
||||
}
|
||||
|
||||
fn after_percent_sign(iter: &mut slice::Iter<u8>) -> Option<u8> {
|
||||
let initial_iter = iter.clone();
|
||||
let h = iter.next().and_then(|&b| (b as char).to_digit(16));
|
||||
let l = iter.next().and_then(|&b| (b as char).to_digit(16));
|
||||
if let (Some(h), Some(l)) = (h, l) {
|
||||
Some(h as u8 * 0x10 + l as u8)
|
||||
} else {
|
||||
*iter = initial_iter;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Iterator for PercentDecode<'a> {
|
||||
type Item = u8;
|
||||
|
||||
fn next(&mut self) -> Option<u8> {
|
||||
self.bytes.next().map(|&byte| {
|
||||
if byte == b'%' {
|
||||
after_percent_sign(&mut self.bytes).unwrap_or(byte)
|
||||
} else {
|
||||
byte
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let bytes = self.bytes.len();
|
||||
(bytes / 3, Some(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<PercentDecode<'a>> for Cow<'a, [u8]> {
|
||||
fn from(iter: PercentDecode<'a>) -> Self {
|
||||
match iter.if_any() {
|
||||
Some(vec) => Cow::Owned(vec),
|
||||
None => Cow::Borrowed(iter.bytes.as_slice()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PercentDecode<'a> {
|
||||
/// If the percent-decoding is different from the input, return it as a new bytes vector.
|
||||
pub fn if_any(&self) -> Option<Vec<u8>> {
|
||||
let mut bytes_iter = self.bytes.clone();
|
||||
while bytes_iter.find(|&&b| b == b'%').is_some() {
|
||||
if let Some(decoded_byte) = after_percent_sign(&mut bytes_iter) {
|
||||
let initial_bytes = self.bytes.as_slice();
|
||||
let unchanged_bytes_len = initial_bytes.len() - bytes_iter.len() - 3;
|
||||
let mut decoded = initial_bytes[..unchanged_bytes_len].to_owned();
|
||||
decoded.push(decoded_byte);
|
||||
decoded.extend(PercentDecode {
|
||||
bytes: bytes_iter
|
||||
});
|
||||
return Some(decoded)
|
||||
}
|
||||
}
|
||||
// Nothing to decode
|
||||
None
|
||||
}
|
||||
|
||||
/// Decode the result of percent-decoding as UTF-8.
|
||||
///
|
||||
/// This is return `Err` when the percent-decoded bytes are not well-formed in UTF-8.
|
||||
pub fn decode_utf8(self) -> Result<Cow<'a, str>, str::Utf8Error> {
|
||||
match self.clone().into() {
|
||||
Cow::Borrowed(bytes) => {
|
||||
match str::from_utf8(bytes) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
Cow::Owned(bytes) => {
|
||||
match String::from_utf8(bytes) {
|
||||
Ok(s) => Ok(s.into()),
|
||||
Err(e) => Err(e.utf8_error()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode the result of percent-decoding as UTF-8, lossily.
|
||||
///
|
||||
/// Invalid UTF-8 percent-encoded byte sequences will be replaced <20> U+FFFD,
|
||||
/// the replacement character.
|
||||
pub fn decode_utf8_lossy(self) -> Cow<'a, str> {
|
||||
encoding::decode_utf8_lossy(self.clone().into())
|
||||
}
|
||||
}
|
||||
217
third_party/rust/url/src/quirks.rs
vendored
Normal file
217
third_party/rust/url/src/quirks.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.
|
||||
|
||||
//! 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 {Url, Position, Host, ParseError, idna};
|
||||
use parser::{Parser, SchemeType, default_port, Context, Input};
|
||||
|
||||
/// 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 = try!(Url::parse(value));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Getter for https://url.spec.whatwg.org/#dom-url-origin
|
||||
pub fn origin(url: &Url) -> String {
|
||||
url.origin().unicode_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
|
||||
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
|
||||
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
|
||||
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
|
||||
pub fn set_host(url: &mut Url, new_host: &str) -> Result<(), ()> {
|
||||
if url.cannot_be_a_base() {
|
||||
return Err(())
|
||||
}
|
||||
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(':') {
|
||||
Parser::parse_port(remaining, || default_port(scheme), Context::Setter)
|
||||
.ok().map(|(port, _remaining)| port)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
}
|
||||
Err(_) => 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
|
||||
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 {
|
||||
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
|
||||
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() || scheme == "file" {
|
||||
return Err(())
|
||||
}
|
||||
result = Parser::parse_port(Input::new(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() {
|
||||
url.set_path(new_pathname)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if url.scheme() != "javascript" {
|
||||
url.set_fragment(match new_hash {
|
||||
"" => None,
|
||||
_ if new_hash.starts_with('#') => Some(&new_hash[1..]),
|
||||
_ => Some(new_hash),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn trim(s: &str) -> &str {
|
||||
if s.len() == 1 {
|
||||
""
|
||||
} else {
|
||||
s
|
||||
}
|
||||
}
|
||||
182
third_party/rust/url/src/slicing.rs
vendored
Normal file
182
third_party/rust/url/src/slicing.rs
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
// 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 std::ops::{Range, RangeFrom, RangeTo, RangeFull, Index};
|
||||
use Url;
|
||||
|
||||
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)]
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
/// (Not 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 => self.path_start 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
193
third_party/rust/url/tests/data.rs
vendored
Normal file
193
third_party/rust/url/tests/data.rs
vendored
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// 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
|
||||
|
||||
extern crate rustc_serialize;
|
||||
extern crate test;
|
||||
extern crate url;
|
||||
|
||||
use rustc_serialize::json::{self, Json};
|
||||
use url::{Url, quirks};
|
||||
|
||||
|
||||
fn run_parsing(input: String, base: String, expected: Result<ExpectedAttributes, ()>) {
|
||||
let base = match Url::parse(&base) {
|
||||
Ok(base) => base,
|
||||
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),
|
||||
};
|
||||
|
||||
url.assert_invariants();
|
||||
|
||||
macro_rules! assert_eq {
|
||||
($expected: expr, $got: expr) => {
|
||||
{
|
||||
let expected = $expected;
|
||||
let got = $got;
|
||||
assert!(expected == got, "{:?} != {} {:?} for URL {:?}",
|
||||
got, stringify!($expected), expected, url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
trait JsonExt {
|
||||
fn take(&mut self, key: &str) -> Option<Json>;
|
||||
fn object(self) -> json::Object;
|
||||
fn string(self) -> String;
|
||||
fn take_string(&mut self, key: &str) -> String;
|
||||
}
|
||||
|
||||
impl JsonExt for Json {
|
||||
fn take(&mut self, key: &str) -> Option<Json> {
|
||||
self.as_object_mut().unwrap().remove(key)
|
||||
}
|
||||
|
||||
fn object(self) -> json::Object {
|
||||
if let Json::Object(o) = self { o } else { panic!("Not a Json::Object") }
|
||||
}
|
||||
|
||||
fn string(self) -> String {
|
||||
if let Json::String(s) = self { s } else { panic!("Not a Json::String") }
|
||||
}
|
||||
|
||||
fn take_string(&mut self, key: &str) -> String {
|
||||
self.take(key).unwrap().string()
|
||||
}
|
||||
}
|
||||
|
||||
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 = Json::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.find("failure").is_some() {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(ExpectedAttributes {
|
||||
href: entry.take_string("href"),
|
||||
origin: entry.take("origin").map(Json::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 collect_setters<F>(add_test: &mut F) where F: FnMut(String, test::TestFn) {
|
||||
let mut json = Json::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($attr).unwrap();
|
||||
for mut test in tests.as_array_mut().unwrap().drain(..) {
|
||||
let comment = test.take("comment").map(Json::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("expected").unwrap();
|
||||
add_test(name, test::TestFn::dyn_test_fn(move || {
|
||||
let mut url = Url::parse(&href).unwrap();
|
||||
url.assert_invariants();
|
||||
let _ = quirks::$setter(&mut url, &new_value);
|
||||
assert_attributes!(url, expected,
|
||||
href protocol username password host hostname port pathname search hash);
|
||||
url.assert_invariants();
|
||||
}))
|
||||
}
|
||||
}}
|
||||
}
|
||||
macro_rules! assert_attributes {
|
||||
($url: expr, $expected: expr, $($attr: ident)+) => {
|
||||
$(
|
||||
if let Some(value) = $expected.take(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);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut tests = Vec::new();
|
||||
{
|
||||
let mut add_one = |name: String, run: test::TestFn| {
|
||||
tests.push(test::TestDescAndFn {
|
||||
desc: test::TestDesc {
|
||||
name: test::DynTestName(name),
|
||||
ignore: false,
|
||||
should_panic: test::ShouldPanic::No,
|
||||
},
|
||||
testfn: run,
|
||||
})
|
||||
};
|
||||
collect_parsing(&mut add_one);
|
||||
collect_setters(&mut add_one);
|
||||
}
|
||||
test::test_main(&std::env::args().collect::<Vec<_>>(), tests)
|
||||
}
|
||||
1148
third_party/rust/url/tests/setters_tests.json
vendored
Normal file
1148
third_party/rust/url/tests/setters_tests.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
303
third_party/rust/url/tests/unit.rs
vendored
Normal file
303
third_party/rust/url/tests/unit.rs
vendored
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
// 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.
|
||||
|
||||
//! Unit tests
|
||||
|
||||
extern crate url;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::path::{Path, PathBuf};
|
||||
use url::{Host, Url, form_urlencoded};
|
||||
|
||||
macro_rules! assert_from_file_path {
|
||||
($path: expr) => { assert_from_file_path!($path, $path) };
|
||||
($path: expr, $url_path: expr) => {{
|
||||
let url = Url::from_file_path(Path::new($path)).unwrap();
|
||||
assert_eq!(url.host(), None);
|
||||
assert_eq!(url.path(), $url_path);
|
||||
assert_eq!(url.to_file_path(), Ok(PathBuf::from($path)));
|
||||
}};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn new_file_paths() {
|
||||
if cfg!(unix) {
|
||||
assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
|
||||
assert_eq!(Url::from_file_path(Path::new("../relative")), Err(()));
|
||||
}
|
||||
if cfg!(windows) {
|
||||
assert_eq!(Url::from_file_path(Path::new("relative")), Err(()));
|
||||
assert_eq!(Url::from_file_path(Path::new(r"..\relative")), Err(()));
|
||||
assert_eq!(Url::from_file_path(Path::new(r"\drive-relative")), Err(()));
|
||||
assert_eq!(Url::from_file_path(Path::new(r"\\ucn\")), Err(()));
|
||||
}
|
||||
|
||||
if cfg!(unix) {
|
||||
assert_from_file_path!("/foo/bar");
|
||||
assert_from_file_path!("/foo/ba\0r", "/foo/ba%00r");
|
||||
assert_from_file_path!("/foo/ba%00r", "/foo/ba%2500r");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn new_path_bad_utf8() {
|
||||
use std::ffi::OsStr;
|
||||
use std::os::unix::prelude::*;
|
||||
|
||||
let url = Url::from_file_path(Path::new(OsStr::from_bytes(b"/foo/ba\x80r"))).unwrap();
|
||||
let os_str = OsStr::from_bytes(b"/foo/ba\x80r");
|
||||
assert_eq!(url.to_file_path(), Ok(PathBuf::from(os_str)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_path_windows_fun() {
|
||||
if cfg!(windows) {
|
||||
assert_from_file_path!(r"C:\foo\bar", "/C:/foo/bar");
|
||||
assert_from_file_path!("C:\\foo\\ba\0r", "/C:/foo/ba%00r");
|
||||
|
||||
// Invalid UTF-8
|
||||
assert!(Url::parse("file:///C:/foo/ba%80r").unwrap().to_file_path().is_err());
|
||||
|
||||
// test windows canonicalized path
|
||||
let path = PathBuf::from(r"\\?\C:\foo\bar");
|
||||
assert!(Url::from_file_path(path).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn new_directory_paths() {
|
||||
if cfg!(unix) {
|
||||
assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
|
||||
assert_eq!(Url::from_directory_path(Path::new("../relative")), Err(()));
|
||||
|
||||
let url = Url::from_directory_path(Path::new("/foo/bar")).unwrap();
|
||||
assert_eq!(url.host(), None);
|
||||
assert_eq!(url.path(), "/foo/bar/");
|
||||
}
|
||||
if cfg!(windows) {
|
||||
assert_eq!(Url::from_directory_path(Path::new("relative")), Err(()));
|
||||
assert_eq!(Url::from_directory_path(Path::new(r"..\relative")), Err(()));
|
||||
assert_eq!(Url::from_directory_path(Path::new(r"\drive-relative")), Err(()));
|
||||
assert_eq!(Url::from_directory_path(Path::new(r"\\ucn\")), Err(()));
|
||||
|
||||
let url = Url::from_directory_path(Path::new(r"C:\foo\bar")).unwrap();
|
||||
assert_eq!(url.host(), None);
|
||||
assert_eq!(url.path(), "/C:/foo/bar/");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_str() {
|
||||
assert!("http://testing.com/this".parse::<Url>().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_124() {
|
||||
let url: Url = "file:a".parse().unwrap();
|
||||
assert_eq!(url.path(), "/a");
|
||||
let url: Url = "file:...".parse().unwrap();
|
||||
assert_eq!(url.path(), "/...");
|
||||
let url: Url = "file:..".parse().unwrap();
|
||||
assert_eq!(url.path(), "/");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_equality() {
|
||||
use std::hash::{Hash, Hasher, SipHasher};
|
||||
|
||||
fn check_eq(a: &Url, b: &Url) {
|
||||
assert_eq!(a, b);
|
||||
|
||||
let mut h1 = SipHasher::new();
|
||||
a.hash(&mut h1);
|
||||
let mut h2 = SipHasher::new();
|
||||
b.hash(&mut h2);
|
||||
assert_eq!(h1.finish(), h2.finish());
|
||||
}
|
||||
|
||||
fn url(s: &str) -> Url {
|
||||
let rv = s.parse().unwrap();
|
||||
check_eq(&rv, &rv);
|
||||
rv
|
||||
}
|
||||
|
||||
// Doesn't care if default port is given.
|
||||
let a: Url = url("https://example.com/");
|
||||
let b: Url = url("https://example.com:443/");
|
||||
check_eq(&a, &b);
|
||||
|
||||
// Different ports
|
||||
let a: Url = url("http://example.com/");
|
||||
let b: Url = url("http://example.com:8080/");
|
||||
assert!(a != b, "{:?} != {:?}", a, b);
|
||||
|
||||
// Different scheme
|
||||
let a: Url = url("http://example.com/");
|
||||
let b: Url = url("https://example.com/");
|
||||
assert!(a != b);
|
||||
|
||||
// Different host
|
||||
let a: Url = url("http://foo.com/");
|
||||
let b: Url = url("http://bar.com/");
|
||||
assert!(a != b);
|
||||
|
||||
// Missing path, automatically substituted. Semantically the same.
|
||||
let a: Url = url("http://foo.com");
|
||||
let b: Url = url("http://foo.com/");
|
||||
check_eq(&a, &b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host() {
|
||||
fn assert_host(input: &str, host: Host<&str>) {
|
||||
assert_eq!(Url::parse(input).unwrap().host(), Some(host));
|
||||
}
|
||||
assert_host("http://www.mozilla.org", Host::Domain("www.mozilla.org"));
|
||||
assert_host("http://1.35.33.49", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
|
||||
assert_host("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]", Host::Ipv6(Ipv6Addr::new(
|
||||
0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344)));
|
||||
assert_host("http://1.35.+33.49", Host::Domain("1.35.+33.49"));
|
||||
assert_host("http://[::]", Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0)));
|
||||
assert_host("http://[::1]", Host::Ipv6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)));
|
||||
assert_host("http://0x1.0X23.0x21.061", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
|
||||
assert_host("http://0x1232131", Host::Ipv4(Ipv4Addr::new(1, 35, 33, 49)));
|
||||
assert_host("http://111", Host::Ipv4(Ipv4Addr::new(0, 0, 0, 111)));
|
||||
assert_host("http://2..2.3", Host::Domain("2..2.3"));
|
||||
assert!(Url::parse("http://42.0x1232131").is_err());
|
||||
assert!(Url::parse("http://192.168.0.257").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn host_serialization() {
|
||||
// libstd’s `Display for Ipv6Addr` serializes 0:0:0:0:0:0:_:_ and 0:0:0:0:0:ffff:_:_
|
||||
// using IPv4-like syntax, as suggested in https://tools.ietf.org/html/rfc5952#section-4
|
||||
// but https://url.spec.whatwg.org/#concept-ipv6-serializer specifies not to.
|
||||
|
||||
// Not [::0.0.0.2] / [::ffff:0.0.0.2]
|
||||
assert_eq!(Url::parse("http://[0::2]").unwrap().host_str(), Some("[::2]"));
|
||||
assert_eq!(Url::parse("http://[0::ffff:0:2]").unwrap().host_str(), Some("[::ffff:0:2]"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_idna() {
|
||||
assert!("http://goșu.ro".parse::<Url>().is_ok());
|
||||
assert_eq!(Url::parse("http://☃.net/").unwrap().host(), Some(Host::Domain("xn--n3h.net")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialization() {
|
||||
let data = [
|
||||
("http://example.com/", "http://example.com/"),
|
||||
("http://addslash.com", "http://addslash.com/"),
|
||||
("http://@emptyuser.com/", "http://emptyuser.com/"),
|
||||
("http://:@emptypass.com/", "http://:@emptypass.com/"),
|
||||
("http://user@user.com/", "http://user@user.com/"),
|
||||
("http://user:pass@userpass.com/", "http://user:pass@userpass.com/"),
|
||||
("http://slashquery.com/path/?q=something", "http://slashquery.com/path/?q=something"),
|
||||
("http://noslashquery.com/path?q=something", "http://noslashquery.com/path?q=something")
|
||||
];
|
||||
for &(input, result) in &data {
|
||||
let url = Url::parse(input).unwrap();
|
||||
assert_eq!(url.as_str(), result);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_form_urlencoded() {
|
||||
let pairs: &[(Cow<str>, Cow<str>)] = &[
|
||||
("foo".into(), "é&".into()),
|
||||
("bar".into(), "".into()),
|
||||
("foo".into(), "#".into())
|
||||
];
|
||||
let encoded = form_urlencoded::Serializer::new(String::new()).extend_pairs(pairs).finish();
|
||||
assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23");
|
||||
assert_eq!(form_urlencoded::parse(encoded.as_bytes()).collect::<Vec<_>>(), pairs.to_vec());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_form_serialize() {
|
||||
let encoded = form_urlencoded::Serializer::new(String::new())
|
||||
.append_pair("foo", "é&")
|
||||
.append_pair("bar", "")
|
||||
.append_pair("foo", "#")
|
||||
.finish();
|
||||
assert_eq!(encoded, "foo=%C3%A9%26&bar=&foo=%23");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// https://github.com/servo/rust-url/issues/25
|
||||
fn issue_25() {
|
||||
let filename = if cfg!(windows) { r"C:\run\pg.sock" } else { "/run/pg.sock" };
|
||||
let mut url = Url::from_file_path(filename).unwrap();
|
||||
url.assert_invariants();
|
||||
url.set_scheme("postgres").unwrap();
|
||||
url.assert_invariants();
|
||||
url.set_host(Some("")).unwrap();
|
||||
url.assert_invariants();
|
||||
url.set_username("me").unwrap();
|
||||
url.assert_invariants();
|
||||
let expected = format!("postgres://me@/{}run/pg.sock", if cfg!(windows) { "C:/" } else { "" });
|
||||
assert_eq!(url.as_str(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// https://github.com/servo/rust-url/issues/61
|
||||
fn issue_61() {
|
||||
let mut url = Url::parse("http://mozilla.org").unwrap();
|
||||
url.set_scheme("https").unwrap();
|
||||
assert_eq!(url.port(), None);
|
||||
assert_eq!(url.port_or_known_default(), Some(443));
|
||||
url.assert_invariants();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
/// https://github.com/servo/rust-url/issues/197
|
||||
fn issue_197() {
|
||||
let mut url = Url::from_file_path("/").expect("Failed to parse path");
|
||||
url.assert_invariants();
|
||||
assert_eq!(url, Url::parse("file:///").expect("Failed to parse path + protocol"));
|
||||
url.path_segments_mut().expect("path_segments_mut").pop_if_empty();
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// https://github.com/servo/rust-url/issues/222
|
||||
fn append_trailing_slash() {
|
||||
let mut url: Url = "http://localhost:6767/foo/bar?a=b".parse().unwrap();
|
||||
url.assert_invariants();
|
||||
url.path_segments_mut().unwrap().push("");
|
||||
url.assert_invariants();
|
||||
assert_eq!(url.to_string(), "http://localhost:6767/foo/bar/?a=b");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// https://github.com/servo/rust-url/issues/227
|
||||
fn extend_query_pairs_then_mutate() {
|
||||
let mut url: Url = "http://localhost:6767/foo/bar".parse().unwrap();
|
||||
url.query_pairs_mut().extend_pairs(vec![ ("auth", "my-token") ].into_iter());
|
||||
url.assert_invariants();
|
||||
assert_eq!(url.to_string(), "http://localhost:6767/foo/bar?auth=my-token");
|
||||
url.path_segments_mut().unwrap().push("some_other_path");
|
||||
url.assert_invariants();
|
||||
assert_eq!(url.to_string(), "http://localhost:6767/foo/bar/some_other_path?auth=my-token");
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// https://github.com/servo/rust-url/issues/222
|
||||
fn append_empty_segment_then_mutate() {
|
||||
let mut url: Url = "http://localhost:6767/foo/bar?a=b".parse().unwrap();
|
||||
url.assert_invariants();
|
||||
url.path_segments_mut().unwrap().push("").pop();
|
||||
url.assert_invariants();
|
||||
assert_eq!(url.to_string(), "http://localhost:6767/foo/bar?a=b");
|
||||
}
|
||||
4277
third_party/rust/url/tests/urltestdata.json
vendored
Normal file
4277
third_party/rust/url/tests/urltestdata.json
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue