| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/schemeful_site.h" |
| |
| #include "base/test/metrics/histogram_tester.h" |
| #include "net/base/url_util.h" |
| #include "testing/gmock/include/gmock/gmock-matchers.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| #include "url/url_util.h" |
| |
| namespace net { |
| |
| TEST(SchemefulSiteTest, DifferentOriginSameRegisterableDomain) { |
| // List of origins which should all share a schemeful site. |
| url::Origin kTestOrigins[] = { |
| url::Origin::Create(GURL("http://a.foo.test")), |
| url::Origin::Create(GURL("http://b.foo.test")), |
| url::Origin::Create(GURL("http://foo.test")), |
| url::Origin::Create(GURL("http://a.b.foo.test"))}; |
| |
| for (const auto& origin_a : kTestOrigins) { |
| for (const auto& origin_b : kTestOrigins) { |
| EXPECT_EQ(SchemefulSite(origin_a), SchemefulSite(origin_b)); |
| } |
| } |
| } |
| |
| TEST(SchemefulSiteTest, Operators) { |
| // Create a list of origins that should all have different schemeful sites. |
| // These are in ascending order. |
| url::Origin kTestOrigins[] = { |
| url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")), |
| url::Origin::Create(GURL("file://foo")), |
| url::Origin::Create(GURL("http://a.bar.test")), |
| url::Origin::Create(GURL("http://c.test")), |
| url::Origin::Create(GURL("http://d.test")), |
| url::Origin::Create(GURL("http://a.foo.test")), |
| url::Origin::Create(GURL("https://a.bar.test")), |
| url::Origin::Create(GURL("https://c.test")), |
| url::Origin::Create(GURL("https://d.test")), |
| url::Origin::Create(GURL("https://a.foo.test"))}; |
| |
| // Compare each origin to every other origin and ensure the operators work as |
| // expected. |
| for (size_t first = 0; first < std::size(kTestOrigins); ++first) { |
| SchemefulSite site1 = SchemefulSite(kTestOrigins[first]); |
| SCOPED_TRACE(site1.GetDebugString()); |
| |
| EXPECT_EQ(site1, site1); |
| EXPECT_FALSE(site1 < site1); |
| |
| // Check the operators work on copies. |
| SchemefulSite site1_copy = site1; |
| EXPECT_EQ(site1, site1_copy); |
| EXPECT_FALSE(site1 < site1_copy); |
| |
| for (size_t second = first + 1; second < std::size(kTestOrigins); |
| ++second) { |
| SchemefulSite site2 = SchemefulSite(kTestOrigins[second]); |
| SCOPED_TRACE(site2.GetDebugString()); |
| |
| EXPECT_TRUE(site1 < site2); |
| EXPECT_FALSE(site2 < site1); |
| EXPECT_FALSE(site1 == site2); |
| EXPECT_FALSE(site2 == site1); |
| } |
| } |
| } |
| |
| TEST(SchemefulSiteTest, SchemeUsed) { |
| url::Origin origin_a = url::Origin::Create(GURL("https://foo.test")); |
| url::Origin origin_b = url::Origin::Create(GURL("http://foo.test")); |
| EXPECT_NE(SchemefulSite(origin_a), SchemefulSite(origin_b)); |
| } |
| |
| TEST(SchemefulSiteTest, PortIgnored) { |
| // Both origins are non-opaque. |
| url::Origin origin_a = url::Origin::Create(GURL("https://foo.test:80")); |
| url::Origin origin_b = url::Origin::Create(GURL("https://foo.test:2395")); |
| |
| EXPECT_EQ(SchemefulSite(origin_a), SchemefulSite(origin_b)); |
| } |
| |
| TEST(SchemefulSiteTest, TopLevelDomainsNotModified) { |
| url::Origin origin_tld = url::Origin::Create(GURL("https://com")); |
| EXPECT_EQ(url::Origin::Create(GURL("https://com")), |
| SchemefulSite(origin_tld).GetInternalOriginForTesting()); |
| |
| // Unknown TLD's should not be modified. |
| url::Origin origin_tld_unknown = |
| url::Origin::Create(GURL("https://bar:1234")); |
| EXPECT_EQ(url::Origin::Create(GURL("https://bar")), |
| SchemefulSite(origin_tld_unknown).GetInternalOriginForTesting()); |
| |
| // Check for two-part TLDs. |
| url::Origin origin_two_part_tld = url::Origin::Create(GURL("http://a.co.uk")); |
| EXPECT_EQ(url::Origin::Create(GURL("http://a.co.uk")), |
| SchemefulSite(origin_two_part_tld).GetInternalOriginForTesting()); |
| } |
| |
| TEST(SchemefulSiteTest, NonStandardScheme) { |
| url::ScopedSchemeRegistryForTests scoped_registry; |
| url::AddStandardScheme("foo", url::SCHEME_WITH_HOST); |
| url::Origin origin = url::Origin::Create(GURL("foo://a.b.test")); |
| EXPECT_FALSE(origin.opaque()); |
| |
| // We should not use registerable domains for non-standard schemes, even if |
| // one exists for the host. |
| EXPECT_EQ(url::Origin::Create(GURL("foo://a.b.test")), |
| SchemefulSite(origin).GetInternalOriginForTesting()); |
| } |
| |
| TEST(SchemefulSiteTest, IPBasedOriginsRemovePort) { |
| // IPv4 and IPv6 origins should not be modified, except for removing their |
| // ports. |
| url::Origin origin_ipv4_a = |
| url::Origin::Create(GURL("http://127.0.0.1:1234")); |
| url::Origin origin_ipv4_b = url::Origin::Create(GURL("http://127.0.0.1")); |
| EXPECT_EQ(url::Origin::Create(GURL("http://127.0.0.1")), |
| SchemefulSite(origin_ipv4_a).GetInternalOriginForTesting()); |
| EXPECT_EQ(SchemefulSite(origin_ipv4_a), SchemefulSite(origin_ipv4_b)); |
| |
| url::Origin origin_ipv6 = url::Origin::Create(GURL("https://[::1]")); |
| EXPECT_EQ(url::Origin::Create(GURL("https://[::1]")), |
| SchemefulSite(origin_ipv6).GetInternalOriginForTesting()); |
| } |
| |
| TEST(SchemefulSiteTest, LocalhostOriginsRemovePort) { |
| // Localhost origins should not be modified, except for removing their ports. |
| url::Origin localhost_http = |
| url::Origin::Create(GURL("http://localhost:1234")); |
| EXPECT_EQ(url::Origin::Create(GURL("http://localhost")), |
| SchemefulSite(localhost_http).GetInternalOriginForTesting()); |
| |
| url::Origin localhost_https = |
| url::Origin::Create(GURL("https://localhost:1234")); |
| EXPECT_EQ(url::Origin::Create(GURL("https://localhost")), |
| SchemefulSite(localhost_https).GetInternalOriginForTesting()); |
| } |
| |
| TEST(SchemefulSiteTest, OpaqueOrigins) { |
| url::Origin opaque_origin_a = |
| url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")); |
| |
| // The schemeful site of an opaque origin should always equal other schemeful |
| // site instances of the same origin. |
| EXPECT_EQ(SchemefulSite(opaque_origin_a), SchemefulSite(opaque_origin_a)); |
| |
| url::Origin opaque_origin_b = |
| url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")); |
| |
| // Two different opaque origins should never have the same SchemefulSite. |
| EXPECT_NE(SchemefulSite(opaque_origin_a), SchemefulSite(opaque_origin_b)); |
| } |
| |
| TEST(SchemefulSiteTest, FileOriginWithoutHostname) { |
| SchemefulSite site1(url::Origin::Create(GURL("file:///"))); |
| SchemefulSite site2(url::Origin::Create(GURL("file:///path/"))); |
| |
| EXPECT_EQ(site1, site2); |
| EXPECT_TRUE(site1.GetInternalOriginForTesting().host().empty()); |
| } |
| |
| TEST(SchemefulSiteTest, SchemeWithNetworkHost) { |
| url::ScopedSchemeRegistryForTests scheme_registry; |
| AddStandardScheme("network", url::SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION); |
| AddStandardScheme("non-network", url::SCHEME_WITH_HOST); |
| |
| ASSERT_TRUE(IsStandardSchemeWithNetworkHost("network")); |
| ASSERT_FALSE(IsStandardSchemeWithNetworkHost("non-network")); |
| |
| std::optional<SchemefulSite> network_host_site = |
| SchemefulSite::CreateIfHasRegisterableDomain( |
| url::Origin::Create(GURL("network://site.example.test:1337"))); |
| EXPECT_TRUE(network_host_site.has_value()); |
| EXPECT_EQ("network", |
| network_host_site->GetInternalOriginForTesting().scheme()); |
| EXPECT_EQ("example.test", |
| network_host_site->GetInternalOriginForTesting().host()); |
| |
| std::optional<SchemefulSite> non_network_host_site_null = |
| SchemefulSite::CreateIfHasRegisterableDomain( |
| url::Origin::Create(GURL("non-network://site.example.test"))); |
| EXPECT_FALSE(non_network_host_site_null.has_value()); |
| SchemefulSite non_network_host_site(GURL("non-network://site.example.test")); |
| EXPECT_EQ("non-network", |
| non_network_host_site.GetInternalOriginForTesting().scheme()); |
| // The host is used as-is, without attempting to get a registrable domain. |
| EXPECT_EQ("site.example.test", |
| non_network_host_site.GetInternalOriginForTesting().host()); |
| } |
| |
| TEST(SchemefulSiteTest, FileSchemeHasRegistrableDomain) { |
| // Test file origin without host. |
| url::Origin origin_file = |
| url::Origin::Create(GURL("file:///dir1/dir2/file.txt")); |
| EXPECT_TRUE(origin_file.host().empty()); |
| SchemefulSite site_file(origin_file); |
| EXPECT_EQ(url::Origin::Create(GURL("file:///")), |
| site_file.GetInternalOriginForTesting()); |
| |
| // Test file origin with host (with registrable domain). |
| url::Origin origin_file_with_host = |
| url::Origin::Create(GURL("file://host.example.test/file")); |
| ASSERT_EQ("host.example.test", origin_file_with_host.host()); |
| SchemefulSite site_file_with_host(origin_file_with_host); |
| EXPECT_EQ(url::Origin::Create(GURL("file://example.test")), |
| site_file_with_host.GetInternalOriginForTesting()); |
| |
| // Test file origin with host same as registrable domain. |
| url::Origin origin_file_registrable_domain = |
| url::Origin::Create(GURL("file://example.test/file")); |
| ASSERT_EQ("example.test", origin_file_registrable_domain.host()); |
| SchemefulSite site_file_registrable_domain(origin_file_registrable_domain); |
| EXPECT_EQ(url::Origin::Create(GURL("file://example.test")), |
| site_file_registrable_domain.GetInternalOriginForTesting()); |
| |
| EXPECT_NE(site_file, site_file_with_host); |
| EXPECT_NE(site_file, site_file_registrable_domain); |
| EXPECT_EQ(site_file_with_host, site_file_registrable_domain); |
| } |
| |
| TEST(SchemefulSiteTest, SerializationConsistent) { |
| url::ScopedSchemeRegistryForTests scoped_registry; |
| url::AddStandardScheme("chrome", url::SCHEME_WITH_HOST); |
| |
| // List of origins which should all share a schemeful site. |
| SchemefulSite kTestSites[] = { |
| SchemefulSite(url::Origin::Create(GURL("http://a.foo.test"))), |
| SchemefulSite(url::Origin::Create(GURL("https://b.foo.test"))), |
| SchemefulSite(url::Origin::Create(GURL("http://b.foo.test"))), |
| SchemefulSite(url::Origin::Create(GURL("http://a.b.foo.test"))), |
| SchemefulSite(url::Origin::Create(GURL("chrome://a.b.test")))}; |
| |
| for (const auto& site : kTestSites) { |
| SCOPED_TRACE(site.GetDebugString()); |
| EXPECT_FALSE(site.GetInternalOriginForTesting().opaque()); |
| |
| std::optional<SchemefulSite> deserialized_site = |
| SchemefulSite::Deserialize(site.Serialize()); |
| EXPECT_TRUE(deserialized_site); |
| EXPECT_EQ(site, deserialized_site); |
| } |
| } |
| |
| TEST(SchemefulSiteTest, SerializationFileSiteWithHost) { |
| const struct { |
| SchemefulSite site; |
| std::string expected; |
| } kTestCases[] = { |
| {SchemefulSite(GURL("file:///etc/passwd")), "file://"}, |
| {SchemefulSite(GURL("file://example.com/etc/passwd")), |
| "file://example.com"}, |
| {SchemefulSite(GURL("file://example.com")), "file://example.com"}, |
| }; |
| |
| for (const auto& test_case : kTestCases) { |
| SCOPED_TRACE(test_case.site.GetDebugString()); |
| std::string serialized_site = test_case.site.SerializeFileSiteWithHost(); |
| EXPECT_EQ(test_case.expected, serialized_site); |
| std::optional<SchemefulSite> deserialized_site = |
| SchemefulSite::Deserialize(serialized_site); |
| EXPECT_TRUE(deserialized_site); |
| EXPECT_EQ(test_case.site, deserialized_site); |
| } |
| } |
| |
| TEST(SchemefulSiteTest, FileURLWithHostEquality) { |
| // Two file URLs with different hosts should result in unequal SchemefulSites. |
| SchemefulSite site1(GURL("file://foo/some/path.txt")); |
| SchemefulSite site2(GURL("file://bar/some/path.txt")); |
| EXPECT_NE(site1, site2); |
| |
| // Two file URLs with the same host should result in equal SchemefulSites. |
| SchemefulSite site3(GURL("file://foo/another/path.pdf")); |
| EXPECT_EQ(site1, site3); |
| } |
| |
| TEST(SchemefulSiteTest, OpaqueSerialization) { |
| // List of origins which should all share a schemeful site. |
| SchemefulSite kTestSites[] = { |
| SchemefulSite(), SchemefulSite(url::Origin()), |
| SchemefulSite(GURL("data:text/html,<body>Hello World</body>"))}; |
| |
| for (auto& site : kTestSites) { |
| std::optional<SchemefulSite> deserialized_site = |
| SchemefulSite::DeserializeWithNonce(*site.SerializeWithNonce()); |
| EXPECT_TRUE(deserialized_site); |
| EXPECT_EQ(site, *deserialized_site); |
| } |
| } |
| |
| TEST(SchemefulSiteTest, FromWire) { |
| SchemefulSite out; |
| |
| // Opaque origin. |
| EXPECT_TRUE(SchemefulSite::FromWire(url::Origin(), &out)); |
| EXPECT_TRUE(out.opaque()); |
| |
| // Valid origin. |
| EXPECT_TRUE(SchemefulSite::FromWire( |
| url::Origin::Create(GURL("https://example.test")), &out)); |
| EXPECT_EQ(SchemefulSite(url::Origin::Create(GURL("https://example.test"))), |
| out); |
| |
| // Invalid origin (not a registrable domain). |
| EXPECT_FALSE(SchemefulSite::FromWire( |
| url::Origin::Create(GURL("https://sub.example.test")), &out)); |
| |
| // Invalid origin (non-default port). |
| EXPECT_FALSE(SchemefulSite::FromWire( |
| url::Origin::Create(GURL("https://example.test:1337")), &out)); |
| } |
| |
| TEST(SchemefulSiteTest, CreateIfHasRegisterableDomain) { |
| for (const auto& site : std::initializer_list<std::string>{ |
| "http://a.bar.test", |
| "http://c.test", |
| "http://a.foo.test", |
| "https://a.bar.test", |
| "https://c.test", |
| "https://a.foo.test", |
| }) { |
| url::Origin origin = url::Origin::Create(GURL(site)); |
| EXPECT_THAT(SchemefulSite::CreateIfHasRegisterableDomain(origin), |
| testing::Optional(SchemefulSite(origin))) |
| << "site = \"" << site << "\""; |
| } |
| |
| for (const auto& site : std::initializer_list<std::string>{ |
| "data:text/html,<body>Hello World</body>", |
| "file:///", |
| "file://foo", |
| "http://127.0.0.1:1234", |
| "https://127.0.0.1:1234", |
| }) { |
| url::Origin origin = url::Origin::Create(GURL(site)); |
| EXPECT_EQ(SchemefulSite::CreateIfHasRegisterableDomain(origin), |
| std::nullopt) |
| << "site = \"" << site << "\""; |
| } |
| } |
| |
| TEST(SchemefulSiteTest, ConvertWebSocketToHttp) { |
| SchemefulSite ws_site(url::Origin::Create(GURL("ws://site.example.test"))); |
| SchemefulSite http_site( |
| url::Origin::Create(GURL("http://site.example.test"))); |
| SchemefulSite wss_site(url::Origin::Create(GURL("wss://site.example.test"))); |
| SchemefulSite https_site( |
| url::Origin::Create(GURL("https://site.example.test"))); |
| |
| ASSERT_NE(ws_site, wss_site); |
| ASSERT_NE(ws_site, http_site); |
| ASSERT_NE(ws_site, https_site); |
| ASSERT_NE(wss_site, http_site); |
| ASSERT_NE(wss_site, https_site); |
| |
| ws_site.ConvertWebSocketToHttp(); |
| wss_site.ConvertWebSocketToHttp(); |
| |
| EXPECT_EQ(ws_site, http_site); |
| EXPECT_EQ(wss_site, https_site); |
| |
| // Does not change non-WebSocket sites. |
| SchemefulSite http_site_copy(http_site); |
| http_site_copy.ConvertWebSocketToHttp(); |
| EXPECT_EQ(http_site, http_site_copy); |
| EXPECT_EQ(url::kHttpScheme, |
| http_site_copy.GetInternalOriginForTesting().scheme()); |
| |
| SchemefulSite file_site(url::Origin::Create(GURL("file:///"))); |
| file_site.ConvertWebSocketToHttp(); |
| EXPECT_EQ(url::kFileScheme, file_site.GetInternalOriginForTesting().scheme()); |
| } |
| |
| TEST(SchemefulSiteTest, GetGURL) { |
| struct { |
| url::Origin origin; |
| GURL wantGURL; |
| } kTestCases[] = { |
| { |
| url::Origin::Create(GURL("data:text/html,<body>Hello World</body>")), |
| GURL(), |
| }, |
| {url::Origin::Create(GURL("file://foo")), GURL("file:///")}, |
| {url::Origin::Create(GURL("http://a.bar.test")), GURL("http://bar.test")}, |
| {url::Origin::Create(GURL("http://c.test")), GURL("http://c.test")}, |
| {url::Origin::Create(GURL("http://c.test:8000")), GURL("http://c.test")}, |
| { |
| url::Origin::Create(GURL("https://a.bar.test")), |
| GURL("https://bar.test"), |
| }, |
| { |
| url::Origin::Create(GURL("https://c.test")), |
| GURL("https://c.test"), |
| }, |
| { |
| url::Origin::Create(GURL("https://c.test:1337")), |
| GURL("https://c.test"), |
| }, |
| }; |
| |
| for (const auto& testcase : kTestCases) { |
| SchemefulSite site(testcase.origin); |
| EXPECT_EQ(site.GetURL(), testcase.wantGURL); |
| } |
| } |
| |
| TEST(SchemefulSiteTest, InternalValue) { |
| url::Origin origin = url::Origin::Create(GURL("https://example.com")); |
| SchemefulSite site(origin); |
| EXPECT_EQ(site.internal_value(), origin); |
| url::Origin opaque_origin; |
| SchemefulSite opaque_site(opaque_origin); |
| EXPECT_EQ(opaque_site.internal_value(), opaque_origin); |
| } |
| |
| } // namespace net |