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