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