• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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 "url/scheme_host_port.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "url/gurl.h"
12 #include "url/url_util.h"
13 
14 namespace {
15 
16 class SchemeHostPortTest : public testing::Test {
17  public:
18   SchemeHostPortTest() = default;
19 
20   SchemeHostPortTest(const SchemeHostPortTest&) = delete;
21   SchemeHostPortTest& operator=(const SchemeHostPortTest&) = delete;
22 
23   ~SchemeHostPortTest() override = default;
24 
25  private:
26   url::ScopedSchemeRegistryForTests scoped_registry_;
27 };
28 
ExpectParsedUrlsEqual(const GURL & a,const GURL & b)29 void ExpectParsedUrlsEqual(const GURL& a, const GURL& b) {
30   EXPECT_EQ(a, b);
31   const url::Parsed& a_parsed = a.parsed_for_possibly_invalid_spec();
32   const url::Parsed& b_parsed = b.parsed_for_possibly_invalid_spec();
33   EXPECT_EQ(a_parsed.scheme.begin, b_parsed.scheme.begin);
34   EXPECT_EQ(a_parsed.scheme.len, b_parsed.scheme.len);
35   EXPECT_EQ(a_parsed.username.begin, b_parsed.username.begin);
36   EXPECT_EQ(a_parsed.username.len, b_parsed.username.len);
37   EXPECT_EQ(a_parsed.password.begin, b_parsed.password.begin);
38   EXPECT_EQ(a_parsed.password.len, b_parsed.password.len);
39   EXPECT_EQ(a_parsed.host.begin, b_parsed.host.begin);
40   EXPECT_EQ(a_parsed.host.len, b_parsed.host.len);
41   EXPECT_EQ(a_parsed.port.begin, b_parsed.port.begin);
42   EXPECT_EQ(a_parsed.port.len, b_parsed.port.len);
43   EXPECT_EQ(a_parsed.path.begin, b_parsed.path.begin);
44   EXPECT_EQ(a_parsed.path.len, b_parsed.path.len);
45   EXPECT_EQ(a_parsed.query.begin, b_parsed.query.begin);
46   EXPECT_EQ(a_parsed.query.len, b_parsed.query.len);
47   EXPECT_EQ(a_parsed.ref.begin, b_parsed.ref.begin);
48   EXPECT_EQ(a_parsed.ref.len, b_parsed.ref.len);
49 }
50 
TEST_F(SchemeHostPortTest,Invalid)51 TEST_F(SchemeHostPortTest, Invalid) {
52   url::SchemeHostPort invalid;
53   EXPECT_EQ("", invalid.scheme());
54   EXPECT_EQ("", invalid.host());
55   EXPECT_EQ(0, invalid.port());
56   EXPECT_FALSE(invalid.IsValid());
57   EXPECT_EQ(invalid, invalid);
58 
59   const char* urls[] = {
60       // about:, data:, javascript: and other no-access schemes translate into
61       // an invalid SchemeHostPort
62       "about:blank", "about:blank#ref", "about:blank?query=123", "about:srcdoc",
63       "about:srcdoc#ref", "about:srcdoc?query=123", "data:text/html,Hello!",
64       "javascript:alert(1)",
65 
66       // GURLs where GURL::is_valid returns false translate into an invalid
67       // SchemeHostPort.
68       "file://example.com:443/etc/passwd", "#!^%!$!&*",
69 
70       // These schemes do not follow the generic URL syntax, so make sure we
71       // treat them as invalid (scheme, host, port) tuples (even though such
72       // URLs' _Origin_ might have a (scheme, host, port) tuple, they themselves
73       // do not). This is only *implicitly* checked in the code, by means of
74       // blob schemes not being standard, and filesystem schemes having type
75       // SCHEME_WITHOUT_AUTHORITY. If conditions change such that the implicit
76       // checks no longer hold, this policy should be made explicit.
77       "blob:https://example.com/uuid-goes-here",
78       "filesystem:https://example.com/temporary/yay.png"};
79 
80   for (auto* test : urls) {
81     SCOPED_TRACE(test);
82     GURL url(test);
83     url::SchemeHostPort tuple(url);
84     EXPECT_EQ("", tuple.scheme());
85     EXPECT_EQ("", tuple.host());
86     EXPECT_EQ(0, tuple.port());
87     EXPECT_FALSE(tuple.IsValid());
88     EXPECT_EQ(tuple, tuple);
89     EXPECT_EQ(tuple, invalid);
90     EXPECT_EQ(invalid, tuple);
91     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
92   }
93 }
94 
TEST_F(SchemeHostPortTest,ExplicitConstruction)95 TEST_F(SchemeHostPortTest, ExplicitConstruction) {
96   struct TestCases {
97     const char* scheme;
98     const char* host;
99     uint16_t port;
100   } cases[] = {
101       {"http", "example.com", 80},
102       {"http", "example.com", 123},
103       {"http", "example.com", 0},  // 0 is a valid port for http.
104       {"https", "example.com", 443},
105       {"https", "example.com", 123},
106       {"file", "", 0},  // 0 indicates "no port" for file: scheme.
107       {"file", "example.com", 0},
108   };
109 
110   for (const auto& test : cases) {
111     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
112                                     << test.port);
113     url::SchemeHostPort tuple(test.scheme, test.host, test.port);
114     EXPECT_EQ(test.scheme, tuple.scheme());
115     EXPECT_EQ(test.host, tuple.host());
116     EXPECT_EQ(test.port, tuple.port());
117     EXPECT_TRUE(tuple.IsValid());
118     EXPECT_EQ(tuple, tuple);
119     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
120   }
121 }
122 
TEST_F(SchemeHostPortTest,InvalidConstruction)123 TEST_F(SchemeHostPortTest, InvalidConstruction) {
124   struct TestCases {
125     const char* scheme;
126     const char* host;
127     uint16_t port;
128   } cases[] = {{"", "", 0},
129                {"data", "", 0},
130                {"blob", "", 0},
131                {"filesystem", "", 0},
132                {"http", "", 80},
133                {"data", "example.com", 80},
134                {"http", "☃.net", 80},
135                {"http\nmore", "example.com", 80},
136                {"http\rmore", "example.com", 80},
137                {"http\n", "example.com", 80},
138                {"http\r", "example.com", 80},
139                {"http", "example.com\nnot-example.com", 80},
140                {"http", "example.com\rnot-example.com", 80},
141                {"http", "example.com\n", 80},
142                {"http", "example.com\r", 80},
143                {"file", "", 80}};  // Can''t have a port for file: scheme.
144 
145   for (const auto& test : cases) {
146     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
147                                     << test.port);
148     url::SchemeHostPort tuple(test.scheme, test.host, test.port);
149     EXPECT_EQ("", tuple.scheme());
150     EXPECT_EQ("", tuple.host());
151     EXPECT_EQ(0, tuple.port());
152     EXPECT_FALSE(tuple.IsValid());
153     EXPECT_EQ(tuple, tuple);
154     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
155   }
156 }
157 
TEST_F(SchemeHostPortTest,InvalidConstructionWithEmbeddedNulls)158 TEST_F(SchemeHostPortTest, InvalidConstructionWithEmbeddedNulls) {
159   struct TestCases {
160     const char* scheme;
161     size_t scheme_length;
162     const char* host;
163     size_t host_length;
164     uint16_t port;
165   } cases[] = {{"http\0more", 9, "example.com", 11, 80},
166                {"http\0", 5, "example.com", 11, 80},
167                {"\0http", 5, "example.com", 11, 80},
168                {"http", 4, "example.com\0not-example.com", 27, 80},
169                {"http", 4, "example.com\0", 12, 80},
170                {"http", 4, "\0example.com", 12, 80}};
171 
172   for (const auto& test : cases) {
173     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
174                                     << test.port);
175     url::SchemeHostPort tuple(std::string(test.scheme, test.scheme_length),
176                               std::string(test.host, test.host_length),
177                               test.port);
178     EXPECT_EQ("", tuple.scheme());
179     EXPECT_EQ("", tuple.host());
180     EXPECT_EQ(0, tuple.port());
181     EXPECT_FALSE(tuple.IsValid());
182     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
183   }
184 }
185 
TEST_F(SchemeHostPortTest,GURLConstruction)186 TEST_F(SchemeHostPortTest, GURLConstruction) {
187   struct TestCases {
188     const char* url;
189     const char* scheme;
190     const char* host;
191     uint16_t port;
192   } cases[] = {
193       {"http://192.168.9.1/", "http", "192.168.9.1", 80},
194       {"http://[2001:db8::1]/", "http", "[2001:db8::1]", 80},
195       {"http://☃.net/", "http", "xn--n3h.net", 80},
196       {"http://example.com/", "http", "example.com", 80},
197       {"http://example.com:123/", "http", "example.com", 123},
198       {"https://example.com/", "https", "example.com", 443},
199       {"https://example.com:123/", "https", "example.com", 123},
200       {"file:///etc/passwd", "file", "", 0},
201       {"file://example.com/etc/passwd", "file", "example.com", 0},
202       {"http://u:p@example.com/", "http", "example.com", 80},
203       {"http://u:p@example.com/path", "http", "example.com", 80},
204       {"http://u:p@example.com/path?123", "http", "example.com", 80},
205       {"http://u:p@example.com/path?123#hash", "http", "example.com", 80},
206   };
207 
208   for (const auto& test : cases) {
209     SCOPED_TRACE(test.url);
210     GURL url(test.url);
211     EXPECT_TRUE(url.is_valid());
212     url::SchemeHostPort tuple(url);
213     EXPECT_EQ(test.scheme, tuple.scheme());
214     EXPECT_EQ(test.host, tuple.host());
215     EXPECT_EQ(test.port, tuple.port());
216     EXPECT_TRUE(tuple.IsValid());
217     EXPECT_EQ(tuple, tuple);
218     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
219   }
220 }
221 
TEST_F(SchemeHostPortTest,Serialization)222 TEST_F(SchemeHostPortTest, Serialization) {
223   struct TestCases {
224     const char* url;
225     const char* expected;
226   } cases[] = {
227       {"http://192.168.9.1/", "http://192.168.9.1"},
228       {"http://[2001:db8::1]/", "http://[2001:db8::1]"},
229       {"http://☃.net/", "http://xn--n3h.net"},
230       {"http://example.com/", "http://example.com"},
231       {"http://example.com:123/", "http://example.com:123"},
232       {"https://example.com/", "https://example.com"},
233       {"https://example.com:123/", "https://example.com:123"},
234       {"file:///etc/passwd", "file://"},
235       {"file://example.com/etc/passwd", "file://example.com"},
236       {"https://example.com:0/", "https://example.com:0"},
237   };
238 
239   for (const auto& test : cases) {
240     SCOPED_TRACE(test.url);
241     GURL url(test.url);
242     url::SchemeHostPort tuple(url);
243     EXPECT_EQ(test.expected, tuple.Serialize());
244     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
245   }
246 }
247 
TEST_F(SchemeHostPortTest,Comparison)248 TEST_F(SchemeHostPortTest, Comparison) {
249   // These tuples are arranged in increasing order:
250   struct SchemeHostPorts {
251     const char* scheme;
252     const char* host;
253     uint16_t port;
254   } tuples[] = {
255       {"http", "a", 80},
256       {"http", "b", 80},
257       {"https", "a", 80},
258       {"https", "b", 80},
259       {"http", "a", 81},
260       {"http", "b", 81},
261       {"https", "a", 81},
262       {"https", "b", 81},
263   };
264 
265   for (size_t i = 0; i < std::size(tuples); i++) {
266     url::SchemeHostPort current(tuples[i].scheme, tuples[i].host,
267                                 tuples[i].port);
268     for (size_t j = i; j < std::size(tuples); j++) {
269       url::SchemeHostPort to_compare(tuples[j].scheme, tuples[j].host,
270                                      tuples[j].port);
271       EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
272       EXPECT_EQ(j < i, to_compare < current) << j << " < " << i;
273     }
274   }
275 }
276 
277 // Some schemes have optional authority. Make sure that GURL conversion from
278 // SchemeHostPort is not opinionated in that regard. For more info, See
279 // crbug.com/820194, where we considered all SchemeHostPorts with
280 // SCHEME_WITH_HOST (i.e., without ports) as valid with empty hosts, even though
281 // most are not (e.g. chrome URLs).
TEST_F(SchemeHostPortTest,EmptyHostGurlConversion)282 TEST_F(SchemeHostPortTest, EmptyHostGurlConversion) {
283   url::AddStandardScheme("chrome", url::SCHEME_WITH_HOST);
284 
285   GURL chrome_url("chrome:");
286   EXPECT_FALSE(chrome_url.is_valid());
287 
288   url::SchemeHostPort chrome_tuple("chrome", "", 0);
289   EXPECT_FALSE(chrome_tuple.GetURL().is_valid());
290   ExpectParsedUrlsEqual(GURL(chrome_tuple.Serialize()), chrome_tuple.GetURL());
291   ExpectParsedUrlsEqual(chrome_url, chrome_tuple.GetURL());
292 }
293 
294 }  // namespace url
295