Copyright 2014->2015
[folly.git] / folly / test / UriTest.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
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
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include <folly/Uri.h>
18 #include <folly/Benchmark.h>
19
20 #include <boost/algorithm/string.hpp>
21 #include <glog/logging.h>
22 #include <gtest/gtest.h>
23 #include <map>
24
25 using namespace folly;
26
27 namespace {
28
29 }  // namespace
30
31 TEST(Uri, Simple) {
32   {
33     fbstring s("http://www.facebook.com/hello/world?query#fragment");
34     Uri u(s);
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
45   }
46
47   {
48     fbstring s("http://www.facebook.com:8080/hello/world?query#fragment");
49     Uri u(s);
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
60   }
61
62   {
63     fbstring s("http://127.0.0.1:8080/hello/world?query#fragment");
64     Uri u(s);
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
75   }
76
77   {
78     fbstring s("http://[::1]:8080/hello/world?query#fragment");
79     Uri u(s);
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
91   }
92
93   {
94     fbstring s("http://[2401:db00:20:7004:face:0:29:0]:8080/hello/world?query");
95     Uri u(s);
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
107   }
108
109   {
110     fbstring s("http://[2401:db00:20:7004:face:0:29:0]/hello/world?query");
111     Uri u(s);
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
123   }
124
125   {
126     fbstring s("http://user:pass@host.com/");
127     Uri u(s);
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());
138   }
139
140   {
141     fbstring s("http://user@host.com/");
142     Uri u(s);
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());
153   }
154
155   {
156     fbstring s("http://user:@host.com/");
157     Uri u(s);
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());
168   }
169
170   {
171     fbstring s("http://:pass@host.com/");
172     Uri u(s);
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());
183   }
184
185   {
186     fbstring s("http://@host.com/");
187     Uri u(s);
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());
198   }
199
200   {
201     fbstring s("http://:@host.com/");
202     Uri u(s);
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());
213   }
214
215   {
216     fbstring s("file:///etc/motd");
217     Uri u(s);
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());
228   }
229
230   {
231     fbstring s("file:/etc/motd");
232     Uri u(s);
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());
243   }
244
245   {
246     fbstring s("file://etc/motd");
247     Uri u(s);
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());
258   }
259
260   {
261     // test query parameters
262     fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
263     Uri u(s);
264     auto paramsList = u.getQueryParams();
265     std::map<fbstring, fbstring> params;
266     for (auto& param : paramsList) {
267       params[param.first] = param.second;
268     }
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"]);
275   }
276
277   {
278     // test query parameters
279     fbstring s("http://localhost?&&&&&&&&&&&&&&&");
280     Uri u(s);
281     auto params = u.getQueryParams();
282     EXPECT_TRUE(params.empty());
283   }
284
285   {
286     // test query parameters
287     fbstring s("http://localhost?&=invalid_key&key2&key3=foo");
288     Uri u(s);
289     auto paramsList = u.getQueryParams();
290     std::map<fbstring, fbstring> params;
291     for (auto& param : paramsList) {
292       params[param.first] = param.second;
293     }
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"]);
298   }
299
300   {
301     // test query parameters
302     fbstring s("http://localhost?&key1=====&&=key2&key3=");
303     Uri u(s);
304     auto paramsList = u.getQueryParams();
305     std::map<fbstring, fbstring> params;
306     for (auto& param : paramsList) {
307       params[param.first] = param.second;
308     }
309     EXPECT_EQ(1, params.size());
310     EXPECT_NE(params.end(), params.find("key3"));
311     EXPECT_EQ("", params["key3"]);
312   }
313
314   {
315     // test query parameters
316     fbstring s("http://localhost?key1=foo=bar&key2=foobar&");
317     Uri u(s);
318     auto paramsList = u.getQueryParams();
319     std::map<fbstring, fbstring> params;
320     for (auto& param : paramsList) {
321       params[param.first] = param.second;
322     }
323     EXPECT_EQ(1, params.size());
324     EXPECT_EQ("foobar", params["key2"]);
325   }
326
327   {
328     fbstring s("2http://www.facebook.com");
329
330     try {
331       Uri u(s);
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));
335     }
336   }
337
338   {
339     fbstring s("www[facebook]com");
340
341     try {
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));
346     }
347   }
348
349   {
350     fbstring s("http://[::1:8080/hello/world?query#fragment");
351
352     try {
353       Uri u(s);
354       CHECK(false) << "Control should not have reached here";
355     } catch (const std::invalid_argument& ex) {
356       // success
357     }
358   }
359
360   {
361     fbstring s("http://::1]:8080/hello/world?query#fragment");
362
363     try {
364       Uri u(s);
365       CHECK(false) << "Control should not have reached here";
366     } catch (const std::invalid_argument& ex) {
367       // success
368     }
369   }
370
371   {
372     fbstring s("http://::1:8080/hello/world?query#fragment");
373
374     try {
375       Uri u(s);
376       CHECK(false) << "Control should not have reached here";
377     } catch (const std::invalid_argument& ex) {
378       // success
379     }
380   }
381
382   {
383     fbstring s("http://2401:db00:20:7004:face:0:29:0/hello/world?query");
384
385     try {
386       Uri u(s);
387       CHECK(false) << "Control should not have reached here";
388     } catch (const std::invalid_argument& ex) {
389       // success
390     }
391   }
392 }
393
394 /**
395  * Result of benchmark varies by the complexity of query.
396  * ============================================================================
397  * folly/test/UriTest.cpp                          relative  time/iter  iters/s
398  * ============================================================================
399  * init_uri_simple                                              4.88us  204.80K
400  * init_uri_simple_with_query_parsing                          22.46us   44.52K
401  * init_uri_complex                                             5.92us  168.85K
402  * init_uri_complex_with_query_parsing                         48.70us   20.53K
403  * ============================================================================
404  */
405 BENCHMARK(init_uri_simple, iters) {
406   const fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
407   for (size_t i = 0; i < iters; ++i) {
408     Uri u(s);
409   }
410 }
411
412 BENCHMARK(init_uri_simple_with_query_parsing, iters) {
413   const fbstring s("http://localhost?&key1=foo&key2=&key3&=bar&=bar=&");
414   for (size_t i = 0; i < iters; ++i) {
415     Uri u(s);
416     u.getQueryParams();
417   }
418 }
419
420 BENCHMARK(init_uri_complex, iters) {
421   const fbstring s(
422       "https://mock.example.com/farm/track.php?TmOxQUDF=uSmTS_VwhjKnh_JME&DI"
423       "h=fbbN&GRsoIm=bGshjaUqavZxQai&UMT=36k18N4dn21&3U=CD8o4A4497W152j6m0V%14"
424       "%57&Hy=t%05mpr.80JUZ7ne_%23zS8DcA%0qc_%291ymamz096%11Zfb3r%09ZqPD%311ZX"
425       "tqJd600ot&5U96U-Rh-VZ=-D_6-9xKYj%1gW6b43s1B9-j21P0oUW5-t46G4kgt&ezgj=mcW"
426       "TTQ.c&Oh=%2PblUfuC%7C997048884827569%03xnyJ%2L1pi7irBioQ6D4r7nNHNdo6v7Y%"
427       "84aurnSJ%2wCFePHMlGZmIHGfCe7392_lImWsSvN&sBeNN=Nf%80yOE%6X10M64F4gG197aX"
428       "R2B4g2533x235A0i4e%57%58uWB%04Erw.60&VMS4=Ek_%02GC0Pkx%6Ov_%207WICUz007%"
429       "04nYX8N%46zzpv%999h&KGmBt988y=q4P57C-Dh-Nz-x_7-5oPxz%1gz3N03t6c7-R67N4DT"
430       "Y6-f98W1&Lts&%02dOty%8eEYEnLz4yexQQLnL4MGU2JFn3OcmXcatBcabZgBdDdy67hdgW"
431       "tYn4");
432   for (size_t i = 0; i < iters; ++i) {
433     Uri u(s);
434   }
435 }
436
437 BENCHMARK(init_uri_complex_with_query_parsing, iters) {
438   const fbstring s(
439       "https://mock.example.com/farm/track.php?TmOxQUDF=uSmTS_VwhjKnh_JME&DI"
440       "h=fbbN&GRsoIm=bGshjaUqavZxQai&UMT=36k18N4dn21&3U=CD8o4A4497W152j6m0V%14"
441       "%57&Hy=t%05mpr.80JUZ7ne_%23zS8DcA%0qc_%291ymamz096%11Zfb3r%09ZqPD%311ZX"
442       "tqJd600ot&5U96U-Rh-VZ=-D_6-9xKYj%1gW6b43s1B9-j21P0oUW5-t46G4kgt&ezgj=mcW"
443       "TTQ.c&Oh=%2PblUfuC%7C997048884827569%03xnyJ%2L1pi7irBioQ6D4r7nNHNdo6v7Y%"
444       "84aurnSJ%2wCFePHMlGZmIHGfCe7392_lImWsSvN&sBeNN=Nf%80yOE%6X10M64F4gG197aX"
445       "R2B4g2533x235A0i4e%57%58uWB%04Erw.60&VMS4=Ek_%02GC0Pkx%6Ov_%207WICUz007%"
446       "04nYX8N%46zzpv%999h&KGmBt988y=q4P57C-Dh-Nz-x_7-5oPxz%1gz3N03t6c7-R67N4DT"
447       "Y6-f98W1&Lts&%02dOty%8eEYEnLz4yexQQLnL4MGU2JFn3OcmXcatBcabZgBdDdy67hdgW"
448       "tYn4");
449   for (size_t i = 0; i < iters; ++i) {
450     Uri u(s);
451     u.getQueryParams();
452   }
453 }
454
455 int main(int argc, char** argv) {
456   testing::InitGoogleTest(&argc, argv);
457   auto r = RUN_ALL_TESTS();
458   if (r) {
459     return r;
460   }
461   runBenchmarks();
462   return 0;
463 }