1 /***
2  * Copyright (C) Microsoft. All rights reserved.
3  * Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
4  *
5  * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
6  *
7  * Tests for the URI builder class.
8  *
9  * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
10  ****/
11 
12 #include "stdafx.h"
13 
14 #include <locale_guard.h>
15 
16 using namespace web;
17 using namespace utility;
18 
19 namespace tests
20 {
21 namespace functional
22 {
23 namespace uri_tests
24 {
25 // Helper functions to verify components of a builder.
VERIFY_URI_BUILDER(uri_builder & builder,const utility::string_t & scheme,const utility::string_t & user_info,const utility::string_t & host,const int port,const utility::string_t & path,const utility::string_t & query,const utility::string_t & fragment)26 static void VERIFY_URI_BUILDER(uri_builder& builder,
27                                const utility::string_t& scheme,
28                                const utility::string_t& user_info,
29                                const utility::string_t& host,
30                                const int port,
31                                const utility::string_t& path,
32                                const utility::string_t& query,
33                                const utility::string_t& fragment)
34 {
35     VERIFY_ARE_EQUAL(scheme, builder.scheme());
36     VERIFY_ARE_EQUAL(host, builder.host());
37     VERIFY_ARE_EQUAL(user_info, builder.user_info());
38     VERIFY_ARE_EQUAL(port, builder.port());
39     VERIFY_ARE_EQUAL(path, builder.path());
40     VERIFY_ARE_EQUAL(query, builder.query());
41     VERIFY_ARE_EQUAL(fragment, builder.fragment());
42 }
VERIFY_URI_BUILDER(uri_builder & builder,const utility::string_t & scheme,const utility::string_t & host,const int port)43 static void VERIFY_URI_BUILDER(uri_builder& builder,
44                                const utility::string_t& scheme,
45                                const utility::string_t& host,
46                                const int port)
47 {
48     VERIFY_URI_BUILDER(builder,
49                        scheme,
50                        utility::string_t(),
51                        host,
52                        port,
53                        utility::string_t(U("/")),
54                        utility::string_t(),
55                        utility::string_t());
56 }
VERIFY_URI_BUILDER_IS_EMPTY(uri_builder & builder)57 static void VERIFY_URI_BUILDER_IS_EMPTY(uri_builder& builder)
58 {
59     VERIFY_URI_BUILDER(builder,
60                        utility::string_t(),
61                        utility::string_t(),
62                        utility::string_t(),
63                        -1,
64                        utility::string_t(U("/")),
65                        utility::string_t(),
66                        utility::string_t());
67 }
68 
SUITE(uri_builder_tests)69 SUITE(uri_builder_tests)
70 {
71     TEST(constructor_tests)
72     {
73         // Default constructor
74         uri_builder builder;
75         VERIFY_URI_BUILDER_IS_EMPTY(builder);
76         // scheme, user_info, host, port
77         utility::string_t scheme(U("ftp"));
78         utility::string_t user_info(U("steve:pass"));
79         utility::string_t host(U("localhost"));
80         int port = 44;
81         utility::string_t path(U("/Yeshere888"));
82         utility::string_t uri_str(U("ftp://steve:pass@localhost:44/Yeshere888"));
83 
84         // utility::string_t
85         utility::string_t uri_wstr(U("ftp://steve:pass@localhost:44/Yeshere888?abc:123&abc2:456#nose"));
86         builder = uri_builder(uri_wstr);
87         VERIFY_URI_BUILDER(builder,
88                            scheme,
89                            user_info,
90                            host,
91                            port,
92                            path,
93                            utility::string_t(U("abc:123&abc2:456")),
94                            utility::string_t(U("nose")));
95 
96         // copy constructor
97         uri_builder other(builder);
98         builder = uri_builder(uri_str);
99         VERIFY_URI_BUILDER(other,
100                            scheme,
101                            user_info,
102                            host,
103                            port,
104                            path,
105                            utility::string_t(U("abc:123&abc2:456")),
106                            utility::string_t(U("nose")));
107         VERIFY_URI_BUILDER(builder, scheme, user_info, host, port, path, U(""), U(""));
108 
109         // move constructor
110         uri_builder move_other = std::move(builder);
111         VERIFY_URI_BUILDER(move_other, scheme, user_info, host, port, path, U(""), U(""));
112     }
113 
114     TEST(assignment_operators)
115     {
116         // assignment operator
117         const utility::string_t scheme = U("http"), host = U("localhost");
118         const int port = 44;
119         uri_builder original;
120         original.set_scheme(scheme).set_host(host).set_port(port);
121         uri_builder assign;
122         assign = original;
123         VERIFY_URI_BUILDER(assign, scheme, utility::string_t(host), port);
124 
125         // move assignment operator
126         uri_builder move_assign;
127         move_assign = std::move(original);
128         VERIFY_URI_BUILDER(assign, scheme, utility::string_t(host), port);
129     }
130 
131     TEST(set_port_as_string)
132     {
133         uri_builder builder;
134 
135         VERIFY_THROWS(builder.set_port(U("")), std::invalid_argument);
136         VERIFY_ARE_EQUAL(-1, builder.port());
137 
138         builder.set_port(U("987"));
139         VERIFY_ARE_EQUAL(987, builder.port());
140 
141         VERIFY_THROWS(builder.set_port(U("abc")), std::invalid_argument);
142         VERIFY_ARE_EQUAL(987, builder.port());
143 
144         builder.set_port(U(" 44 "));
145         VERIFY_ARE_EQUAL(44, builder.port());
146 
147         builder.set_port(U("99"));
148         VERIFY_ARE_EQUAL(99, builder.port());
149     }
150 
151     TEST(component_assignment)
152     {
153         uri_builder builder;
154         const utility::string_t scheme(U("myscheme"));
155         const utility::string_t uinfo(U("johndoe:test"));
156         const utility::string_t host(U("localhost"));
157         const int port = 88;
158         const utility::string_t path(U("jklajsd"));
159         const utility::string_t query(U("key1=val1"));
160         const utility::string_t fragment(U("last"));
161 
162         builder.set_scheme(scheme);
163         builder.set_user_info(uinfo);
164         builder.set_host(host);
165         builder.set_port(port);
166         builder.set_path(path);
167         builder.set_query(query);
168         builder.set_fragment(fragment);
169 
170         VERIFY_URI_BUILDER(builder, scheme, uinfo, host, port, path, query, fragment);
171     }
172 
173     TEST(component_assignment_encode)
174     {
175         {
176             uri_builder builder;
177             const utility::string_t scheme(U("myscheme"));
178             const utility::string_t uinfo(U("johndoe:test"));
179             const utility::string_t host(U("localhost"));
180             const int port = 88;
181             const utility::string_t path(U("jklajsd/yes no"));
182             const utility::string_t query(U("key1=va%l1"));
183             const utility::string_t fragment(U("las t"));
184 
185             builder.set_scheme(scheme);
186             builder.set_user_info(uinfo, true);
187             builder.set_host(host, true);
188             builder.set_port(port);
189             builder.set_path(path, true);
190             builder.set_query(query, true);
191             builder.set_fragment(fragment, true);
192 
193             VERIFY_URI_BUILDER(builder,
194                                scheme,
195                                utility::string_t(U("johndoe:test")),
196                                utility::string_t(U("localhost")),
197                                port,
198                                utility::string_t(U("jklajsd/yes%20no")),
199                                utility::string_t(U("key1=va%25l1")),
200                                utility::string_t(U("las%20t")));
201         }
202         {
203             uri_builder builder;
204             const utility::string_t scheme(U("myscheme"));
205             const utility::string_t uinfo(U("johndoe:test"));
206             const utility::string_t host(U("localhost"));
207             const int port = 88;
208             const utility::string_t path(U("jklajsd/yes no"));
209             const utility::string_t query(U("key1=va%l1"));
210             const utility::string_t fragment(U("las t"));
211 
212             builder.set_scheme(scheme);
213             builder.set_user_info(uinfo, true);
214             builder.set_host(host, true);
215             builder.set_port(port);
216             builder.set_path(path, true);
217             builder.set_query(query, true);
218             builder.set_fragment(fragment, true);
219 
220             VERIFY_URI_BUILDER(builder,
221                                scheme,
222                                utility::string_t(U("johndoe:test")),
223                                utility::string_t(U("localhost")),
224                                port,
225                                utility::string_t(U("jklajsd/yes%20no")),
226                                utility::string_t(U("key1=va%25l1")),
227                                utility::string_t(U("las%20t")));
228         }
229     }
230 
231     TEST(validation)
232     {
233         {
234             // true
235             uri_builder builder(U("http://localhost:4567/"));
236             VERIFY_IS_TRUE(builder.is_valid());
237 
238             // false
239             builder = uri_builder();
240             builder.set_scheme(U("123"));
241             VERIFY_IS_FALSE(builder.is_valid());
242         }
243         {
244             // true
245             uri_builder builder(U("http://localhost:4567/"));
246             VERIFY_IS_TRUE(builder.is_valid());
247 
248             // false
249             builder = uri_builder();
250             builder.set_scheme(U("123"));
251             VERIFY_IS_FALSE(builder.is_valid());
252         }
253     }
254 
255     TEST(uri_creation_string)
256     {
257         utility::string_t uri_str(U("http://steve:temp@localhost:4556/"));
258 
259         // to_string
260         uri_builder builder(uri_str);
261         VERIFY_ARE_EQUAL(uri_str, builder.to_string());
262 
263         // to_string
264         VERIFY_ARE_EQUAL(uri_str, builder.to_string());
265 
266         // to uri
267         VERIFY_ARE_EQUAL(uri_str, builder.to_uri().to_string());
268 
269         // to encoded string
270         uri_builder with_space(builder);
271         with_space.set_path(utility::string_t(U("path%20with%20space")));
272         VERIFY_ARE_EQUAL(U("http://steve:temp@localhost:4556/path%20with%20space"), with_space.to_string());
273     }
274 
275     TEST(append_path_string)
276     {
277         // empty uri builder path
278         uri_builder builder;
279         builder.append_path(U("/path1"));
280         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
281 
282         // empty append path
283         builder.append_path(U(""));
284         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
285 
286         // uri builder with slash
287         builder.append_path(U("/"));
288         builder.append_path(U("path2"));
289         VERIFY_ARE_EQUAL(U("/path1/path2"), builder.path());
290 
291         // both with slash
292         builder.append_path(U("/"));
293         builder.append_path(U("/path3"));
294         VERIFY_ARE_EQUAL(U("/path1/path2/path3"), builder.path());
295 
296         // both without slash
297         builder.append_path(U("path4"));
298         VERIFY_ARE_EQUAL(U("/path1/path2/path3/path4"), builder.path());
299 
300         // encoding
301         builder.clear();
302         builder.append_path(U("encode%things"));
303         VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
304 
305         builder.clear();
306         builder.append_path(U("encode%things"), false);
307         VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
308 
309         builder.clear();
310         builder.append_path(U("encode%things"), true);
311         VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path());
312 
313         // self references
314         builder.set_path(U("example"));
315         builder.append_path(builder.path());
316         VERIFY_ARE_EQUAL(U("example/example"), builder.path());
317 
318         builder.set_path(U("/example"));
319         builder.append_path(builder.path());
320         VERIFY_ARE_EQUAL(U("/example/example"), builder.path());
321 
322         builder.set_path(U("/example/"));
323         builder.append_path(builder.path());
324         VERIFY_ARE_EQUAL(U("/example/example/"), builder.path());
325     }
326 
327     TEST(append_path_raw_string)
328     {
329         // empty uri builder path
330         uri_builder builder;
331         builder.append_path_raw(U("path1"));
332         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
333 
334         // empty append path
335         builder.append_path_raw(U(""));
336         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
337 
338         // uri builder with slash
339         builder.append_path_raw(U("/"));
340         builder.append_path_raw(U("path2"));
341         VERIFY_ARE_EQUAL(U("/path1///path2"), builder.path());
342 
343         // leading slash (should result in "//")
344         builder.append_path_raw(U("/path3"));
345         VERIFY_ARE_EQUAL(U("/path1///path2//path3"), builder.path());
346 
347         // trailing slash
348         builder.append_path_raw(U("path4/"));
349         builder.append_path_raw(U("path5"));
350         VERIFY_ARE_EQUAL(U("/path1///path2//path3/path4//path5"), builder.path());
351 
352         // encoding
353         builder.clear();
354         builder.append_path_raw(U("encode%things"));
355         VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
356 
357         builder.clear();
358         builder.append_path_raw(U("encode%things"), false);
359         VERIFY_ARE_EQUAL(U("/encode%things"), builder.path());
360 
361         builder.clear();
362         builder.append_path_raw(U("encode%things"), true);
363         VERIFY_ARE_EQUAL(U("/encode%25things"), builder.path());
364 
365         // self references
366         builder.set_path(U("example"));
367         builder.append_path_raw(builder.path());
368         VERIFY_ARE_EQUAL(U("example/example"), builder.path());
369 
370         builder.set_path(U("/example"));
371         builder.append_path_raw(builder.path());
372         VERIFY_ARE_EQUAL(U("/example//example"), builder.path());
373 
374         builder.set_path(U("/example/"));
375         builder.append_path_raw(builder.path());
376         VERIFY_ARE_EQUAL(U("/example///example/"), builder.path());
377     }
378 
379     TEST(append_query_string)
380     {
381         // empty uri builder query
382         uri_builder builder;
383         builder.append_query(U("key1=value1"));
384         VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
385 
386         // empty append query
387         builder.append_query(U(""));
388         VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
389 
390         // uri builder with ampersand
391         builder.append_query(U("&"));
392         builder.append_query(U("key2=value2"));
393         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2"), builder.query());
394 
395         // both with ampersand
396         builder.append_query(U("&"));
397         builder.append_query(U("&key3=value3"));
398         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3"), builder.query());
399 
400         // both without ampersand
401         builder.append_query(U("key4=value4"));
402         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4"), builder.query());
403 
404         // number query
405         builder.append_query(U("key5"), 1);
406         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1"), builder.query());
407 
408         // string query
409         builder.append_query(U("key6"), U("val6"));
410         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6"), builder.query());
411 
412         // key and value separate with '=', '&', and ';'
413         builder.append_query(U("key=&;"), U("=&;value"));
414         VERIFY_ARE_EQUAL(
415             U("key1=value1&key2=value2&key3=value3&key4=value4&key5=1&key6=val6&key%3D%26%3B=%3D%26%3Bvalue"),
416             builder.query());
417 
418         // self references
419         builder.set_query(U("example"));
420         builder.append_query(builder.query());
421         VERIFY_ARE_EQUAL(U("example&example"), builder.query());
422 
423         builder.set_query(U("&example"));
424         builder.append_query(builder.query());
425         VERIFY_ARE_EQUAL(U("&example&example"), builder.query());
426 
427         builder.set_query(U("&example&"));
428         builder.append_query(builder.query());
429         VERIFY_ARE_EQUAL(U("&example&example&"), builder.query());
430     }
431 
432     TEST(append_query_string_no_encode)
433     {
434         uri_builder builder;
435         builder.append_query(U("key=&;"), U("=&;value"), false);
436         VERIFY_ARE_EQUAL(U("key=&;==&;value"), builder.query());
437     }
438 
439     TEST(append_string)
440     {
441         // with just path
442         uri_builder builder;
443         builder.append(U("/path1"));
444         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
445 
446         // with just query
447         builder.append(U("?key1=value1"));
448         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
449         VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
450         VERIFY_ARE_EQUAL(U("/path1?key1=value1"), builder.to_string());
451 
452         // with just fragment
453         builder.append(U("#fragment"));
454         VERIFY_ARE_EQUAL(U("/path1"), builder.path());
455         VERIFY_ARE_EQUAL(U("key1=value1"), builder.query());
456         VERIFY_ARE_EQUAL(U("fragment"), builder.fragment());
457         VERIFY_ARE_EQUAL(U("/path1?key1=value1#fragment"), builder.to_string());
458 
459         // with all
460         builder.append(U("/path2?key2=value2#frag2"));
461         VERIFY_ARE_EQUAL(U("/path1/path2"), builder.path());
462         VERIFY_ARE_EQUAL(U("key1=value1&key2=value2"), builder.query());
463         VERIFY_ARE_EQUAL(U("fragmentfrag2"), builder.fragment());
464         VERIFY_ARE_EQUAL(U("/path1/path2?key1=value1&key2=value2#fragmentfrag2"), builder.to_string());
465     }
466 
467     TEST(append_empty_string)
468     {
469         utility::string_t uri_str(U("http://uribuilder.com/"));
470         uri_builder builder(uri_str);
471         builder.append(U(""));
472 
473         VERIFY_ARE_EQUAL(builder.to_string(), uri_str);
474     }
475 
476     TEST(append_path_encoding)
477     {
478         uri_builder builder;
479         builder.append_path(U("/path space"), true);
480         VERIFY_ARE_EQUAL(U("/path%20space"), builder.path());
481 
482         builder.append_path(U("path2"));
483         VERIFY_ARE_EQUAL(U("/path%20space/path2"), builder.path());
484     }
485 
486     TEST(append_query_encoding)
487     {
488         uri_builder builder;
489         builder.append_query(U("key1 =value2"), true);
490         VERIFY_ARE_EQUAL(U("key1%20=value2"), builder.query());
491 
492         builder.append_query(U("key2=value3"));
493         VERIFY_ARE_EQUAL(U("key1%20=value2&key2=value3"), builder.query());
494     }
495 
496     TEST(append_encoding)
497     {
498         uri_builder builder;
499         builder.append(uri::encode_uri(U("path space?key =space#frag space")));
500         VERIFY_ARE_EQUAL(U("/path%20space"), builder.path());
501         VERIFY_ARE_EQUAL(U("key%20=space"), builder.query());
502         VERIFY_ARE_EQUAL(U("frag%20space"), builder.fragment());
503         VERIFY_ARE_EQUAL(U("/path%20space?key%20=space#frag%20space"), builder.to_string());
504 
505         // try with encoded_string
506         builder = uri_builder();
507         builder.append(U("/path2?key2=value2#frag2"));
508         VERIFY_ARE_EQUAL(U("/path2"), builder.path());
509         VERIFY_ARE_EQUAL(U("key2=value2"), builder.query());
510         VERIFY_ARE_EQUAL(U("frag2"), builder.fragment());
511         VERIFY_ARE_EQUAL(U("/path2?key2=value2#frag2"), builder.to_string());
512     }
513 
514     TEST(host_encoding)
515     {
516         // Check that ASCII characters that are invalid in a host name
517         // do not get percent-encoded.
518 
519         uri_builder ub1;
520         ub1.set_scheme(U("http")).set_host(U("????dfasddsf!@#$%^&*()_+")).set_port(80);
521 
522         VERIFY_IS_FALSE(ub1.is_valid());
523     }
524 
525     TEST(clear)
526     {
527         uri_builder ub;
528         ub.clear();
529         CHECK(ub.scheme() == U(""));
530         CHECK(ub.user_info() == U(""));
531         CHECK(ub.host() == U(""));
532         CHECK(ub.port() == -1);
533         CHECK(ub.path() == U("/"));
534         CHECK(ub.query() == U(""));
535         CHECK(ub.fragment() == U(""));
536 
537         ub = uri_builder(U("http://myhost.com/path1"));
538         ub.append_path(U("path2"));
539         uri u = ub.to_uri();
540         ub.clear();
541         CHECK(ub.scheme() == U(""));
542         CHECK(ub.user_info() == U(""));
543         CHECK(ub.host() == U(""));
544         CHECK(ub.port() == -1);
545         CHECK(ub.path() == U("/"));
546         CHECK(ub.query() == U(""));
547         CHECK(ub.fragment() == U(""));
548         CHECK(u.to_string() == U("http://myhost.com/path1/path2"));
549 
550         ub.append_path(U("path3"));
551         ub.set_host(U("hahah"));
552         ub.set_fragment(U("No"));
553         ub.clear();
554         CHECK(ub.scheme() == U(""));
555         CHECK(ub.user_info() == U(""));
556         CHECK(ub.host() == U(""));
557         CHECK(ub.port() == -1);
558         CHECK(ub.path() == U("/"));
559         CHECK(ub.query() == U(""));
560         CHECK(ub.fragment() == U(""));
561     }
562 
563     TEST(to_string_invalid_uri)
564     {
565         uri_builder builder(U("http://invaliduri.com"));
566         builder.set_scheme(U("1http"));
567         VERIFY_THROWS(builder.to_string(), uri_exception);
568         VERIFY_THROWS(builder.to_uri(), uri_exception);
569 
570         builder.set_scheme(U("ht*ip"));
571         VERIFY_THROWS(builder.to_string(), uri_exception);
572 
573         builder.set_scheme(U("htt%20p"));
574         VERIFY_THROWS(builder.to_string(), uri_exception);
575     }
576 
577     TEST(append_query_locale, "Ignore:Android", "Locale unsupported on Android")
578     {
579         std::locale changedLocale;
580         try
581         {
582 #ifdef _WIN32
583             changedLocale = std::locale("fr-FR");
584 #else
585             changedLocale = std::locale("fr_FR.UTF-8");
586 #endif
587         }
588         catch (const std::exception&)
589         {
590             // Silently pass if locale isn't installed on machine.
591             return;
592         }
593 
594         tests::common::utilities::locale_guard loc(changedLocale);
595 
596         uri_builder builder;
597         auto const& key = U("key1000");
598         builder.append_query(key, 1000);
599         ::utility::string_t expected(key);
600         expected.append(U("=1000"));
601         VERIFY_ARE_EQUAL(expected, builder.query());
602     }
603 
604     TEST(github_crash_994) { web::uri uri(U("http://127.0.0.1:34568/")); }
605 
606 } // SUITE(uri_builder_tests)
607 
608 } // namespace uri_tests
609 } // namespace functional
610 } // namespace tests
611