blob: 9c122957947f3ac09dc53cedff2582f16ac7448a [file] [log] [blame]
mkwst28c7c112015-07-14 22:41:061// 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 <string.h>
8
9#include "base/logging.h"
tyoshino11a7c9fe2015-08-19 08:51:4610#include "base/numerics/safe_conversions.h"
mkwst28c7c112015-07-14 22:41:0611#include "base/strings/string_number_conversions.h"
12#include "url/gurl.h"
13#include "url/url_canon.h"
14#include "url/url_canon_stdstring.h"
15#include "url/url_constants.h"
16#include "url/url_util.h"
17
18namespace url {
19
tyoshino11a7c9fe2015-08-19 08:51:4620namespace {
mkwst28c7c112015-07-14 22:41:0621
tyoshino11a7c9fe2015-08-19 08:51:4622bool IsCanonicalHost(const base::StringPiece& host) {
msramek9b7972dd2015-08-18 13:04:1923 std::string canon_host;
tyoshino11a7c9fe2015-08-19 08:51:4624
25 // Try to canonicalize the host (copy/pasted from net/base. :( ).
26 const Component raw_host_component(0,
27 base::checked_cast<int>(host.length()));
28 StdStringCanonOutput canon_host_output(&canon_host);
29 CanonHostInfo host_info;
30 CanonicalizeHostVerbose(host.data(), raw_host_component,
31 &canon_host_output, &host_info);
mkwst28c7c112015-07-14 22:41:0632
33 if (host_info.out_host.is_nonempty() &&
tyoshino11a7c9fe2015-08-19 08:51:4634 host_info.family != CanonHostInfo::BROKEN) {
mkwst28c7c112015-07-14 22:41:0635 // Success! Assert that there's no extra garbage.
36 canon_host_output.Complete();
37 DCHECK_EQ(host_info.out_host.len, static_cast<int>(canon_host.length()));
38 } else {
39 // Empty host, or canonicalization failed.
40 canon_host.clear();
41 }
mkwst28c7c112015-07-14 22:41:0642
tyoshino11a7c9fe2015-08-19 08:51:4643 return host == canon_host;
mkwst28c7c112015-07-14 22:41:0644}
45
tyoshino11a7c9fe2015-08-19 08:51:4646bool IsValidInput(const base::StringPiece& scheme,
47 const base::StringPiece& host,
48 uint16 port) {
49 SchemeType scheme_type = SCHEME_WITH_PORT;
50 bool is_standard = GetStandardSchemeType(
51 scheme.data(),
52 Component(0, base::checked_cast<int>(scheme.length())),
53 &scheme_type);
54 if (!is_standard)
55 return false;
mkwst28c7c112015-07-14 22:41:0656
57 // These schemes do not follow the generic URL syntax, so we treat them as
58 // invalid (scheme, host, port) tuples (even though such URLs' _Origin_ might
59 // have a (scheme, host, port) tuple, they themselves do not).
tyoshino11a7c9fe2015-08-19 08:51:4660 if (scheme == kFileSystemScheme || scheme == kBlobScheme)
61 return false;
62
63 switch (scheme_type) {
64 case SCHEME_WITH_PORT:
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 if (!IsCanonicalHost(host))
72 return false;
73
74 return true;
75
76 case SCHEME_WITHOUT_PORT:
77 if (port != 0) {
78 // Return an invalid object if a URL with the scheme never represents
79 // the port data but the given |port| is non-zero.
80 return false;
81 }
82
83 if (!IsCanonicalHost(host))
84 return false;
85
86 return true;
87
88 case SCHEME_WITHOUT_AUTHORITY:
89 return false;
90
91 default:
92 NOTREACHED();
93 return false;
94 }
95}
96
97} // namespace
98
99SchemeHostPort::SchemeHostPort() : port_(0) {
100}
101
102SchemeHostPort::SchemeHostPort(base::StringPiece scheme,
103 base::StringPiece host,
104 uint16 port)
105 : port_(0) {
106 if (!IsValidInput(scheme, host, port))
mkwst28c7c112015-07-14 22:41:06107 return;
108
tyoshino11a7c9fe2015-08-19 08:51:46109 scheme.CopyToString(&scheme_);
110 host.CopyToString(&host_);
111 port_ = port;
112}
113
114SchemeHostPort::SchemeHostPort(const GURL& url) : port_(0) {
115 if (!url.is_valid())
116 return;
117
118 const std::string& scheme = url.scheme();
119 const std::string& host = url.host();
120
121 // A valid GURL never returns PORT_INVALID.
122 int port = url.EffectiveIntPort();
123 if (port == PORT_UNSPECIFIED)
124 port = 0;
125
126 if (!IsValidInput(scheme, host, port))
127 return;
128
129 scheme_ = scheme;
130 host_ = host;
131 port_ = port;
mkwst28c7c112015-07-14 22:41:06132}
133
134SchemeHostPort::~SchemeHostPort() {
135}
136
137bool SchemeHostPort::IsInvalid() const {
138 return scheme_.empty() && host_.empty() && !port_;
139}
140
141std::string SchemeHostPort::Serialize() const {
142 std::string result;
143 if (IsInvalid())
144 return result;
145
mkwst28c7c112015-07-14 22:41:06146 result.append(scheme_);
147 result.append(kStandardSchemeSeparator);
148 result.append(host_);
149
tyoshino11a7c9fe2015-08-19 08:51:46150 if (port_ == 0)
151 return result;
152
153 // Omit the port component if the port matches with the default port
154 // defined for the scheme, if any.
155 int default_port = DefaultPortForScheme(scheme_.data(),
156 static_cast<int>(scheme_.length()));
157 if (default_port == PORT_UNSPECIFIED)
158 return result;
159 if (port_ != default_port) {
mkwst28c7c112015-07-14 22:41:06160 result.push_back(':');
161 result.append(base::IntToString(port_));
162 }
163
164 return result;
165}
166
167bool SchemeHostPort::Equals(const SchemeHostPort& other) const {
168 return port_ == other.port() && scheme_ == other.scheme() &&
169 host_ == other.host();
170}
171
172bool SchemeHostPort::operator<(const SchemeHostPort& other) const {
173 if (port_ != other.port_)
174 return port_ < other.port_;
175 if (scheme_ != other.scheme_)
176 return scheme_ < other.scheme_;
177 if (host_ != other.host_)
178 return host_ < other.host_;
179 return false;
180}
181
182} // namespace url