1 //
2 // Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
3 // Copyright 2018 Capitar IT Group BV <info@capitar.com>
4 //
5 // This software is supplied under the terms of the MIT License, a
6 // copy of which should be located in the distribution where this
7 // file was obtained (LICENSE.txt).  A copy of the license may also be
8 // found online at https://opensource.org/licenses/MIT.
9 //
10 
11 #include "nng_impl.h"
12 #include <nuts.h>
13 #include <string.h>
14 
15 void
test_url_host(void)16 test_url_host(void)
17 {
18 	nng_url *url;
19 
20 	NUTS_PASS(nng_url_parse(&url, "http://www.google.com"));
21 	NUTS_ASSERT(url != NULL);
22 	NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
23 	NUTS_TRUE(strcmp(url->u_host, "www.google.com") == 0);
24 	NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
25 	NUTS_TRUE(strcmp(url->u_port, "80") == 0);
26 	NUTS_TRUE(strcmp(url->u_path, "") == 0);
27 	NUTS_TRUE(strcmp(url->u_requri, "") == 0);
28 	NUTS_TRUE(url->u_query == NULL);
29 	NUTS_TRUE(url->u_fragment == NULL);
30 	NUTS_TRUE(url->u_userinfo == NULL);
31 	nng_url_free(url);
32 }
33 
34 void
test_url_host_port(void)35 test_url_host_port(void)
36 {
37 	nng_url *url;
38 	NUTS_PASS(nng_url_parse(&url, "http://www.google.com:1234"));
39 	NUTS_ASSERT(url != NULL);
40 	NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
41 	NUTS_TRUE(strcmp(url->u_host, "www.google.com:1234") == 0);
42 	NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
43 	NUTS_TRUE(strcmp(url->u_port, "1234") == 0);
44 	NUTS_TRUE(strcmp(url->u_path, "") == 0);
45 	NUTS_TRUE(strcmp(url->u_requri, "") == 0);
46 	NUTS_TRUE(url->u_query == NULL);
47 	NUTS_TRUE(url->u_fragment == NULL);
48 	NUTS_TRUE(url->u_userinfo == NULL);
49 	nng_url_free(url);
50 }
51 
52 void
test_url_host_port_path(void)53 test_url_host_port_path(void)
54 {
55 	nng_url *url;
56 
57 	NUTS_PASS(
58 	    nng_url_parse(&url, "http://www.google.com:1234/somewhere"));
59 	NUTS_ASSERT(url != NULL);
60 	NUTS_TRUE(strcmp(url->u_scheme, "http") == 0);
61 	NUTS_TRUE(strcmp(url->u_host, "www.google.com:1234") == 0);
62 	NUTS_TRUE(strcmp(url->u_hostname, "www.google.com") == 0);
63 	NUTS_TRUE(strcmp(url->u_port, "1234") == 0);
64 	NUTS_TRUE(strcmp(url->u_path, "/somewhere") == 0);
65 	NUTS_TRUE(strcmp(url->u_requri, "/somewhere") == 0);
66 	NUTS_TRUE(url->u_userinfo == NULL);
67 	NUTS_TRUE(url->u_query == NULL);
68 	NUTS_TRUE(url->u_fragment == NULL);
69 	nng_url_free(url);
70 }
71 
72 void
test_url_user_info(void)73 test_url_user_info(void)
74 {
75 	nng_url *url;
76 	NUTS_PASS(nng_url_parse(
77 	    &url, "http://garrett@www.google.com:1234/somewhere"));
78 	NUTS_ASSERT(url != NULL);
79 	NUTS_MATCH(url->u_scheme, "http");
80 	NUTS_MATCH(url->u_userinfo, "garrett");
81 	NUTS_MATCH(url->u_host, "www.google.com:1234");
82 	NUTS_MATCH(url->u_hostname, "www.google.com");
83 	NUTS_MATCH(url->u_port, "1234");
84 	NUTS_MATCH(url->u_path, "/somewhere");
85 	NUTS_MATCH(url->u_requri, "/somewhere");
86 	NUTS_NULL(url->u_query);
87 	NUTS_NULL(url->u_fragment);
88 	nng_url_free(url);
89 }
90 
91 void
test_url_path_query_param(void)92 test_url_path_query_param(void)
93 {
94 	nng_url *url;
95 	NUTS_PASS(
96 	    nng_url_parse(&url, "http://www.google.com/somewhere?result=yes"));
97 	NUTS_ASSERT(url != NULL);
98 	NUTS_MATCH(url->u_scheme, "http");
99 	NUTS_MATCH(url->u_host, "www.google.com");
100 	NUTS_MATCH(url->u_hostname, "www.google.com");
101 	NUTS_MATCH(url->u_port, "80");
102 	NUTS_MATCH(url->u_path, "/somewhere");
103 	NUTS_MATCH(url->u_query, "result=yes");
104 	NUTS_MATCH(url->u_requri, "/somewhere?result=yes");
105 	NUTS_NULL(url->u_userinfo);
106 	NUTS_NULL(url->u_fragment);
107 	nng_url_free(url);
108 }
109 
110 void
test_url_query_param_anchor(void)111 test_url_query_param_anchor(void)
112 {
113 	nng_url *url;
114 	NUTS_PASS(nng_url_parse(&url,
115 	    "http://www.google.com/"
116 	    "somewhere?result=yes#chapter1"));
117 	NUTS_ASSERT(url != NULL);
118 	NUTS_MATCH(url->u_scheme, "http");
119 	NUTS_MATCH(url->u_host, "www.google.com");
120 	NUTS_MATCH(url->u_hostname, "www.google.com");
121 	NUTS_MATCH(url->u_port, "80");
122 	NUTS_MATCH(url->u_path, "/somewhere");
123 	NUTS_MATCH(url->u_query, "result=yes");
124 	NUTS_MATCH(url->u_fragment, "chapter1");
125 	NUTS_MATCH(url->u_requri, "/somewhere?result=yes#chapter1");
126 	NUTS_NULL(url->u_userinfo);
127 	nng_url_free(url);
128 }
129 
130 void
test_url_path_anchor(void)131 test_url_path_anchor(void)
132 {
133 	nng_url *url;
134 	NUTS_PASS(
135 	    nng_url_parse(&url, "http://www.google.com/somewhere#chapter2"));
136 	NUTS_ASSERT(url != NULL);
137 	NUTS_MATCH(url->u_scheme, "http");
138 	NUTS_MATCH(url->u_host, "www.google.com");
139 	NUTS_MATCH(url->u_hostname, "www.google.com");
140 	NUTS_MATCH(url->u_port, "80");
141 	NUTS_MATCH(url->u_path, "/somewhere");
142 	NUTS_MATCH(url->u_fragment, "chapter2");
143 	NUTS_MATCH(url->u_requri, "/somewhere#chapter2");
144 	NUTS_NULL(url->u_query);
145 	NUTS_NULL(url->u_userinfo);
146 	nng_url_free(url);
147 }
148 
149 void
test_url_anchor(void)150 test_url_anchor(void)
151 {
152 	nng_url *url;
153 	NUTS_PASS(nng_url_parse(&url, "http://www.google.com#chapter3"));
154 	NUTS_ASSERT(url != NULL);
155 	NUTS_MATCH(url->u_scheme, "http");
156 	NUTS_MATCH(url->u_host, "www.google.com");
157 	NUTS_MATCH(url->u_hostname, "www.google.com");
158 	NUTS_MATCH(url->u_path, "");
159 	NUTS_MATCH(url->u_port, "80");
160 	NUTS_MATCH(url->u_fragment, "chapter3");
161 	NUTS_MATCH(url->u_requri, "#chapter3");
162 	NUTS_NULL(url->u_query);
163 	NUTS_NULL(url->u_userinfo);
164 	nng_url_free(url);
165 }
166 
167 void
test_url_query_param(void)168 test_url_query_param(void)
169 {
170 	nng_url *url;
171 	NUTS_PASS(nng_url_parse(&url, "http://www.google.com?color=red"));
172 	NUTS_ASSERT(url != NULL);
173 	NUTS_MATCH(url->u_scheme, "http");
174 	NUTS_MATCH(url->u_host, "www.google.com");
175 	NUTS_MATCH(url->u_hostname, "www.google.com");
176 	NUTS_MATCH(url->u_path, "");
177 	NUTS_MATCH(url->u_port, "80");
178 	NUTS_MATCH(url->u_query, "color=red");
179 	NUTS_MATCH(url->u_requri, "?color=red");
180 	NUTS_ASSERT(url != NULL);
181 	NUTS_NULL(url->u_userinfo);
182 	nng_url_free(url);
183 }
184 
185 void
test_url_v6_host(void)186 test_url_v6_host(void)
187 {
188 	nng_url *url;
189 	NUTS_PASS(nng_url_parse(&url, "http://[::1]"));
190 	NUTS_ASSERT(url != NULL);
191 	NUTS_MATCH(url->u_scheme, "http");
192 	NUTS_MATCH(url->u_host, "[::1]");
193 	NUTS_MATCH(url->u_hostname, "::1");
194 	NUTS_MATCH(url->u_path, "");
195 	NUTS_MATCH(url->u_port, "80");
196 	NUTS_NULL(url->u_query);
197 	NUTS_NULL(url->u_fragment);
198 	NUTS_NULL(url->u_userinfo);
199 	nng_url_free(url);
200 }
201 
202 void
test_url_v6_host_port(void)203 test_url_v6_host_port(void)
204 {
205 	nng_url *url;
206 	NUTS_PASS(nng_url_parse(&url, "http://[::1]:29"));
207 	NUTS_ASSERT(url != NULL);
208 	NUTS_MATCH(url->u_scheme, "http");
209 	NUTS_MATCH(url->u_host, "[::1]:29");
210 	NUTS_MATCH(url->u_hostname, "::1");
211 	NUTS_MATCH(url->u_path, "");
212 	NUTS_MATCH(url->u_port, "29");
213 	NUTS_NULL(url->u_query);
214 	NUTS_NULL(url->u_fragment);
215 	NUTS_NULL(url->u_userinfo);
216 	nng_url_free(url);
217 }
218 
219 void
test_url_v6_host_port_path(void)220 test_url_v6_host_port_path(void)
221 {
222 	nng_url *url;
223 	NUTS_PASS(nng_url_parse(&url, "http://[::1]:29/bottles"));
224 	NUTS_ASSERT(url != NULL);
225 	NUTS_MATCH(url->u_scheme, "http");
226 	NUTS_MATCH(url->u_host, "[::1]:29");
227 	NUTS_MATCH(url->u_hostname, "::1");
228 	NUTS_MATCH(url->u_path, "/bottles");
229 	NUTS_MATCH(url->u_port, "29");
230 	NUTS_NULL(url->u_query);
231 	NUTS_NULL(url->u_fragment);
232 	NUTS_NULL(url->u_userinfo);
233 	nng_url_free(url);
234 }
235 
236 void
test_url_tcp_port(void)237 test_url_tcp_port(void)
238 {
239 	nng_url *url;
240 	NUTS_PASS(nng_url_parse(&url, "tcp://:9876/"));
241 	NUTS_ASSERT(url != NULL);
242 	NUTS_MATCH(url->u_scheme, "tcp");
243 	NUTS_MATCH(url->u_host, ":9876");
244 	NUTS_MATCH(url->u_hostname, "");
245 	NUTS_MATCH(url->u_path, "/");
246 	NUTS_MATCH(url->u_port, "9876");
247 	NUTS_NULL(url->u_query);
248 	NUTS_NULL(url->u_fragment);
249 	NUTS_NULL(url->u_userinfo);
250 	nng_url_free(url);
251 }
252 
253 void
test_url_bare_ws(void)254 test_url_bare_ws(void)
255 {
256 	nng_url *url;
257 
258 	NUTS_PASS(nng_url_parse(&url, "ws://"));
259 	NUTS_ASSERT(url != NULL);
260 	NUTS_MATCH(url->u_scheme, "ws");
261 	NUTS_MATCH(url->u_host, "");
262 	NUTS_MATCH(url->u_hostname, "");
263 	NUTS_MATCH(url->u_path, "");
264 	NUTS_MATCH(url->u_port, "80");
265 	NUTS_NULL(url->u_query);
266 	NUTS_NULL(url->u_fragment);
267 	NUTS_NULL(url->u_userinfo);
268 	nng_url_free(url);
269 }
270 
271 void
test_url_ws_wildcard(void)272 test_url_ws_wildcard(void)
273 {
274 	nng_url *url;
275 	NUTS_PASS(nng_url_parse(&url, "ws://*:12345/foobar"));
276 	NUTS_ASSERT(url != NULL);
277 	NUTS_MATCH(url->u_scheme, "ws");
278 	NUTS_MATCH(url->u_host, ":12345");
279 	NUTS_MATCH(url->u_hostname, "");
280 	NUTS_MATCH(url->u_path, "/foobar");
281 	NUTS_MATCH(url->u_port, "12345");
282 	NUTS_NULL(url->u_query);
283 	NUTS_NULL(url->u_fragment);
284 	NUTS_NULL(url->u_userinfo);
285 	nng_url_free(url);
286 }
287 
288 void
test_url_ssh(void)289 test_url_ssh(void)
290 {
291 	nng_url *url;
292 	NUTS_PASS(nng_url_parse(&url, "ssh://user@host.example.com"));
293 	NUTS_ASSERT(url != NULL);
294 	NUTS_MATCH(url->u_scheme, "ssh");
295 	NUTS_MATCH(url->u_host, "host.example.com");
296 	NUTS_MATCH(url->u_hostname, "host.example.com");
297 	NUTS_MATCH(url->u_path, "");
298 	NUTS_MATCH(url->u_port, "22");
299 	NUTS_NULL(url->u_query);
300 	NUTS_NULL(url->u_fragment);
301 	NUTS_MATCH(url->u_userinfo, "user");
302 	nng_url_free(url);
303 }
304 
305 void
test_url_bad_scheme(void)306 test_url_bad_scheme(void)
307 {
308 	nng_url *url = NULL;
309 	NUTS_FAIL(nng_url_parse(&url, "www.google.com"), NNG_EINVAL);
310 	NUTS_NULL(url);
311 	NUTS_FAIL(nng_url_parse(&url, "http:www.google.com"), NNG_EINVAL);
312 	NUTS_NULL(url);
313 }
314 
315 void
test_url_bad_ipv6(void)316 test_url_bad_ipv6(void)
317 {
318 	nng_url *url = NULL;
319 	NUTS_FAIL(nng_url_parse(&url, "http://[::1"), NNG_EINVAL);
320 	NUTS_NULL(url);
321 	NUTS_FAIL(nng_url_parse(&url, "http://[::1]bogus"), NNG_EINVAL);
322 	NUTS_NULL(url);
323 }
324 
325 void
test_url_canonify(void)326 test_url_canonify(void)
327 {
328 	nng_url *url = NULL;
329 	NUTS_PASS(nng_url_parse(
330 	    &url, "hTTp://www.EXAMPLE.com/bogus/.%2e/%7egarrett"));
331 	NUTS_ASSERT(url != NULL);
332 	NUTS_MATCH(url->u_scheme, "http");
333 	NUTS_MATCH(url->u_hostname, "www.example.com");
334 	NUTS_MATCH(url->u_port, "80");
335 	NUTS_MATCH(url->u_path, "/~garrett");
336 	nng_url_free(url);
337 }
338 
339 void
test_url_path_resolve(void)340 test_url_path_resolve(void)
341 {
342 	nng_url *url = NULL;
343 	NUTS_PASS(
344 	    nng_url_parse(&url, "http://www.x.com//abc/def/./x/..///./../y"));
345 	NUTS_MATCH(url->u_scheme, "http");
346 	NUTS_MATCH(url->u_hostname, "www.x.com");
347 	NUTS_MATCH(url->u_port, "80");
348 	NUTS_MATCH(url->u_path, "/abc/y");
349 	nng_url_free(url);
350 }
351 
352 void
test_url_query_info_pass(void)353 test_url_query_info_pass(void)
354 {
355 	nng_url *url = NULL;
356 	NUTS_PASS(
357 	    nng_url_parse(&url, "http://www.x.com/?/abc/def/./x/.././../y"));
358 	NUTS_ASSERT(url != NULL);
359 	NUTS_MATCH(url->u_scheme, "http");
360 	NUTS_MATCH(url->u_hostname, "www.x.com");
361 	NUTS_MATCH(url->u_port, "80");
362 	NUTS_MATCH(url->u_path, "/");
363 	NUTS_MATCH(url->u_query, "/abc/def/./x/.././../y");
364 	nng_url_free(url);
365 }
366 
367 void
test_url_bad_utf8(void)368 test_url_bad_utf8(void)
369 {
370 	nng_url *url = NULL;
371 	NUTS_FAIL(nng_url_parse(&url, "http://x.com/x%80x"), NNG_EINVAL);
372 	NUTS_NULL(url);
373 	NUTS_FAIL(nng_url_parse(&url, "http://x.com/x%c0%81"), NNG_EINVAL);
374 	NUTS_NULL(url);
375 }
376 
377 void
test_url_good_utf8(void)378 test_url_good_utf8(void)
379 {
380 	nng_url *url = NULL;
381 	NUTS_PASS(nng_url_parse(&url, "http://www.x.com/%c2%a2_cents"));
382 	NUTS_ASSERT(url != NULL);
383 	NUTS_MATCH(url->u_scheme, "http");
384 	NUTS_MATCH(url->u_hostname, "www.x.com");
385 	NUTS_MATCH(url->u_port, "80");
386 	NUTS_MATCH(url->u_path, "/\xc2\xa2_cents");
387 	nng_url_free(url);
388 }
389 
390 void
test_url_decode(void)391 test_url_decode(void)
392 {
393 	uint8_t out[16];
394 	size_t  len;
395 
396 	out[3] = 'x';
397 	len    = nni_url_decode(out, "abc", 3);
398 	NUTS_TRUE(len == 3);
399 	NUTS_TRUE(memcmp(out, "abc", 3) == 0);
400 	NUTS_TRUE(out[3] == 'x');
401 
402 	len = nni_url_decode(out, "x%00y", 3); // embedded NULL
403 	NUTS_TRUE(len == 3);
404 	NUTS_TRUE(memcmp(out, "x\x00y", 3) == 0);
405 	NUTS_TRUE(out[3] == 'x');
406 
407 	len = nni_url_decode(out, "%3987", 3);
408 	NUTS_TRUE(len == 3);
409 	NUTS_TRUE(memcmp(out, "987", 3) == 0);
410 	NUTS_TRUE(out[3] == 'x');
411 
412 	len = nni_url_decode(out, "78%39", 3);
413 	NUTS_TRUE(len == 3);
414 	NUTS_TRUE(memcmp(out, "789", 3) == 0);
415 	NUTS_TRUE(out[3] == 'x');
416 
417 	len = nni_url_decode(out, "", 5);
418 	NUTS_TRUE(len == 0);
419 	NUTS_TRUE(memcmp(out, "789", 3) == 0);
420 	NUTS_TRUE(out[3] == 'x');
421 
422 	len = nni_url_decode(out, "be", 2);
423 	NUTS_TRUE(len == 2);
424 	NUTS_TRUE(memcmp(out, "be9", 3) == 0);
425 	NUTS_TRUE(out[3] == 'x');
426 
427 	len = nni_url_decode(out, "78%39", 2);
428 	NUTS_TRUE(len == (size_t) -1);
429 
430 	len = nni_url_decode(out, "", 2);
431 	NUTS_TRUE(len == 0);
432 
433 	len = nni_url_decode(out, "78%", 5);
434 	NUTS_TRUE(len == (size_t) -1);
435 
436 	len = nni_url_decode(out, "78%xy", 5);
437 	NUTS_TRUE(len == (size_t) -1);
438 
439 	len = nni_url_decode(out, "78%1$", 5);
440 	NUTS_TRUE(len == (size_t) -1);
441 
442 	len = nni_url_decode(out, "%%20", 5);
443 	NUTS_TRUE(len == (size_t) -1);
444 }
445 
446 NUTS_TESTS = {
447 	{ "url host", test_url_host },
448 	{ "url host port", test_url_host_port },
449 	{ "url host port path", test_url_host_port_path },
450 	{ "url user info", test_url_user_info },
451 	{ "url path query param", test_url_path_query_param },
452 	{ "url query param anchor", test_url_query_param_anchor },
453 	{ "url path anchor", test_url_path_anchor },
454 	{ "url anchor", test_url_anchor },
455 	{ "url query param", test_url_query_param },
456 	{ "url v6 host", test_url_v6_host },
457 	{ "url v6 host port", test_url_v6_host_port },
458 	{ "url v6 host port path", test_url_v6_host_port_path },
459 	{ "url tcp port", test_url_tcp_port },
460 	{ "url bare ws", test_url_bare_ws },
461 	{ "url ws wildcard", test_url_ws_wildcard },
462 	{ "url ssh", test_url_ssh },
463 	{ "url bad scheme", test_url_bad_scheme },
464 	{ "url bad v6", test_url_bad_ipv6 },
465 	{ "url canonify", test_url_canonify },
466 	{ "url path resolve", test_url_path_resolve },
467 	{ "url query info pass", test_url_query_info_pass },
468 	{ "url bad utf8", test_url_bad_utf8 },
469 	{ "url good utf8", test_url_good_utf8 },
470 	{ "url decode", test_url_decode },
471 	{ NULL, NULL },
472 };