2 * Copyright 2016 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include <folly/Uri.h>
18 #include <folly/Benchmark.h>
20 #include <boost/algorithm/string.hpp>
21 #include <glog/logging.h>
22 #include <gtest/gtest.h>
25 using namespace folly;
33 fbstring s("http://www.facebook.com/hello/world?query#fragment");
35 EXPECT_EQ("http", u.scheme());
36 EXPECT_EQ("", u.username());
37 EXPECT_EQ("", u.password());
38 EXPECT_EQ("www.facebook.com", u.host());
39 EXPECT_EQ(0, u.port());
40 EXPECT_EQ("www.facebook.com", u.authority());
41 EXPECT_EQ("/hello/world", u.path());
42 EXPECT_EQ("query", u.query());
43 EXPECT_EQ("fragment", u.fragment());
44 EXPECT_EQ(s, u.fbstr()); // canonical
48 fbstring s("http://www.facebook.com:8080/hello/world?query#fragment");
50 EXPECT_EQ("http", u.scheme());
51 EXPECT_EQ("", u.username());
52 EXPECT_EQ("", u.password());
53 EXPECT_EQ("www.facebook.com", u.host());
54 EXPECT_EQ(8080, u.port());
55 EXPECT_EQ("www.facebook.com:8080", u.authority());
56 EXPECT_EQ("/hello/world", u.path());
57 EXPECT_EQ("query", u.query());
58 EXPECT_EQ("fragment", u.fragment());
59 EXPECT_EQ(s, u.fbstr()); // canonical
63 fbstring s("http://127.0.0.1:8080/hello/world?query#fragment");
65 EXPECT_EQ("http", u.scheme());
66 EXPECT_EQ("", u.username());
67 EXPECT_EQ("", u.password());
68 EXPECT_EQ("127.0.0.1", u.host());
69 EXPECT_EQ(8080, u.port());
70 EXPECT_EQ("127.0.0.1:8080", u.authority());
71 EXPECT_EQ("/hello/world", u.path());
72 EXPECT_EQ("query", u.query());
73 EXPECT_EQ("fragment", u.fragment());
74 EXPECT_EQ(s, u.fbstr()); // canonical
78 fbstring s("http://[::1]:8080/hello/world?query#fragment");
80 EXPECT_EQ("http", u.scheme());
81 EXPECT_EQ("", u.username());
82 EXPECT_EQ("", u.password());
83 EXPECT_EQ("[::1]", u.host());
84 EXPECT_EQ("::1", u.hostname());
85 EXPECT_EQ(8080, u.port());
86 EXPECT_EQ("[::1]:8080", u.authority());
87 EXPECT_EQ("/hello/world", u.path());
88 EXPECT_EQ("query", u.query());
89 EXPECT_EQ("fragment", u.fragment());
90 EXPECT_EQ(s, u.fbstr()); // canonical
94 fbstring s("http://[2401:db00:20:7004:face:0:29:0]:8080/hello/world?query");
96 EXPECT_EQ("http", u.scheme());
97 EXPECT_EQ("", u.username());
98 EXPECT_EQ("", u.password());
99 EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.host());
100 EXPECT_EQ("2401:db00:20:7004:face:0:29:0", u.hostname());
101 EXPECT_EQ(8080, u.port());
102 EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]:8080", u.authority());
103 EXPECT_EQ("/hello/world", u.path());
104 EXPECT_EQ("query", u.query());
105 EXPECT_EQ("", u.fragment());
106 EXPECT_EQ(s, u.fbstr()); // canonical
110 fbstring s("http://[2401:db00:20:7004:face:0:29:0]/hello/world?query");
112 EXPECT_EQ("http", u.scheme());
113 EXPECT_EQ("", u.username());
114 EXPECT_EQ("", u.password());
115 EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.host());
116 EXPECT_EQ("2401:db00:20:7004:face:0:29:0", u.hostname());
117 EXPECT_EQ(0, u.port());
118 EXPECT_EQ("[2401:db00:20:7004:face:0:29:0]", u.authority());
119 EXPECT_EQ("/hello/world", u.path());
120 EXPECT_EQ("query", u.query());
121 EXPECT_EQ("", u.fragment());
122 EXPECT_EQ(s, u.fbstr()); // canonical
126 fbstring s("http://user:pass@host.com/");
128 EXPECT_EQ("http", u.scheme());
129 EXPECT_EQ("user", u.username());
130 EXPECT_EQ("pass", u.password());
131 EXPECT_EQ("host.com", u.host());
132 EXPECT_EQ(0, u.port());
133 EXPECT_EQ("user:pass@host.com", u.authority());
134 EXPECT_EQ("/", u.path());
135 EXPECT_EQ("", u.query());
136 EXPECT_EQ("", u.fragment());
137 EXPECT_EQ(s, u.fbstr());
141 fbstring s("http://user@host.com/");
143 EXPECT_EQ("http", u.scheme());
144 EXPECT_EQ("user", u.username());
145 EXPECT_EQ("", u.password());
146 EXPECT_EQ("host.com", u.host());
147 EXPECT_EQ(0, u.port());
148 EXPECT_EQ("user@host.com", u.authority());
149 EXPECT_EQ("/", u.path());
150 EXPECT_EQ("", u.query());
151 EXPECT_EQ("", u.fragment());
152 EXPECT_EQ(s, u.fbstr());
156 fbstring s("http://user:@host.com/");
158 EXPECT_EQ("http", u.scheme());
159 EXPECT_EQ("user", u.username());
160 EXPECT_EQ("", u.password());
161 EXPECT_EQ("host.com", u.host());
162 EXPECT_EQ(0, u.port());
163 EXPECT_EQ("user@host.com", u.authority());
164 EXPECT_EQ("/", u.path());
165 EXPECT_EQ("", u.query());
166 EXPECT_EQ("", u.fragment());
167 EXPECT_EQ("http://user@host.com/", u.fbstr());
171 fbstring s("http://:pass@host.com/");
173 EXPECT_EQ("http", u.scheme());
174 EXPECT_EQ("", u.username());
175 EXPECT_EQ("pass", u.password());
176 EXPECT_EQ("host.com", u.host());
177 EXPECT_EQ(0, u.port());
178 EXPECT_EQ(":pass@host.com", u.authority());
179 EXPECT_EQ("/", u.path());
180 EXPECT_EQ("", u.query());
181 EXPECT_EQ("", u.fragment());
182 EXPECT_EQ(s, u.fbstr());
186 fbstring s("http://@host.com/");
188 EXPECT_EQ("http", u.scheme());
189 EXPECT_EQ("", u.username());
190 EXPECT_EQ("", u.password());
191 EXPECT_EQ("host.com", u.host());
192 EXPECT_EQ(0, u.port());
193 EXPECT_EQ("host.com", u.authority());
194 EXPECT_EQ("/", u.path());
195 EXPECT_EQ("", u.query());
196 EXPECT_EQ("", u.fragment());
197 EXPECT_EQ("http://host.com/", u.fbstr());
201 fbstring s("http://:@host.com/");
203 EXPECT_EQ("http", u.scheme());
204 EXPECT_EQ("", u.username());
205 EXPECT_EQ("", u.password());
206 EXPECT_EQ("host.com", u.host());
207 EXPECT_EQ(0, u.port());
208 EXPECT_EQ("host.com", u.authority());
209 EXPECT_EQ("/", u.path());
210 EXPECT_EQ("", u.query());
211 EXPECT_EQ("", u.fragment());
212 EXPECT_EQ("http://host.com/", u.fbstr());
216 fbstring s("file:///etc/motd");
218 EXPECT_EQ("file", u.scheme());
219 EXPECT_EQ("", u.username());
220 EXPECT_EQ("", u.password());
221 EXPECT_EQ("", u.host());
222 EXPECT_EQ(0, u.port());
223 EXPECT_EQ("", u.authority());
224 EXPECT_EQ("/etc/motd", u.path());
225 EXPECT_EQ("", u.query());
226 EXPECT_EQ("", u.fragment());
227 EXPECT_EQ(s, u.fbstr());
231 fbstring s("file:/etc/motd");
233 EXPECT_EQ("file", u.scheme());
234 EXPECT_EQ("", u.username());
235 EXPECT_EQ("", u.password());
236 EXPECT_EQ("", u.host());
237 EXPECT_EQ(0, u.port());
238 EXPECT_EQ("", u.authority());
239 EXPECT_EQ("/etc/motd", u.path());
240 EXPECT_EQ("", u.query());
241 EXPECT_EQ("", u.fragment());
242 EXPECT_EQ("file:/etc/motd", u.fbstr());
246 fbstring s("file://etc/motd");
248 EXPECT_EQ("file", u.scheme());
249 EXPECT_EQ("", u.username());
250 EXPECT_EQ("", u.password());
251 EXPECT_EQ("etc", u.host());
252 EXPECT_EQ(0, u.port());
253 EXPECT_EQ("etc", u.authority());
254 EXPECT_EQ("/motd", u.path());
255 EXPECT_EQ("", u.query());
256 EXPECT_EQ("", u.fragment());
257 EXPECT_EQ(s, u.fbstr());
261 // test query parameters
262 fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
264 auto paramsList = u.getQueryParams();
265 std::map<fbstring, fbstring> params;
266 for (auto& param : paramsList) {
267 params[param.first] = param.second;
269 EXPECT_EQ(3, params.size());
270 EXPECT_EQ("foo", params["key1"]);
271 EXPECT_NE(params.end(), params.find("key2"));
272 EXPECT_EQ("", params["key2"]);
273 EXPECT_NE(params.end(), params.find("key3"));
274 EXPECT_EQ("", params["key3"]);
278 // test query parameters
279 fbstring s("http://localhost?&&&&&&&&&&&&&&&");
281 auto params = u.getQueryParams();
282 EXPECT_TRUE(params.empty());
286 // test query parameters
287 fbstring s("http://localhost?&=invalid_key&key2&key3=foo");
289 auto paramsList = u.getQueryParams();
290 std::map<fbstring, fbstring> params;
291 for (auto& param : paramsList) {
292 params[param.first] = param.second;
294 EXPECT_EQ(2, params.size());
295 EXPECT_NE(params.end(), params.find("key2"));
296 EXPECT_EQ("", params["key2"]);
297 EXPECT_EQ("foo", params["key3"]);
301 // test query parameters
302 fbstring s("http://localhost?&key1=====&&=key2&key3=");
304 auto paramsList = u.getQueryParams();
305 std::map<fbstring, fbstring> params;
306 for (auto& param : paramsList) {
307 params[param.first] = param.second;
309 EXPECT_EQ(1, params.size());
310 EXPECT_NE(params.end(), params.find("key3"));
311 EXPECT_EQ("", params["key3"]);
315 // test query parameters
316 fbstring s("http://localhost?key1=foo=bar&key2=foobar&");
318 auto paramsList = u.getQueryParams();
319 std::map<fbstring, fbstring> params;
320 for (auto& param : paramsList) {
321 params[param.first] = param.second;
323 EXPECT_EQ(1, params.size());
324 EXPECT_EQ("foobar", params["key2"]);
328 fbstring s("2http://www.facebook.com");
332 CHECK(false) << "Control should not have reached here";
333 } catch (const std::invalid_argument& ex) {
334 EXPECT_TRUE(boost::algorithm::ends_with(ex.what(), s));
339 fbstring s("www[facebook]com");
342 Uri u("http://" + s);
343 CHECK(false) << "Control should not have reached here";
344 } catch (const std::invalid_argument& ex) {
345 EXPECT_TRUE(boost::algorithm::ends_with(ex.what(), s));
350 fbstring s("http://[::1:8080/hello/world?query#fragment");
354 CHECK(false) << "Control should not have reached here";
355 } catch (const std::invalid_argument& ex) {
361 fbstring s("http://::1]:8080/hello/world?query#fragment");
365 CHECK(false) << "Control should not have reached here";
366 } catch (const std::invalid_argument& ex) {
372 fbstring s("http://::1:8080/hello/world?query#fragment");
376 CHECK(false) << "Control should not have reached here";
377 } catch (const std::invalid_argument& ex) {
383 fbstring s("http://2401:db00:20:7004:face:0:29:0/hello/world?query");
387 CHECK(false) << "Control should not have reached here";
388 } catch (const std::invalid_argument& ex) {
393 // No authority (no "//") is valid
395 fbstring s("this:is/a/valid/uri");
397 EXPECT_EQ("this", u.scheme());
398 EXPECT_EQ("is/a/valid/uri", u.path());
399 EXPECT_EQ(s, u.fbstr());
402 fbstring s("this:is:another:valid:uri");
404 EXPECT_EQ("this", u.scheme());
405 EXPECT_EQ("is:another:valid:uri", u.path());
406 EXPECT_EQ(s, u.fbstr());
409 fbstring s("this:is@another:valid:uri");
411 EXPECT_EQ("this", u.scheme());
412 EXPECT_EQ("is@another:valid:uri", u.path());
413 EXPECT_EQ(s, u.fbstr());
418 * Result of benchmark varies by the complexity of query.
419 * ============================================================================
420 * folly/test/UriTest.cpp relative time/iter iters/s
421 * ============================================================================
422 * init_uri_simple 4.88us 204.80K
423 * init_uri_simple_with_query_parsing 22.46us 44.52K
424 * init_uri_complex 5.92us 168.85K
425 * init_uri_complex_with_query_parsing 48.70us 20.53K
426 * ============================================================================
428 BENCHMARK(init_uri_simple, iters) {
429 const fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
430 for (size_t i = 0; i < iters; ++i) {
435 BENCHMARK(init_uri_simple_with_query_parsing, iters) {
436 const fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
437 for (size_t i = 0; i < iters; ++i) {
443 BENCHMARK(init_uri_complex, iters) {
445 "https://mock.example.com/farm/track.php?TmOxQUDF=uSmTS_VwhjKnh_JME&DI"
446 "h=fbbN&GRsoIm=bGshjaUqavZxQai&UMT=36k18N4dn21&3U=CD8o4A4497W152j6m0V%14"
447 "%57&Hy=t%05mpr.80JUZ7ne_%23zS8DcA%0qc_%291ymamz096%11Zfb3r%09ZqPD%311ZX"
448 "tqJd600ot&5U96U-Rh-VZ=-D_6-9xKYj%1gW6b43s1B9-j21P0oUW5-t46G4kgt&ezgj=mcW"
449 "TTQ.c&Oh=%2PblUfuC%7C997048884827569%03xnyJ%2L1pi7irBioQ6D4r7nNHNdo6v7Y%"
450 "84aurnSJ%2wCFePHMlGZmIHGfCe7392_lImWsSvN&sBeNN=Nf%80yOE%6X10M64F4gG197aX"
451 "R2B4g2533x235A0i4e%57%58uWB%04Erw.60&VMS4=Ek_%02GC0Pkx%6Ov_%207WICUz007%"
452 "04nYX8N%46zzpv%999h&KGmBt988y=q4P57C-Dh-Nz-x_7-5oPxz%1gz3N03t6c7-R67N4DT"
453 "Y6-f98W1&Lts&%02dOty%8eEYEnLz4yexQQLnL4MGU2JFn3OcmXcatBcabZgBdDdy67hdgW"
455 for (size_t i = 0; i < iters; ++i) {
460 BENCHMARK(init_uri_complex_with_query_parsing, iters) {
462 "https://mock.example.com/farm/track.php?TmOxQUDF=uSmTS_VwhjKnh_JME&DI"
463 "h=fbbN&GRsoIm=bGshjaUqavZxQai&UMT=36k18N4dn21&3U=CD8o4A4497W152j6m0V%14"
464 "%57&Hy=t%05mpr.80JUZ7ne_%23zS8DcA%0qc_%291ymamz096%11Zfb3r%09ZqPD%311ZX"
465 "tqJd600ot&5U96U-Rh-VZ=-D_6-9xKYj%1gW6b43s1B9-j21P0oUW5-t46G4kgt&ezgj=mcW"
466 "TTQ.c&Oh=%2PblUfuC%7C997048884827569%03xnyJ%2L1pi7irBioQ6D4r7nNHNdo6v7Y%"
467 "84aurnSJ%2wCFePHMlGZmIHGfCe7392_lImWsSvN&sBeNN=Nf%80yOE%6X10M64F4gG197aX"
468 "R2B4g2533x235A0i4e%57%58uWB%04Erw.60&VMS4=Ek_%02GC0Pkx%6Ov_%207WICUz007%"
469 "04nYX8N%46zzpv%999h&KGmBt988y=q4P57C-Dh-Nz-x_7-5oPxz%1gz3N03t6c7-R67N4DT"
470 "Y6-f98W1&Lts&%02dOty%8eEYEnLz4yexQQLnL4MGU2JFn3OcmXcatBcabZgBdDdy67hdgW"
472 for (size_t i = 0; i < iters; ++i) {
478 int main(int argc, char** argv) {
479 testing::InitGoogleTest(&argc, argv);
480 auto r = RUN_ALL_TESTS();