1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/base/schemeful_site.h"
6
7 #include <string_view>
8
9 #include "base/check.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/trace_event/memory_usage_estimator.h"
12 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
13 #include "net/base/url_util.h"
14 #include "url/gurl.h"
15 #include "url/url_canon.h"
16 #include "url/url_constants.h"
17
18 namespace net {
19
20 struct SchemefulSite::ObtainASiteResult {
21 // This is only set if the supplied origin differs from calculated one.
22 std::optional<url::Origin> origin;
23 bool used_registerable_domain;
24 };
25
26 // Return a tuple containing:
27 // * a new origin using the registerable domain of `origin` if possible and
28 // a port of 0; otherwise, the passed-in origin.
29 // * a bool indicating whether `origin` had a non-null registerable domain.
30 // (False if `origin` was opaque.)
31 //
32 // Follows steps specified in
33 // https://html.spec.whatwg.org/multipage/origin.html#obtain-a-site
ObtainASite(const url::Origin & origin)34 SchemefulSite::ObtainASiteResult SchemefulSite::ObtainASite(
35 const url::Origin& origin) {
36 // 1. If origin is an opaque origin, then return origin.
37 if (origin.opaque()) {
38 return {std::nullopt, false /* used_registerable_domain */};
39 }
40
41 int port = url::DefaultPortForScheme(origin.scheme());
42
43 // Provide a default port of 0 for non-standard schemes.
44 if (port == url::PORT_UNSPECIFIED) {
45 port = 0;
46 }
47
48 std::string_view registerable_domain;
49
50 // Non-normative step.
51 // We only lookup the registerable domain for schemes with network hosts, this
52 // is non-normative. Other schemes for non-opaque origins do not
53 // meaningfully have a registerable domain for their host, so they are
54 // skipped.
55 if (IsStandardSchemeWithNetworkHost(origin.scheme())) {
56 registerable_domain = GetDomainAndRegistryAsStringPiece(
57 origin, net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
58 if (!registerable_domain.empty() &&
59 registerable_domain.size() == origin.host().size() &&
60 origin.port() == port) {
61 return {std::nullopt, /* used_registerable_domain */ true};
62 }
63 }
64
65 // If origin's host's registrable domain is null, then return (origin's
66 // scheme, origin's host).
67 //
68 // `GetDomainAndRegistry()` returns an empty string for IP literals and
69 // effective TLDs.
70 //
71 // Note that `registerable_domain` could still end up empty, since the
72 // `origin` might have a scheme that permits empty hostnames, such as "file".
73 bool used_registerable_domain = !registerable_domain.empty();
74 if (!used_registerable_domain)
75 registerable_domain = origin.host();
76
77 return {url::Origin::CreateFromNormalizedTuple(
78 origin.scheme(), std::string(registerable_domain), port),
79 used_registerable_domain};
80 }
81
SchemefulSite(ObtainASiteResult result,const url::Origin & origin)82 SchemefulSite::SchemefulSite(ObtainASiteResult result,
83 const url::Origin& origin) {
84 if (result.origin) {
85 site_as_origin_ = std::move(*(result.origin));
86 } else {
87 site_as_origin_ = origin;
88 }
89 }
90
SchemefulSite(const url::Origin & origin)91 SchemefulSite::SchemefulSite(const url::Origin& origin)
92 : SchemefulSite(ObtainASite(origin), origin) {}
93
SchemefulSite(const GURL & url)94 SchemefulSite::SchemefulSite(const GURL& url)
95 : SchemefulSite(url::Origin::Create(url)) {}
96
97 SchemefulSite::SchemefulSite(const SchemefulSite& other) = default;
98 SchemefulSite::SchemefulSite(SchemefulSite&& other) noexcept = default;
99
100 SchemefulSite& SchemefulSite::operator=(const SchemefulSite& other) = default;
101 SchemefulSite& SchemefulSite::operator=(SchemefulSite&& other) noexcept =
102 default;
103
104 // static
FromWire(const url::Origin & site_as_origin,SchemefulSite * out)105 bool SchemefulSite::FromWire(const url::Origin& site_as_origin,
106 SchemefulSite* out) {
107 // The origin passed into this constructor may not match the
108 // `site_as_origin_` used as the internal representation of the schemeful
109 // site. However, a valid SchemefulSite's internal origin should result in a
110 // match if used to construct another SchemefulSite. Thus, if there is a
111 // mismatch here, we must indicate a failure.
112 SchemefulSite candidate(site_as_origin);
113 if (candidate.site_as_origin_ != site_as_origin)
114 return false;
115
116 *out = std::move(candidate);
117 return true;
118 }
119
CreateIfHasRegisterableDomain(const url::Origin & origin)120 std::optional<SchemefulSite> SchemefulSite::CreateIfHasRegisterableDomain(
121 const url::Origin& origin) {
122 ObtainASiteResult result = ObtainASite(origin);
123 if (!result.used_registerable_domain) {
124 return std::nullopt;
125 }
126 return SchemefulSite(std::move(result), origin);
127 }
128
ConvertWebSocketToHttp()129 void SchemefulSite::ConvertWebSocketToHttp() {
130 if (site_as_origin_.scheme() == url::kWsScheme ||
131 site_as_origin_.scheme() == url::kWssScheme) {
132 site_as_origin_ = url::Origin::Create(
133 ChangeWebSocketSchemeToHttpScheme(site_as_origin_.GetURL()));
134 }
135 }
136
137 // static
Deserialize(std::string_view value)138 SchemefulSite SchemefulSite::Deserialize(std::string_view value) {
139 return SchemefulSite(GURL(value));
140 }
141
Serialize() const142 std::string SchemefulSite::Serialize() const {
143 return site_as_origin_.Serialize();
144 }
145
SerializeFileSiteWithHost() const146 std::string SchemefulSite::SerializeFileSiteWithHost() const {
147 DCHECK_EQ(url::kFileScheme, site_as_origin_.scheme());
148 return site_as_origin_.GetTupleOrPrecursorTupleIfOpaque().Serialize();
149 }
150
GetDebugString() const151 std::string SchemefulSite::GetDebugString() const {
152 return site_as_origin_.GetDebugString();
153 }
154
GetURL() const155 GURL SchemefulSite::GetURL() const {
156 return site_as_origin_.GetURL();
157 }
158
GetInternalOriginForTesting() const159 const url::Origin& SchemefulSite::GetInternalOriginForTesting() const {
160 return site_as_origin_;
161 }
162
EstimateMemoryUsage() const163 size_t SchemefulSite::EstimateMemoryUsage() const {
164 return base::trace_event::EstimateMemoryUsage(site_as_origin_);
165 }
166
operator ==(const SchemefulSite & other) const167 bool SchemefulSite::operator==(const SchemefulSite& other) const {
168 return site_as_origin_ == other.site_as_origin_;
169 }
170
operator !=(const SchemefulSite & other) const171 bool SchemefulSite::operator!=(const SchemefulSite& other) const {
172 return !(*this == other);
173 }
174
175 // Allows SchemefulSite to be used as a key in STL containers (for example, a
176 // std::set or std::map).
operator <(const SchemefulSite & other) const177 bool SchemefulSite::operator<(const SchemefulSite& other) const {
178 return site_as_origin_ < other.site_as_origin_;
179 }
180
181 // static
DeserializeWithNonce(base::PassKey<NetworkAnonymizationKey>,std::string_view value)182 std::optional<SchemefulSite> SchemefulSite::DeserializeWithNonce(
183 base::PassKey<NetworkAnonymizationKey>,
184 std::string_view value) {
185 return DeserializeWithNonce(value);
186 }
187
188 // static
DeserializeWithNonce(std::string_view value)189 std::optional<SchemefulSite> SchemefulSite::DeserializeWithNonce(
190 std::string_view value) {
191 std::optional<url::Origin> result = url::Origin::Deserialize(value);
192 if (!result)
193 return std::nullopt;
194 return SchemefulSite(result.value());
195 }
196
SerializeWithNonce(base::PassKey<NetworkAnonymizationKey>)197 std::optional<std::string> SchemefulSite::SerializeWithNonce(
198 base::PassKey<NetworkAnonymizationKey>) {
199 return SerializeWithNonce();
200 }
201
SerializeWithNonce()202 std::optional<std::string> SchemefulSite::SerializeWithNonce() {
203 return site_as_origin_.SerializeWithNonceAndInitIfNeeded();
204 }
205
SchemelesslyEqual(const SchemefulSite & other) const206 bool SchemefulSite::SchemelesslyEqual(const SchemefulSite& other) const {
207 return site_as_origin_.host() == other.site_as_origin_.host();
208 }
209
operator <<(std::ostream & os,const SchemefulSite & ss)210 std::ostream& operator<<(std::ostream& os, const SchemefulSite& ss) {
211 os << ss.Serialize();
212 return os;
213 }
214
215 } // namespace net
216