Upload upstream chromium 67.0.3396
[platform/framework/web/chromium-efl.git] / url / scheme_host_port.cc
1 // Copyright 2015 The Chromium Authors. All rights reserved.
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 "url/scheme_host_port.h"
6
7 #include <stdint.h>
8 #include <string.h>
9
10 #include <tuple>
11
12 #include "base/logging.h"
13 #include "base/numerics/safe_conversions.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "url/gurl.h"
16 #include "url/third_party/mozilla/url_parse.h"
17 #include "url/url_canon.h"
18 #include "url/url_canon_stdstring.h"
19 #include "url/url_constants.h"
20 #include "url/url_util.h"
21
22 namespace url {
23
24 namespace {
25
26 bool IsCanonicalHost(const base::StringPiece& host) {
27   std::string canon_host;
28
29   // Try to canonicalize the host (copy/pasted from net/base. :( ).
30   const Component raw_host_component(0,
31                                      base::checked_cast<int>(host.length()));
32   StdStringCanonOutput canon_host_output(&canon_host);
33   CanonHostInfo host_info;
34   CanonicalizeHostVerbose(host.data(), raw_host_component,
35                           &canon_host_output, &host_info);
36
37   if (host_info.out_host.is_nonempty() &&
38       host_info.family != CanonHostInfo::BROKEN) {
39     // Success!  Assert that there's no extra garbage.
40     canon_host_output.Complete();
41     DCHECK_EQ(host_info.out_host.len, static_cast<int>(canon_host.length()));
42   } else {
43     // Empty host, or canonicalization failed.
44     canon_host.clear();
45   }
46
47   return host == canon_host;
48 }
49
50 bool IsValidInput(const base::StringPiece& scheme,
51                   const base::StringPiece& host,
52                   uint16_t port,
53                   SchemeHostPort::ConstructPolicy policy) {
54   SchemeType scheme_type = SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION;
55   bool is_standard = GetStandardSchemeType(
56       scheme.data(),
57       Component(0, base::checked_cast<int>(scheme.length())),
58       &scheme_type);
59   if (!is_standard)
60     return false;
61
62   switch (scheme_type) {
63     case SCHEME_WITH_HOST_AND_PORT:
64     case SCHEME_WITH_HOST_PORT_AND_USER_INFORMATION:
65       // A URL with |scheme| is required to have the host and port (may be
66       // omitted in a serialization if it's the same as the default value).
67       // Return an invalid instance if either of them is not given.
68       if (host.empty() || port == 0)
69         return false;
70
71       // Don't do an expensive canonicalization if the host is already
72       // canonicalized.
73       DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION ||
74              IsCanonicalHost(host));
75       if (policy == SchemeHostPort::CHECK_CANONICALIZATION &&
76           !IsCanonicalHost(host)) {
77         return false;
78       }
79
80       return true;
81
82     case SCHEME_WITH_HOST:
83       if (port != 0) {
84         // Return an invalid object if a URL with the scheme never represents
85         // the port data but the given |port| is non-zero.
86         return false;
87       }
88
89       // Don't do an expensive canonicalization if the host is already
90       // canonicalized.
91       DCHECK(policy == SchemeHostPort::CHECK_CANONICALIZATION ||
92              IsCanonicalHost(host));
93       if (policy == SchemeHostPort::CHECK_CANONICALIZATION &&
94           !IsCanonicalHost(host)) {
95         return false;
96       }
97
98       return true;
99
100     case SCHEME_WITHOUT_AUTHORITY:
101       return false;
102
103     default:
104       NOTREACHED();
105       return false;
106   }
107 }
108
109 }  // namespace
110
111 SchemeHostPort::SchemeHostPort() : port_(0) {
112 }
113
114 SchemeHostPort::SchemeHostPort(std::string scheme,
115                                std::string host,
116                                uint16_t port,
117                                ConstructPolicy policy)
118     : port_(0) {
119   if (!IsValidInput(scheme, host, port, policy))
120     return;
121
122   scheme_ = std::move(scheme);
123   host_ = std::move(host);
124   port_ = port;
125 }
126
127 SchemeHostPort::SchemeHostPort(base::StringPiece scheme,
128                                base::StringPiece host,
129                                uint16_t port)
130     : SchemeHostPort(scheme.as_string(),
131                      host.as_string(),
132                      port,
133                      ConstructPolicy::CHECK_CANONICALIZATION) {}
134
135 SchemeHostPort::SchemeHostPort(const GURL& url) : port_(0) {
136   if (!url.is_valid())
137     return;
138
139   base::StringPiece scheme = url.scheme_piece();
140   base::StringPiece host = url.host_piece();
141
142   // A valid GURL never returns PORT_INVALID.
143   int port = url.EffectiveIntPort();
144   if (port == PORT_UNSPECIFIED) {
145     port = 0;
146   } else {
147     DCHECK_GE(port, 0);
148     DCHECK_LE(port, 65535);
149   }
150
151   if (!IsValidInput(scheme, host, port, ALREADY_CANONICALIZED))
152     return;
153
154   scheme.CopyToString(&scheme_);
155   host.CopyToString(&host_);
156   port_ = port;
157 }
158
159 SchemeHostPort::~SchemeHostPort() = default;
160
161 bool SchemeHostPort::IsInvalid() const {
162   return scheme_.empty() && host_.empty() && !port_;
163 }
164
165 std::string SchemeHostPort::Serialize() const {
166   // Null checking for |parsed| in SerializeInternal is probably slower than
167   // just filling it in and discarding it here.
168   url::Parsed parsed;
169   return SerializeInternal(&parsed);
170 }
171
172 GURL SchemeHostPort::GetURL() const {
173   url::Parsed parsed;
174   std::string serialized = SerializeInternal(&parsed);
175
176   if (IsInvalid())
177     return GURL(std::move(serialized), parsed, false);
178
179   // SchemeHostPort does not have enough information to determine if an empty
180   // host is valid or not for the given scheme. Force re-parsing.
181   DCHECK(!scheme_.empty());
182   if (host_.empty())
183     return GURL(serialized);
184
185   // If the serialized string is passed to GURL for parsing, it will append an
186   // empty path "/". Add that here. Note: per RFC 6454 we cannot do this for
187   // normal Origin serialization.
188   DCHECK(!parsed.path.is_valid());
189   parsed.path = Component(serialized.length(), 1);
190   serialized.append("/");
191   return GURL(std::move(serialized), parsed, true);
192 }
193
194 bool SchemeHostPort::Equals(const SchemeHostPort& other) const {
195   return port_ == other.port() && scheme_ == other.scheme() &&
196          host_ == other.host();
197 }
198
199 bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
200   return std::tie(port_, scheme_, host_) <
201          std::tie(other.port_, other.scheme_, other.host_);
202 }
203
204 std::string SchemeHostPort::SerializeInternal(url::Parsed* parsed) const {
205   std::string result;
206   if (IsInvalid())
207     return result;
208
209   // Reserve enough space for the "normal" case of scheme://host/.
210   result.reserve(scheme_.size() + host_.size() + 4);
211
212   if (!scheme_.empty()) {
213     parsed->scheme = Component(0, scheme_.length());
214     result.append(scheme_);
215   }
216
217   result.append(kStandardSchemeSeparator);
218
219   if (!host_.empty()) {
220     parsed->host = Component(result.length(), host_.length());
221     result.append(host_);
222   }
223
224   if (port_ == 0)
225     return result;
226
227   // Omit the port component if the port matches with the default port
228   // defined for the scheme, if any.
229   int default_port = DefaultPortForScheme(scheme_.data(),
230                                           static_cast<int>(scheme_.length()));
231   if (default_port == PORT_UNSPECIFIED)
232     return result;
233   if (port_ != default_port) {
234     result.push_back(':');
235     std::string port(base::UintToString(port_));
236     parsed->port = Component(result.length(), port.length());
237     result.append(std::move(port));
238   }
239
240   return result;
241 }
242
243 }  // namespace url