xref: /aosp_15_r20/external/cronet/url/scheme_host_port_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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       // Non-special URLs which don't have an opaque path.
67       "git:/", "git://", "git:///", "git://host/", "git://host/path",
68 
69       // GURLs where GURL::is_valid returns false translate into an invalid
70       // SchemeHostPort.
71       "file://example.com:443/etc/passwd", "#!^%!$!&*",
72 
73       // These schemes do not follow the generic URL syntax, so make sure we
74       // treat them as invalid (scheme, host, port) tuples (even though such
75       // URLs' _Origin_ might have a (scheme, host, port) tuple, they themselves
76       // do not). This is only *implicitly* checked in the code, by means of
77       // blob schemes not being standard, and filesystem schemes having type
78       // SCHEME_WITHOUT_AUTHORITY. If conditions change such that the implicit
79       // checks no longer hold, this policy should be made explicit.
80       "blob:https://example.com/uuid-goes-here",
81       "filesystem:https://example.com/temporary/yay.png"};
82 
83   for (auto* test : urls) {
84     SCOPED_TRACE(test);
85     GURL url(test);
86     url::SchemeHostPort tuple(url);
87     EXPECT_EQ("", tuple.scheme());
88     EXPECT_EQ("", tuple.host());
89     EXPECT_EQ(0, tuple.port());
90     EXPECT_FALSE(tuple.IsValid());
91     EXPECT_EQ(tuple, tuple);
92     EXPECT_EQ(tuple, invalid);
93     EXPECT_EQ(invalid, tuple);
94     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
95   }
96 }
97 
TEST_F(SchemeHostPortTest,ExplicitConstruction)98 TEST_F(SchemeHostPortTest, ExplicitConstruction) {
99   struct TestCases {
100     const char* scheme;
101     const char* host;
102     uint16_t port;
103   } cases[] = {
104       {"http", "example.com", 80},
105       {"http", "example.com", 123},
106       {"http", "example.com", 0},  // 0 is a valid port for http.
107       {"https", "example.com", 443},
108       {"https", "example.com", 123},
109       {"file", "", 0},  // 0 indicates "no port" for file: scheme.
110       {"file", "example.com", 0},
111   };
112 
113   for (const auto& test : cases) {
114     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
115                                     << test.port);
116     url::SchemeHostPort tuple(test.scheme, test.host, test.port);
117     EXPECT_EQ(test.scheme, tuple.scheme());
118     EXPECT_EQ(test.host, tuple.host());
119     EXPECT_EQ(test.port, tuple.port());
120     EXPECT_TRUE(tuple.IsValid());
121     EXPECT_EQ(tuple, tuple);
122     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
123   }
124 }
125 
TEST_F(SchemeHostPortTest,InvalidConstruction)126 TEST_F(SchemeHostPortTest, InvalidConstruction) {
127   struct TestCases {
128     const char* scheme;
129     const char* host;
130     uint16_t port;
131   } cases[] = {{"", "", 0},
132                {"data", "", 0},
133                {"blob", "", 0},
134                {"filesystem", "", 0},
135                {"http", "", 80},
136                {"data", "example.com", 80},
137                {"git", "", 0},
138                {"git", "example.com", 80},
139                {"http", "☃.net", 80},
140                {"http\nmore", "example.com", 80},
141                {"http\rmore", "example.com", 80},
142                {"http\n", "example.com", 80},
143                {"http\r", "example.com", 80},
144                {"http", "example.com\nnot-example.com", 80},
145                {"http", "example.com\rnot-example.com", 80},
146                {"http", "example.com\n", 80},
147                {"http", "example.com\r", 80},
148                {"file", "", 80}};  // Can''t have a port for file: scheme.
149 
150   for (const auto& test : cases) {
151     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
152                                     << test.port);
153     url::SchemeHostPort tuple(test.scheme, test.host, test.port);
154     EXPECT_EQ("", tuple.scheme());
155     EXPECT_EQ("", tuple.host());
156     EXPECT_EQ(0, tuple.port());
157     EXPECT_FALSE(tuple.IsValid());
158     EXPECT_EQ(tuple, tuple);
159     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
160   }
161 }
162 
TEST_F(SchemeHostPortTest,InvalidConstructionWithEmbeddedNulls)163 TEST_F(SchemeHostPortTest, InvalidConstructionWithEmbeddedNulls) {
164   struct TestCases {
165     const char* scheme;
166     size_t scheme_length;
167     const char* host;
168     size_t host_length;
169     uint16_t port;
170   } cases[] = {{"http\0more", 9, "example.com", 11, 80},
171                {"http\0", 5, "example.com", 11, 80},
172                {"\0http", 5, "example.com", 11, 80},
173                {"http", 4, "example.com\0not-example.com", 27, 80},
174                {"http", 4, "example.com\0", 12, 80},
175                {"http", 4, "\0example.com", 12, 80}};
176 
177   for (const auto& test : cases) {
178     SCOPED_TRACE(testing::Message() << test.scheme << "://" << test.host << ":"
179                                     << test.port);
180     url::SchemeHostPort tuple(std::string(test.scheme, test.scheme_length),
181                               std::string(test.host, test.host_length),
182                               test.port);
183     EXPECT_EQ("", tuple.scheme());
184     EXPECT_EQ("", tuple.host());
185     EXPECT_EQ(0, tuple.port());
186     EXPECT_FALSE(tuple.IsValid());
187     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
188   }
189 }
190 
TEST_F(SchemeHostPortTest,GURLConstruction)191 TEST_F(SchemeHostPortTest, GURLConstruction) {
192   struct TestCases {
193     const char* url;
194     const char* scheme;
195     const char* host;
196     uint16_t port;
197   } cases[] = {
198       {"http://192.168.9.1/", "http", "192.168.9.1", 80},
199       {"http://[2001:db8::1]/", "http", "[2001:db8::1]", 80},
200       {"http://☃.net/", "http", "xn--n3h.net", 80},
201       {"http://example.com/", "http", "example.com", 80},
202       {"http://example.com:123/", "http", "example.com", 123},
203       {"https://example.com/", "https", "example.com", 443},
204       {"https://example.com:123/", "https", "example.com", 123},
205       {"file:///etc/passwd", "file", "", 0},
206       {"file://example.com/etc/passwd", "file", "example.com", 0},
207       {"http://u:[email protected]/", "http", "example.com", 80},
208       {"http://u:[email protected]/path", "http", "example.com", 80},
209       {"http://u:[email protected]/path?123", "http", "example.com", 80},
210       {"http://u:[email protected]/path?123#hash", "http", "example.com", 80},
211   };
212 
213   for (const auto& test : cases) {
214     SCOPED_TRACE(test.url);
215     GURL url(test.url);
216     EXPECT_TRUE(url.is_valid());
217     url::SchemeHostPort tuple(url);
218     EXPECT_EQ(test.scheme, tuple.scheme());
219     EXPECT_EQ(test.host, tuple.host());
220     EXPECT_EQ(test.port, tuple.port());
221     EXPECT_TRUE(tuple.IsValid());
222     EXPECT_EQ(tuple, tuple);
223     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
224   }
225 }
226 
TEST_F(SchemeHostPortTest,Serialization)227 TEST_F(SchemeHostPortTest, Serialization) {
228   struct TestCases {
229     const char* url;
230     const char* expected;
231   } cases[] = {
232       {"http://192.168.9.1/", "http://192.168.9.1"},
233       {"http://[2001:db8::1]/", "http://[2001:db8::1]"},
234       {"http://☃.net/", "http://xn--n3h.net"},
235       {"http://example.com/", "http://example.com"},
236       {"http://example.com:123/", "http://example.com:123"},
237       {"https://example.com/", "https://example.com"},
238       {"https://example.com:123/", "https://example.com:123"},
239       {"file:///etc/passwd", "file://"},
240       {"file://example.com/etc/passwd", "file://example.com"},
241       {"https://example.com:0/", "https://example.com:0"},
242   };
243 
244   for (const auto& test : cases) {
245     SCOPED_TRACE(test.url);
246     GURL url(test.url);
247     url::SchemeHostPort tuple(url);
248     EXPECT_EQ(test.expected, tuple.Serialize());
249     ExpectParsedUrlsEqual(GURL(tuple.Serialize()), tuple.GetURL());
250   }
251 }
252 
TEST_F(SchemeHostPortTest,Comparison)253 TEST_F(SchemeHostPortTest, Comparison) {
254   // These tuples are arranged in increasing order:
255   struct SchemeHostPorts {
256     const char* scheme;
257     const char* host;
258     uint16_t port;
259   } tuples[] = {
260       {"http", "a", 80},
261       {"http", "b", 80},
262       {"https", "a", 80},
263       {"https", "b", 80},
264       {"http", "a", 81},
265       {"http", "b", 81},
266       {"https", "a", 81},
267       {"https", "b", 81},
268   };
269 
270   for (size_t i = 0; i < std::size(tuples); i++) {
271     url::SchemeHostPort current(tuples[i].scheme, tuples[i].host,
272                                 tuples[i].port);
273     for (size_t j = i; j < std::size(tuples); j++) {
274       url::SchemeHostPort to_compare(tuples[j].scheme, tuples[j].host,
275                                      tuples[j].port);
276       EXPECT_EQ(i < j, current < to_compare) << i << " < " << j;
277       EXPECT_EQ(j < i, to_compare < current) << j << " < " << i;
278     }
279   }
280 }
281 
282 // Some schemes have optional authority. Make sure that GURL conversion from
283 // SchemeHostPort is not opinionated in that regard. For more info, See
284 // crbug.com/820194, where we considered all SchemeHostPorts with
285 // SCHEME_WITH_HOST (i.e., without ports) as valid with empty hosts, even though
286 // most are not (e.g. chrome URLs).
TEST_F(SchemeHostPortTest,EmptyHostGurlConversion)287 TEST_F(SchemeHostPortTest, EmptyHostGurlConversion) {
288   url::AddStandardScheme("chrome", url::SCHEME_WITH_HOST);
289 
290   GURL chrome_url("chrome:");
291   EXPECT_FALSE(chrome_url.is_valid());
292 
293   url::SchemeHostPort chrome_tuple("chrome", "", 0);
294   EXPECT_FALSE(chrome_tuple.GetURL().is_valid());
295   ExpectParsedUrlsEqual(GURL(chrome_tuple.Serialize()), chrome_tuple.GetURL());
296   ExpectParsedUrlsEqual(chrome_url, chrome_tuple.GetURL());
297 }
298 
299 }  // namespace url
300