1 // Copyright 2013-2014 The rust-url developers.
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
12 use std
::cell
::{Cell, RefCell}
;
13 use std
::net
::{Ipv4Addr, Ipv6Addr}
;
14 use std
::path
::{Path, PathBuf}
;
15 use url
::{form_urlencoded, Host, Origin, Url}
;
19 use std
::mem
::size_of
;
20 assert_eq
!(size_of
::<Url
>(), size_of
::<Option
<Url
>>());
25 let base
: Url
= "sc://%C3%B1".parse().unwrap();
26 let url
= base
.join("/resources/testharness.js").unwrap();
27 assert_eq
!(url
.as_str(), "sc://%C3%B1/resources/testharness.js");
31 fn test_relative_empty() {
32 let base
: Url
= "sc://%C3%B1".parse().unwrap();
33 let url
= base
.join("").unwrap();
34 assert_eq
!(url
.as_str(), "sc://%C3%B1");
38 fn test_strip_trailing_spaces_from_opaque_path() {
39 let mut url
: Url
= "data:space ?query".parse().unwrap();
41 assert_eq
!(url
.as_str(), "data:space");
43 let mut url
: Url
= "data:space #hash".parse().unwrap();
44 url
.set_fragment(None
);
45 assert_eq
!(url
.as_str(), "data:space");
49 fn test_set_empty_host() {
50 let mut base
: Url
= "moz://foo:bar@servo/baz".parse().unwrap();
51 base
.set_username("").unwrap();
52 assert_eq
!(base
.as_str(), "moz://:bar@servo/baz");
53 base
.set_host(None
).unwrap();
54 assert_eq
!(base
.as_str(), "moz:/baz");
55 base
.set_host(Some("servo")).unwrap();
56 assert_eq
!(base
.as_str(), "moz://servo/baz");
58 let mut base
: Url
= "file://server/share/foo/bar".parse().unwrap();
59 base
.set_host(None
).unwrap();
60 assert_eq
!(base
.as_str(), "file:///share/foo/bar");
62 let mut base
: Url
= "file://server/share/foo/bar".parse().unwrap();
63 base
.set_host(Some("foo")).unwrap();
64 assert_eq
!(base
.as_str(), "file://foo/share/foo/bar");
68 fn test_set_empty_username_and_password() {
69 let mut base
: Url
= "moz://foo:bar@servo/baz".parse().unwrap();
70 base
.set_username("").unwrap();
71 assert_eq
!(base
.as_str(), "moz://:bar@servo/baz");
73 base
.set_password(Some("")).unwrap();
74 assert_eq
!(base
.as_str(), "moz://servo/baz");
76 base
.set_password(None
).unwrap();
77 assert_eq
!(base
.as_str(), "moz://servo/baz");
81 fn test_set_empty_password() {
82 let mut base
: Url
= "moz://foo:bar@servo/baz".parse().unwrap();
84 base
.set_password(Some("")).unwrap();
85 assert_eq
!(base
.as_str(), "moz://foo@servo/baz");
87 base
.set_password(None
).unwrap();
88 assert_eq
!(base
.as_str(), "moz://foo@servo/baz");
92 fn test_set_empty_hostname() {
94 let mut base
: Url
= "moz://foo@servo/baz".parse().unwrap();
96 quirks
::set_hostname(&mut base
, "").is_err(),
97 "setting an empty hostname to a url with a username should fail"
99 base
= "moz://:pass@servo/baz".parse().unwrap();
101 quirks
::set_hostname(&mut base
, "").is_err(),
102 "setting an empty hostname to a url with a password should fail"
104 base
= "moz://servo/baz".parse().unwrap();
105 quirks
::set_hostname(&mut base
, "").unwrap();
106 assert_eq
!(base
.as_str(), "moz:///baz");
110 fn test_set_empty_query() {
111 let mut base
: Url
= "moz://example.com/path?query".parse().unwrap();
113 base
.set_query(Some(""));
114 assert_eq
!(base
.as_str(), "moz://example.com/path?");
116 base
.set_query(None
);
117 assert_eq
!(base
.as_str(), "moz://example.com/path");
120 macro_rules
! assert_from_file_path
{
122 assert_from_file_path
!($path
, $path
)
124 ($path
: expr
, $url_path
: expr
) => {{
125 let url
= Url
::from_file_path(Path
::new($path
)).unwrap();
126 assert_eq
!(url
.host(), None
);
127 assert_eq
!(url
.path(), $url_path
);
128 assert_eq
!(url
.to_file_path(), Ok(PathBuf
::from($path
)));
133 fn new_file_paths() {
135 assert_eq
!(Url
::from_file_path(Path
::new("relative")), Err(()));
136 assert_eq
!(Url
::from_file_path(Path
::new("../relative")), Err(()));
139 assert_eq
!(Url
::from_file_path(Path
::new("relative")), Err(()));
140 assert_eq
!(Url
::from_file_path(Path
::new(r
"..\relative")), Err(()));
141 assert_eq
!(Url
::from_file_path(Path
::new(r
"\drive-relative")), Err(()));
142 assert_eq
!(Url
::from_file_path(Path
::new(r
"\\ucn\")), Err(()));
146 assert_from_file_path
!("/foo/bar");
147 assert_from_file_path
!("/foo/ba\0r", "/foo/ba%00r");
148 assert_from_file_path
!("/foo/ba%00r", "/foo/ba%2500r");
154 fn new_path_bad_utf8() {
156 use std
::os
::unix
::prelude
::*;
158 let url
= Url
::from_file_path(Path
::new(OsStr
::from_bytes(b
"/foo/ba\x80r"))).unwrap();
159 let os_str
= OsStr
::from_bytes(b
"/foo/ba\x80r");
160 assert_eq
!(url
.to_file_path(), Ok(PathBuf
::from(os_str
)));
164 fn new_path_windows_fun() {
166 assert_from_file_path
!(r
"C:\foo\bar", "/C:/foo/bar");
167 assert_from_file_path
!("C:\\foo\\ba\0r", "/C:/foo/ba%00r");
170 assert
!(Url
::parse("file:///C:/foo/ba%80r")
175 // test windows canonicalized path
176 let path
= PathBuf
::from(r
"\\?\C:\foo\bar");
177 assert
!(Url
::from_file_path(path
).is_ok());
179 // Percent-encoded drive letter
180 let url
= Url
::parse("file:///C%3A/foo/bar").unwrap();
181 assert_eq
!(url
.to_file_path(), Ok(PathBuf
::from(r
"C:\foo\bar")));
186 fn new_directory_paths() {
188 assert_eq
!(Url
::from_directory_path(Path
::new("relative")), Err(()));
189 assert_eq
!(Url
::from_directory_path(Path
::new("../relative")), Err(()));
191 let url
= Url
::from_directory_path(Path
::new("/foo/bar")).unwrap();
192 assert_eq
!(url
.host(), None
);
193 assert_eq
!(url
.path(), "/foo/bar/");
196 assert_eq
!(Url
::from_directory_path(Path
::new("relative")), Err(()));
197 assert_eq
!(Url
::from_directory_path(Path
::new(r
"..\relative")), Err(()));
199 Url
::from_directory_path(Path
::new(r
"\drive-relative")),
202 assert_eq
!(Url
::from_directory_path(Path
::new(r
"\\ucn\")), Err(()));
204 let url
= Url
::from_directory_path(Path
::new(r
"C:\foo\bar")).unwrap();
205 assert_eq
!(url
.host(), None
);
206 assert_eq
!(url
.path(), "/C:/foo/bar/");
211 fn path_backslash_fun() {
212 let mut special_url
= "http://foobar.com".parse
::<Url
>().unwrap();
213 special_url
.path_segments_mut().unwrap().push("foo\\bar");
214 assert_eq
!(special_url
.as_str(), "http://foobar.com/foo%5Cbar");
216 let mut nonspecial_url
= "thing://foobar.com".parse
::<Url
>().unwrap();
217 nonspecial_url
.path_segments_mut().unwrap().push("foo\\bar");
218 assert_eq
!(nonspecial_url
.as_str(), "thing://foobar.com/foo\\bar");
223 assert
!("http://testing.com/this".parse
::<Url
>().is_ok());
227 fn parse_with_params() {
228 let url
= Url
::parse_with_params(
229 "http://testing.com/this?dont=clobberme",
236 "http://testing.com/this?dont=clobberme&lang=rust"
242 let url
: Url
= "file:a".parse().unwrap();
243 assert_eq
!(url
.path(), "/a");
244 let url
: Url
= "file:...".parse().unwrap();
245 assert_eq
!(url
.path(), "/...");
246 let url
: Url
= "file:..".parse().unwrap();
247 assert_eq
!(url
.path(), "/");
252 use std
::collections
::hash_map
::DefaultHasher
;
253 use std
::hash
::{Hash, Hasher}
;
255 fn check_eq(a
: &Url
, b
: &Url
) {
258 let mut h1
= DefaultHasher
::new();
260 let mut h2
= DefaultHasher
::new();
262 assert_eq
!(h1
.finish(), h2
.finish());
265 fn url(s
: &str) -> Url
{
266 let rv
= s
.parse().unwrap();
271 // Doesn't care if default port is given.
272 let a
: Url
= url("https://example.com/");
273 let b
: Url
= url("https://example.com:443/");
277 let a
: Url
= url("http://example.com/");
278 let b
: Url
= url("http://example.com:8080/");
279 assert
!(a
!= b
, "{:?} != {:?}", a
, b
);
282 let a
: Url
= url("http://example.com/");
283 let b
: Url
= url("https://example.com/");
287 let a
: Url
= url("http://foo.com/");
288 let b
: Url
= url("http://bar.com/");
291 // Missing path, automatically substituted. Semantically the same.
292 let a
: Url
= url("http://foo.com");
293 let b
: Url
= url("http://foo.com/");
299 fn assert_host(input
: &str, host
: Host
<&str>) {
300 assert_eq
!(Url
::parse(input
).unwrap().host(), Some(host
));
302 assert_host("http://www.mozilla.org", Host
::Domain("www.mozilla.org"));
305 Host
::Ipv4(Ipv4Addr
::new(1, 35, 33, 49)),
308 "http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]",
309 Host
::Ipv6(Ipv6Addr
::new(
310 0x2001, 0x0db8, 0x85a3, 0x08d3, 0x1319, 0x8a2e, 0x0370, 0x7344,
315 Host
::Ipv6(Ipv6Addr
::new(0, 0, 0, 0, 0, 0, 0, 0)),
319 Host
::Ipv6(Ipv6Addr
::new(0, 0, 0, 0, 0, 0, 0, 1)),
322 "http://0x1.0X23.0x21.061",
323 Host
::Ipv4(Ipv4Addr
::new(1, 35, 33, 49)),
325 assert_host("http://0x1232131", Host
::Ipv4(Ipv4Addr
::new(1, 35, 33, 49)));
326 assert_host("http://111", Host
::Ipv4(Ipv4Addr
::new(0, 0, 0, 111)));
327 assert
!(Url
::parse("http://1.35.+33.49").is_err());
328 assert
!(Url
::parse("http://2..2.3").is_err());
329 assert
!(Url
::parse("http://42.0x1232131").is_err());
330 assert
!(Url
::parse("http://192.168.0.257").is_err());
332 assert_eq
!(Host
::Domain("foo"), Host
::Domain("foo").to_owned());
333 assert_ne
!(Host
::Domain("foo"), Host
::Domain("bar").to_owned());
337 fn host_serialization() {
338 // libstd’s `Display for Ipv6Addr` serializes 0:0:0:0:0:0:_:_ and 0:0:0:0:0:ffff:_:_
339 // using IPv4-like syntax, as suggested in https://tools.ietf.org/html/rfc5952#section-4
340 // but https://url.spec.whatwg.org/#concept-ipv6-serializer specifies not to.
342 // Not [::0.0.0.2] / [::ffff:0.0.0.2]
344 Url
::parse("http://[0::2]").unwrap().host_str(),
348 Url
::parse("http://[0::ffff:0:2]").unwrap().host_str(),
355 assert
!("http://goșu.ro".parse
::<Url
>().is_ok());
357 Url
::parse("http://☃.net/").unwrap().host(),
358 Some(Host
::Domain("xn--n3h.net"))
360 assert
!("https://r2---sn-huoa-cvhl.googlevideo.com/crossdomain.xml"
366 fn test_serialization() {
368 ("http://example.com/", "http://example.com/"),
369 ("http://addslash.com", "http://addslash.com/"),
370 ("http://@emptyuser.com/", "http://emptyuser.com/"),
371 ("http://:@emptypass.com/", "http://emptypass.com/"),
372 ("http://user@user.com/", "http://user@user.com/"),
374 "http://user:pass@userpass.com/",
375 "http://user:pass@userpass.com/",
378 "http://slashquery.com/path/?q=something",
379 "http://slashquery.com/path/?q=something",
382 "http://noslashquery.com/path?q=something",
383 "http://noslashquery.com/path?q=something",
386 for &(input
, result
) in &data
{
387 let url
= Url
::parse(input
).unwrap();
388 assert_eq
!(url
.as_str(), result
);
393 fn test_form_urlencoded() {
394 let pairs
: &[(Cow
<'_
, str>, Cow
<'_
, str>)] = &[
395 ("foo".into(), "é&".into()),
396 ("bar".into(), "".into()),
397 ("foo".into(), "#".into()),
399 let encoded
= form_urlencoded
::Serializer
::new(String
::new())
402 assert_eq
!(encoded
, "foo=%C3%A9%26&bar=&foo=%23");
404 form_urlencoded
::parse(encoded
.as_bytes()).collect
::<Vec
<_
>>(),
410 fn test_form_serialize() {
411 let encoded
= form_urlencoded
::Serializer
::new(String
::new())
412 .append_pair("foo", "é&")
413 .append_pair("bar", "")
414 .append_pair("foo", "#")
415 .append_key_only("json")
417 assert_eq
!(encoded
, "foo=%C3%A9%26&bar=&foo=%23&json");
421 fn form_urlencoded_encoding_override() {
422 let encoded
= form_urlencoded
::Serializer
::new(String
::new())
423 .encoding_override(Some(&|s
| s
.as_bytes().to_ascii_uppercase().into()))
424 .append_pair("foo", "bar")
425 .append_key_only("xml")
427 assert_eq
!(encoded
, "FOO=BAR&XML");
431 /// https://github.com/servo/rust-url/issues/61
433 let mut url
= Url
::parse("http://mozilla.org").unwrap();
434 url
.set_scheme("https").unwrap();
435 assert_eq
!(url
.port(), None
);
436 assert_eq
!(url
.port_or_known_default(), Some(443));
437 url
.check_invariants().unwrap();
442 /// https://github.com/servo/rust-url/issues/197
444 let mut url
= Url
::from_file_path("/").expect("Failed to parse path");
445 url
.check_invariants().unwrap();
448 Url
::parse("file:///").expect("Failed to parse path + protocol")
450 url
.path_segments_mut()
451 .expect("path_segments_mut")
457 Url
::parse("mailto:").unwrap().cannot_be_a_base();
461 /// https://github.com/servo/rust-url/issues/222
462 fn append_trailing_slash() {
463 let mut url
: Url
= "http://localhost:6767/foo/bar?a=b".parse().unwrap();
464 url
.check_invariants().unwrap();
465 url
.path_segments_mut().unwrap().push("");
466 url
.check_invariants().unwrap();
467 assert_eq
!(url
.to_string(), "http://localhost:6767/foo/bar/?a=b");
471 /// https://github.com/servo/rust-url/issues/227
472 fn extend_query_pairs_then_mutate() {
473 let mut url
: Url
= "http://localhost:6767/foo/bar".parse().unwrap();
474 url
.query_pairs_mut()
475 .extend_pairs(vec
![("auth", "my-token")]);
476 url
.check_invariants().unwrap();
479 "http://localhost:6767/foo/bar?auth=my-token"
481 url
.path_segments_mut().unwrap().push("some_other_path");
482 url
.check_invariants().unwrap();
485 "http://localhost:6767/foo/bar/some_other_path?auth=my-token"
490 /// https://github.com/servo/rust-url/issues/222
491 fn append_empty_segment_then_mutate() {
492 let mut url
: Url
= "http://localhost:6767/foo/bar?a=b".parse().unwrap();
493 url
.check_invariants().unwrap();
494 url
.path_segments_mut().unwrap().push("").pop();
495 url
.check_invariants().unwrap();
496 assert_eq
!(url
.to_string(), "http://localhost:6767/foo/bar?a=b");
500 /// https://github.com/servo/rust-url/issues/243
502 let mut url
= Url
::parse("https://example.net/hello").unwrap();
503 url
.set_host(Some("foo.com")).unwrap();
504 assert_eq
!(url
.as_str(), "https://foo.com/hello");
505 assert
!(url
.set_host(None
).is_err());
506 assert_eq
!(url
.as_str(), "https://foo.com/hello");
507 assert
!(url
.set_host(Some("")).is_err());
508 assert_eq
!(url
.as_str(), "https://foo.com/hello");
510 let mut url
= Url
::parse("foobar://example.net/hello").unwrap();
511 url
.set_host(None
).unwrap();
512 assert_eq
!(url
.as_str(), "foobar:/hello");
514 let mut url
= Url
::parse("foo://ș").unwrap();
515 assert_eq
!(url
.as_str(), "foo://%C8%99");
516 url
.set_host(Some("goșu.ro")).unwrap();
517 assert_eq
!(url
.as_str(), "foo://go%C8%99u.ro");
521 // https://github.com/servo/rust-url/issues/166
522 fn test_leading_dots() {
524 Host
::parse(".org").unwrap(),
525 Host
::Domain(".org".to_owned())
527 assert_eq
!(Url
::parse("file://./foo").unwrap().domain(), Some("."));
531 /// https://github.com/servo/rust-url/issues/302
532 fn test_origin_hash() {
533 use std
::collections
::hash_map
::DefaultHasher
;
534 use std
::hash
::{Hash, Hasher}
;
536 fn hash
<T
: Hash
>(value
: &T
) -> u64 {
537 let mut hasher
= DefaultHasher
::new();
538 value
.hash(&mut hasher
);
542 let origin
= &Url
::parse("http://example.net/").unwrap().origin();
544 let origins_to_compare
= [
545 Url
::parse("http://example.net:80/").unwrap().origin(),
546 Url
::parse("http://example.net:81/").unwrap().origin(),
547 Url
::parse("http://example.net").unwrap().origin(),
548 Url
::parse("http://example.net/hello").unwrap().origin(),
549 Url
::parse("https://example.net").unwrap().origin(),
550 Url
::parse("ftp://example.net").unwrap().origin(),
551 Url
::parse("file://example.net").unwrap().origin(),
552 Url
::parse("http://user@example.net/").unwrap().origin(),
553 Url
::parse("http://user:pass@example.net/")
558 for origin_to_compare
in &origins_to_compare
{
559 if origin
== origin_to_compare
{
560 assert_eq
!(hash(origin
), hash(origin_to_compare
));
562 assert_ne
!(hash(origin
), hash(origin_to_compare
));
566 let opaque_origin
= Url
::parse("file://example.net").unwrap().origin();
567 let same_opaque_origin
= Url
::parse("file://example.net").unwrap().origin();
568 let other_opaque_origin
= Url
::parse("file://other").unwrap().origin();
570 assert_ne
!(hash(&opaque_origin
), hash(&same_opaque_origin
));
571 assert_ne
!(hash(&opaque_origin
), hash(&other_opaque_origin
));
575 fn test_origin_blob_equality() {
576 let origin
= &Url
::parse("http://example.net/").unwrap().origin();
577 let blob_origin
= &Url
::parse("blob:http://example.net/").unwrap().origin();
579 assert_eq
!(origin
, blob_origin
);
583 fn test_origin_opaque() {
584 assert
!(!Origin
::new_opaque().is_tuple());
585 assert
!(!&Url
::parse("blob:malformed//").unwrap().origin().is_tuple())
589 fn test_origin_unicode_serialization() {
591 ("http://😅.com", "http://😅.com"),
592 ("ftp://😅:🙂@🙂.com", "ftp://🙂.com"),
593 ("https://user@😅.com", "https://😅.com"),
594 ("http://😅.🙂:40", "http://😅.🙂:40"),
596 for &(unicode_url
, expected_serialization
) in &data
{
597 let origin
= Url
::parse(unicode_url
).unwrap().origin();
598 assert_eq
!(origin
.unicode_serialization(), *expected_serialization
);
601 let ascii_origins
= [
602 Url
::parse("http://example.net/").unwrap().origin(),
603 Url
::parse("http://example.net:80/").unwrap().origin(),
604 Url
::parse("http://example.net:81/").unwrap().origin(),
605 Url
::parse("http://example.net").unwrap().origin(),
606 Url
::parse("http://example.net/hello").unwrap().origin(),
607 Url
::parse("https://example.net").unwrap().origin(),
608 Url
::parse("ftp://example.net").unwrap().origin(),
609 Url
::parse("file://example.net").unwrap().origin(),
610 Url
::parse("http://user@example.net/").unwrap().origin(),
611 Url
::parse("http://user:pass@example.net/")
614 Url
::parse("http://127.0.0.1").unwrap().origin(),
616 for ascii_origin
in &ascii_origins
{
618 ascii_origin
.ascii_serialization(),
619 ascii_origin
.unicode_serialization()
625 fn test_socket_addrs() {
626 use std
::net
::ToSocketAddrs
;
629 ("https://127.0.0.1/", "127.0.0.1", 443),
630 ("https://127.0.0.1:9742/", "127.0.0.1", 9742),
631 ("custom-protocol://127.0.0.1:9742/", "127.0.0.1", 9742),
632 ("custom-protocol://127.0.0.1/", "127.0.0.1", 9743),
633 ("https://[::1]/", "::1", 443),
634 ("https://[::1]:9742/", "::1", 9742),
635 ("custom-protocol://[::1]:9742/", "::1", 9742),
636 ("custom-protocol://[::1]/", "::1", 9743),
637 ("https://localhost/", "localhost", 443),
638 ("https://localhost:9742/", "localhost", 9742),
639 ("custom-protocol://localhost:9742/", "localhost", 9742),
640 ("custom-protocol://localhost/", "localhost", 9743),
643 for (url_string
, host
, port
) in &data
{
644 let url
= url
::Url
::parse(url_string
).unwrap();
646 .socket_addrs(|| match url
.scheme() {
647 "custom-protocol" => Some(9743),
653 (*host
, *port
).to_socket_addrs().unwrap().next()
659 fn test_no_base_url() {
660 let mut no_base_url
= Url
::parse("mailto:test@example.net").unwrap();
662 assert
!(no_base_url
.cannot_be_a_base());
663 assert
!(no_base_url
.path_segments().is_none());
664 assert
!(no_base_url
.path_segments_mut().is_err());
665 assert
!(no_base_url
.set_host(Some("foo")).is_err());
667 .set_ip_host("127.0.0.1".parse().unwrap())
670 no_base_url
.set_path("/foo");
671 assert_eq
!(no_base_url
.path(), "%2Ffoo");
676 let url
= Url
::parse("https://127.0.0.1/").unwrap();
677 assert_eq
!(url
.domain(), None
);
679 let url
= Url
::parse("mailto:test@example.net").unwrap();
680 assert_eq
!(url
.domain(), None
);
682 let url
= Url
::parse("https://example.com/").unwrap();
683 assert_eq
!(url
.domain(), Some("example.com"));
688 let url
= Url
::parse("https://example.com/products?page=2#fragment").unwrap();
689 assert_eq
!(url
.query(), Some("page=2"));
691 url
.query_pairs().next(),
692 Some((Cow
::Borrowed("page"), Cow
::Borrowed("2")))
695 let url
= Url
::parse("https://example.com/products").unwrap();
696 assert
!(url
.query().is_none());
697 assert_eq
!(url
.query_pairs().count(), 0);
699 let url
= Url
::parse("https://example.com/?country=español").unwrap();
700 assert_eq
!(url
.query(), Some("country=espa%C3%B1ol"));
702 url
.query_pairs().next(),
703 Some((Cow
::Borrowed("country"), Cow
::Borrowed("español")))
706 let url
= Url
::parse("https://example.com/products?page=2&sort=desc").unwrap();
707 assert_eq
!(url
.query(), Some("page=2&sort=desc"));
708 let mut pairs
= url
.query_pairs();
709 assert_eq
!(pairs
.count(), 2);
712 Some((Cow
::Borrowed("page"), Cow
::Borrowed("2")))
716 Some((Cow
::Borrowed("sort"), Cow
::Borrowed("desc")))
722 let url
= Url
::parse("https://example.com/#fragment").unwrap();
723 assert_eq
!(url
.fragment(), Some("fragment"));
725 let url
= Url
::parse("https://example.com/").unwrap();
726 assert_eq
!(url
.fragment(), None
);
730 fn test_set_ip_host() {
731 let mut url
= Url
::parse("http://example.com").unwrap();
733 url
.set_ip_host("127.0.0.1".parse().unwrap()).unwrap();
734 assert_eq
!(url
.host_str(), Some("127.0.0.1"));
736 url
.set_ip_host("::1".parse().unwrap()).unwrap();
737 assert_eq
!(url
.host_str(), Some("[::1]"));
742 use url
::quirks
::set_href
;
744 let mut url
= Url
::parse("https://existing.url").unwrap();
746 assert
!(set_href(&mut url
, "mal//formed").is_err());
750 "https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment"
755 Url
::parse("https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment")
761 fn test_domain_encoding_quirks() {
762 use url
::quirks
::{domain_to_ascii, domain_to_unicode}
;
765 ("http://example.com", "", ""),
766 ("😅.🙂", "xn--j28h.xn--938h", "😅.🙂"),
767 ("example.com", "example.com", "example.com"),
768 ("mailto:test@example.net", "", ""),
772 assert_eq
!(domain_to_ascii(url
.0), url
.1);
773 assert_eq
!(domain_to_unicode(url
.0), url
.2);
777 #[cfg(feature = "expose_internals")]
779 fn test_expose_internals() {
780 use url
::quirks
::internal_components
;
781 use url
::quirks
::InternalComponents
;
783 let url
= Url
::parse("https://example.com/path/file.ext?key=val&key2=val2#fragment").unwrap();
784 let InternalComponents
{
793 } = internal_components(&url
);
795 assert_eq
!(scheme_end
, 5);
796 assert_eq
!(username_end
, 8);
797 assert_eq
!(host_start
, 8);
798 assert_eq
!(host_end
, 19);
799 assert_eq
!(port
, None
);
800 assert_eq
!(path_start
, 19);
801 assert_eq
!(query_start
, Some(33));
802 assert_eq
!(fragment_start
, Some(51));
806 fn test_windows_unc_path() {
811 let url
= Url
::from_file_path(Path
::new(r
"\\host\share\path\file.txt")).unwrap();
812 assert_eq
!(url
.as_str(), "file://host/share/path/file.txt");
814 let url
= Url
::from_file_path(Path
::new(r
"\\höst\share\path\file.txt")).unwrap();
815 assert_eq
!(url
.as_str(), "file://xn--hst-sna/share/path/file.txt");
817 let url
= Url
::from_file_path(Path
::new(r
"\\192.168.0.1\share\path\file.txt")).unwrap();
818 assert_eq
!(url
.host(), Some(Host
::Ipv4(Ipv4Addr
::new(192, 168, 0, 1))));
820 let path
= url
.to_file_path().unwrap();
821 assert_eq
!(path
.to_str(), Some(r
"\\192.168.0.1\share\path\file.txt"));
823 // Another way to write these:
824 let url
= Url
::from_file_path(Path
::new(r
"\\?\UNC\host\share\path\file.txt")).unwrap();
825 assert_eq
!(url
.as_str(), "file://host/share/path/file.txt");
827 // Paths starting with "\\.\" (Local Device Paths) are intentionally not supported.
828 let url
= Url
::from_file_path(Path
::new(r
"\\.\some\path\file.txt"));
829 assert
!(url
.is_err());
833 fn test_syntax_violation_callback() {
834 use url
::SyntaxViolation
::*;
835 let violation
= Cell
::new(None
);
836 let url
= Url
::options()
837 .syntax_violation_callback(Some(&|v
| violation
.set(Some(v
))))
838 .parse("http:////mozilla.org:42")
840 assert_eq
!(url
.port(), Some(42));
842 let v
= violation
.take().unwrap();
843 assert_eq
!(v
, ExpectedDoubleSlash
);
844 assert_eq
!(v
.description(), "expected //");
845 assert_eq
!(v
.to_string(), "expected //");
849 fn test_syntax_violation_callback_lifetimes() {
850 use url
::SyntaxViolation
::*;
851 let violation
= Cell
::new(None
);
852 let vfn
= |s
| violation
.set(Some(s
));
854 let url
= Url
::options()
855 .syntax_violation_callback(Some(&vfn
))
856 .parse("http:////mozilla.org:42")
858 assert_eq
!(url
.port(), Some(42));
859 assert_eq
!(violation
.take(), Some(ExpectedDoubleSlash
));
861 let url
= Url
::options()
862 .syntax_violation_callback(Some(&vfn
))
863 .parse("http://mozilla.org\\path")
865 assert_eq
!(url
.path(), "/path");
866 assert_eq
!(violation
.take(), Some(Backslash
));
870 fn test_syntax_violation_callback_types() {
871 use url
::SyntaxViolation
::*;
874 ("http://mozilla.org/\\foo", Backslash
, "backslash"),
875 (" http://mozilla.org", C0SpaceIgnored
, "leading or trailing control or space character are ignored in URLs"),
876 ("http://user:pass@mozilla.org", EmbeddedCredentials
, "embedding authentication information (username or password) in an URL is not recommended"),
877 ("http:///mozilla.org", ExpectedDoubleSlash
, "expected //"),
878 ("file:/foo.txt", ExpectedFileDoubleSlash
, "expected // after file:"),
879 ("file://mozilla.org/c:/file.txt", FileWithHostAndWindowsDrive
, "file: with host and Windows drive letter"),
880 ("http://mozilla.org/^", NonUrlCodePoint
, "non-URL code point"),
881 ("http://mozilla.org/#\x000", NullInFragment
, "NULL characters are ignored in URL fragment identifiers"),
882 ("http://mozilla.org/%1", PercentDecode
, "expected 2 hex digits after %"),
883 ("http://mozilla.org\t/foo", TabOrNewlineIgnored
, "tabs or newlines are ignored in URLs"),
884 ("http://user@:pass@mozilla.org", UnencodedAtSign
, "unencoded @ sign in username or password")
887 for test_case
in &data
{
888 let violation
= Cell
::new(None
);
890 .syntax_violation_callback(Some(&|v
| violation
.set(Some(v
))))
894 let v
= violation
.take();
895 assert_eq
!(v
, Some(test_case
.1));
896 assert_eq
!(v
.unwrap().description(), test_case
.2);
897 assert_eq
!(v
.unwrap().to_string(), test_case
.2);
902 fn test_options_reuse() {
903 use url
::SyntaxViolation
::*;
904 let violations
= RefCell
::new(Vec
::new());
905 let vfn
= |v
| violations
.borrow_mut().push(v
);
907 let options
= Url
::options().syntax_violation_callback(Some(&vfn
));
908 let url
= options
.parse("http:////mozilla.org").unwrap();
910 let options
= options
.base_url(Some(&url
));
911 let url
= options
.parse("/sub\\path").unwrap();
912 assert_eq
!(url
.as_str(), "http://mozilla.org/sub/path");
913 assert_eq
!(*violations
.borrow(), vec
!(ExpectedDoubleSlash
, Backslash
));
916 /// https://github.com/servo/rust-url/issues/505
919 fn test_url_from_file_path() {
920 use std
::path
::PathBuf
;
923 let p
= PathBuf
::from("c:///");
924 let u
= Url
::from_file_path(p
).unwrap();
925 let path
= u
.to_file_path().unwrap();
926 assert_eq
!("C:\\", path
.to_str().unwrap());
929 /// https://github.com/servo/rust-url/issues/505
932 fn test_url_from_file_path() {
933 use std
::path
::PathBuf
;
936 let p
= PathBuf
::from("/c:/");
937 let u
= Url
::from_file_path(p
).unwrap();
938 let path
= u
.to_file_path().unwrap();
939 assert_eq
!("/c:/", path
.to_str().unwrap());
943 fn test_non_special_path() {
944 let mut db_url
= url
::Url
::parse("postgres://postgres@localhost/").unwrap();
945 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/");
946 db_url
.set_path("diesel_foo");
947 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/diesel_foo");
948 assert_eq
!(db_url
.path(), "/diesel_foo");
952 fn test_non_special_path2() {
953 let mut db_url
= url
::Url
::parse("postgres://postgres@localhost/").unwrap();
954 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/");
956 assert_eq
!(db_url
.path(), "");
957 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost");
958 db_url
.set_path("foo");
959 assert_eq
!(db_url
.path(), "/foo");
960 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/foo");
961 db_url
.set_path("/bar");
962 assert_eq
!(db_url
.path(), "/bar");
963 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/bar");
967 fn test_non_special_path3() {
968 let mut db_url
= url
::Url
::parse("postgres://postgres@localhost/").unwrap();
969 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/");
970 db_url
.set_path("/");
971 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/");
972 assert_eq
!(db_url
.path(), "/");
973 db_url
.set_path("/foo");
974 assert_eq
!(db_url
.as_str(), "postgres://postgres@localhost/foo");
975 assert_eq
!(db_url
.path(), "/foo");
979 fn test_set_scheme_to_file_with_host() {
980 let mut url
: Url
= "http://localhost:6767/foo/bar".parse().unwrap();
981 let result
= url
.set_scheme("file");
982 assert_eq
!(url
.to_string(), "http://localhost:6767/foo/bar");
983 assert_eq
!(result
, Err(()));
988 let mut url
= Url
::parse("arhttpsps:/.//eom/dae.com/\\\\t\\:").unwrap();
989 url
::quirks
::set_hostname(&mut url
, "//eom/datcom/\\\\t\\://eom/data.cs").unwrap();
993 fn test_null_host_with_leading_empty_path_segment() {
994 // since Note in item 3 of URL serializing in the URL Standard
995 // https://url.spec.whatwg.org/#url-serializing
996 let url
= Url
::parse("m:/.//\\").unwrap();
997 let encoded
= url
.as_str();
998 let reparsed
= Url
::parse(encoded
).unwrap();
999 assert_eq
!(reparsed
, url
);
1003 fn pop_if_empty_in_bounds() {
1004 let mut url
= Url
::parse("m://").unwrap();
1005 let mut segments
= url
.path_segments_mut().unwrap();
1006 segments
.pop_if_empty();
1012 use url
::Position
::*;
1015 struct ExpectedSlices
<'a
> {
1029 full
: "https://user:pass@domain.com:9742/path/file.ext?key=val&key2=val2#fragment",
1035 path
: "/path/file.ext",
1036 query
: "key=val&key2=val2",
1037 fragment
: "fragment",
1040 full
: "https://domain.com:9742/path/file.ext#fragment",
1044 path
: "/path/file.ext",
1045 fragment
: "fragment",
1046 ..Default
::default()
1049 full
: "https://domain.com:9742/path/file.ext",
1053 path
: "/path/file.ext",
1054 ..Default
::default()
1057 full
: "blob:blob-info",
1060 ..Default
::default()
1064 for expected_slices
in &data
{
1065 let url
= Url
::parse(expected_slices
.full
).unwrap();
1066 assert_eq
!(&url
[..], expected_slices
.full
);
1067 assert_eq
!(&url
[BeforeScheme
..AfterScheme
], expected_slices
.scheme
);
1069 &url
[BeforeUsername
..AfterUsername
],
1070 expected_slices
.username
1073 &url
[BeforePassword
..AfterPassword
],
1074 expected_slices
.password
1076 assert_eq
!(&url
[BeforeHost
..AfterHost
], expected_slices
.host
);
1077 assert_eq
!(&url
[BeforePort
..AfterPort
], expected_slices
.port
);
1078 assert_eq
!(&url
[BeforePath
..AfterPath
], expected_slices
.path
);
1079 assert_eq
!(&url
[BeforeQuery
..AfterQuery
], expected_slices
.query
);
1081 &url
[BeforeFragment
..AfterFragment
],
1082 expected_slices
.fragment
1084 assert_eq
!(&url
[..AfterFragment
], expected_slices
.full
);
1089 fn test_make_relative() {
1092 "http://127.0.0.1:8080/test",
1093 "http://127.0.0.1:8080/test",
1097 "http://127.0.0.1:8080/test",
1098 "http://127.0.0.1:8080/test/",
1102 "http://127.0.0.1:8080/test/",
1103 "http://127.0.0.1:8080/test",
1107 "http://127.0.0.1:8080/",
1108 "http://127.0.0.1:8080/?foo=bar#123",
1112 "http://127.0.0.1:8080/",
1113 "http://127.0.0.1:8080/test/video",
1117 "http://127.0.0.1:8080/test",
1118 "http://127.0.0.1:8080/test/video",
1122 "http://127.0.0.1:8080/test/",
1123 "http://127.0.0.1:8080/test/video",
1127 "http://127.0.0.1:8080/test",
1128 "http://127.0.0.1:8080/test2/video",
1132 "http://127.0.0.1:8080/test/",
1133 "http://127.0.0.1:8080/test2/video",
1137 "http://127.0.0.1:8080/test/bla",
1138 "http://127.0.0.1:8080/test2/video",
1142 "http://127.0.0.1:8080/test/bla/",
1143 "http://127.0.0.1:8080/test2/video",
1144 "../../test2/video",
1147 "http://127.0.0.1:8080/test/?foo=bar#123",
1148 "http://127.0.0.1:8080/test/video",
1152 "http://127.0.0.1:8080/test/",
1153 "http://127.0.0.1:8080/test/video?baz=meh#456",
1154 "video?baz=meh#456",
1157 "http://127.0.0.1:8080/test",
1158 "http://127.0.0.1:8080/test?baz=meh#456",
1162 "http://127.0.0.1:8080/test/",
1163 "http://127.0.0.1:8080/test?baz=meh#456",
1164 "../test?baz=meh#456",
1167 "http://127.0.0.1:8080/test/",
1168 "http://127.0.0.1:8080/test/?baz=meh#456",
1172 "http://127.0.0.1:8080/test/?foo=bar#123",
1173 "http://127.0.0.1:8080/test/video?baz=meh#456",
1174 "video?baz=meh#456",
1177 "http://127.0.0.1:8080/file.txt",
1178 "http://127.0.0.1:8080/test/file.txt",
1182 "http://127.0.0.1:8080/not_equal.txt",
1183 "http://127.0.0.1:8080/test/file.txt",
1188 for (base
, uri
, relative
) in &tests
{
1189 let base_uri
= url
::Url
::parse(base
).unwrap();
1190 let relative_uri
= url
::Url
::parse(uri
).unwrap();
1191 let make_relative
= base_uri
.make_relative(&relative_uri
).unwrap();
1193 make_relative
, *relative
,
1194 "base: {}, uri: {}, relative: {}",
1198 base_uri
.join(relative
).unwrap().as_str(),
1200 "base: {}, uri: {}, relative: {}",
1208 ("http://127.0.0.1:8080/", "https://127.0.0.1:8080/test/"),
1209 ("http://127.0.0.1:8080/", "http://127.0.0.1:8081/test/"),
1210 ("http://127.0.0.1:8080/", "http://127.0.0.2:8080/test/"),
1211 ("mailto:a@example.com", "mailto:b@example.com"),
1214 for (base
, uri
) in &error_tests
{
1215 let base_uri
= url
::Url
::parse(base
).unwrap();
1216 let relative_uri
= url
::Url
::parse(uri
).unwrap();
1217 let make_relative
= base_uri
.make_relative(&relative_uri
);
1218 assert_eq
!(make_relative
, None
, "base: {}, uri: {}", base
, uri
);
1223 fn test_has_authority() {
1224 let url
= Url
::parse("mailto:joe@example.com").unwrap();
1225 assert
!(!url
.has_authority());
1226 let url
= Url
::parse("unix:/run/foo.socket").unwrap();
1227 assert
!(!url
.has_authority());
1228 let url
= Url
::parse("file:///tmp/foo").unwrap();
1229 assert
!(url
.has_authority());
1230 let url
= Url
::parse("http://example.com/tmp/foo").unwrap();
1231 assert
!(url
.has_authority());
1235 fn test_authority() {
1236 let url
= Url
::parse("mailto:joe@example.com").unwrap();
1237 assert_eq
!(url
.authority(), "");
1238 let url
= Url
::parse("unix:/run/foo.socket").unwrap();
1239 assert_eq
!(url
.authority(), "");
1240 let url
= Url
::parse("file:///tmp/foo").unwrap();
1241 assert_eq
!(url
.authority(), "");
1242 let url
= Url
::parse("http://example.com/tmp/foo").unwrap();
1243 assert_eq
!(url
.authority(), "example.com");
1244 let url
= Url
::parse("ftp://127.0.0.1:21/").unwrap();
1245 assert_eq
!(url
.authority(), "127.0.0.1");
1246 let url
= Url
::parse("ftp://user@127.0.0.1:2121/").unwrap();
1247 assert_eq
!(url
.authority(), "user@127.0.0.1:2121");
1248 let url
= Url
::parse("https://:@example.com/").unwrap();
1249 assert_eq
!(url
.authority(), "example.com");
1250 let url
= Url
::parse("https://:password@[::1]:8080/").unwrap();
1251 assert_eq
!(url
.authority(), ":password@[::1]:8080");
1252 let url
= Url
::parse("gopher://user:@àlex.example.com:70").unwrap();
1253 assert_eq
!(url
.authority(), "user@%C3%A0lex.example.com:70");
1254 let url
= Url
::parse("irc://àlex:àlex@àlex.рф.example.com:6667/foo").unwrap();
1257 "%C3%A0lex:%C3%A0lex@%C3%A0lex.%D1%80%D1%84.example.com:6667"
1259 let url
= Url
::parse("https://àlex:àlex@àlex.рф.example.com:443/foo").unwrap();
1262 "%C3%A0lex:%C3%A0lex@xn--lex-8ka.xn--p1ai.example.com"
1267 /// https://github.com/servo/rust-url/issues/838
1268 fn test_file_with_drive() {
1269 let s1
= "fIlE:p:?../";
1270 let url
= url
::Url
::parse(s1
).unwrap();
1271 assert_eq
!(url
.to_string(), "file:///p:?../");
1272 assert_eq
!(url
.path(), "/p:");
1275 ("a", "file:///p:/a"),
1276 ("", "file:///p:?../"),
1277 ("?x", "file:///p:?x"),
1278 (".", "file:///p:/"),
1279 ("..", "file:///p:/"),
1280 ("../", "file:///p:/"),
1283 for case
in &testcases
{
1284 let url2
= url
::Url
::join(&url
, case
.0).unwrap();
1285 assert_eq
!(url2
.to_string(), case
.1);
1290 /// Similar to test_file_with_drive, but with a path
1291 /// that could be confused for a drive.
1292 fn test_file_with_drive_and_path() {
1293 let s1
= "fIlE:p:/x|?../";
1294 let url
= url
::Url
::parse(s1
).unwrap();
1295 assert_eq
!(url
.to_string(), "file:///p:/x|?../");
1296 assert_eq
!(url
.path(), "/p:/x|");
1298 let url2
= url
::Url
::join(&url
, s2
).unwrap();
1299 assert_eq
!(url2
.to_string(), "file:///p:/a");
1304 let mut url
= url
::Url
::parse("file://").unwrap();