1 /* $Id: test_ncbi_url.cpp 613672 2020-08-11 16:34:37Z grichenk $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Author: Aleksey Grichenko
27 *
28 * File Description:
29 * TEST for: CUrl class
30 *
31 */
32
33 #include <ncbi_pch.hpp>
34 #include <corelib/ncbistr.hpp>
35 #include <corelib/ncbi_url.hpp>
36
37 #define BOOST_AUTO_TEST_MAIN
38 #include <corelib/test_boost.hpp>
39
40 #include <common/test_assert.h> /* This header must go last */
41
42
43 USING_NCBI_SCOPE;
44
45
46 const char* kScheme = "scheme";
47 const char* kSchemeLB = NCBI_SCHEME_SERVICE;
48 const char* kUser = "user";
49 const char* kPassword = "password";
50 const char* kHostShort = "hostname";
51 const char* kHostFull = "host.net";
52 const char* kHostIPv6 = "[1:2:3:4]";
53 const char* kServiceSimple = "servicename";
54 const char* kServiceSpecial = "/service/name";
55 const char* kPort = "1234";
56 const char* kPathAbs = "/path/file";
57 const char* kPathRel = "rel/path/file";
58 const char* kArgs = "arg1=val1&arg2=val2";
59 const char* kFragment = "fragment";
60
61
62 enum EUrlParts {
63 fHostNone = 0,
64 fHostShort = 0x0001,
65 fHostFull = 0x0002,
66 fHostIPv6 = 0x0003,
67 fHostMask = 0x0003,
68 fService = 0x0004,
69 fServiceSimple = 0,
70 fServiceSpecial = 0x0001,
71 fAuthorityMask = 0x0007, // host or service
72
73 fScheme = 0x0008,
74 fUser = 0x0010,
75 fPassword = 0x0020,
76 fPort = 0x0040,
77
78 fPathNone = 0,
79 fPathRoot = 0x0080,
80 fPathAbs = 0x0100,
81 fPathRel = 0x0180,
82 fPathMask = 0x0180,
83
84 fArgs = 0x0200,
85 fFragment = 0x0400,
86
87 fMax = 0x0800
88 };
89
90
s_TestUrl(string url_str,int flags)91 void s_TestUrl(string url_str, int flags)
92 {
93 CUrl url(url_str);
94 if (url.GetIsGeneric()) {
95 BOOST_CHECK(url_str.find("//") != NPOS);
96 }
97 else {
98 BOOST_CHECK(url_str.find("//") == NPOS);
99 }
100
101 if (flags & fScheme) {
102 BOOST_CHECK_EQUAL(url.GetScheme(), kScheme);
103 }
104 else {
105 BOOST_CHECK(url.GetScheme().empty());
106 }
107
108 if (flags & fService) {
109 BOOST_CHECK(url.GetHost().empty());
110 BOOST_CHECK(url.IsService());
111 switch (flags & fHostMask) {
112 case fServiceSimple:
113 BOOST_CHECK_EQUAL(url.GetService(), kServiceSimple);
114 break;
115 case fServiceSpecial:
116 BOOST_CHECK_EQUAL(url.GetService(), kServiceSpecial);
117 break;
118 default:
119 BOOST_ERROR("Invalid service type flag");
120 }
121 }
122 else {
123 BOOST_CHECK(url.GetService().empty());
124 switch (flags & fHostMask) {
125 case fHostNone:
126 BOOST_CHECK(url.GetHost().empty());
127 break;
128 case fHostShort:
129 BOOST_CHECK_EQUAL(url.GetHost(), kHostShort);
130 break;
131 case fHostFull:
132 BOOST_CHECK_EQUAL(url.GetHost(), kHostFull);
133 break;
134 case fHostIPv6:
135 BOOST_CHECK_EQUAL(url.GetHost(), kHostIPv6);
136 break;
137 default:
138 BOOST_ERROR("Invalid host type flag");
139 break;
140 }
141 }
142
143 if (flags & fUser) {
144 BOOST_CHECK_EQUAL(url.GetUser(), kUser);
145 }
146 else {
147 BOOST_CHECK(url.GetUser().empty());
148 }
149
150 if (flags & fPassword) {
151 BOOST_CHECK_EQUAL(url.GetPassword(), kPassword);
152 }
153 else {
154 BOOST_CHECK(url.GetPassword().empty());
155 }
156
157 if (flags & fPort) {
158 BOOST_CHECK_EQUAL(url.GetPort(), kPort);
159 }
160 else {
161 BOOST_CHECK(url.GetPort().empty());
162 }
163
164 switch (flags & fPathMask) {
165 case fPathNone:
166 BOOST_CHECK(url.GetPath().empty());
167 break;
168 case fPathRoot:
169 BOOST_CHECK_EQUAL(url.GetPath(), "/");
170 break;
171 case fPathAbs:
172 BOOST_CHECK_EQUAL(url.GetPath(), kPathAbs);
173 break;
174 case fPathRel:
175 BOOST_CHECK_EQUAL(url.GetPath(), kPathRel);
176 break;
177 default:
178 BOOST_ERROR("Invalid path type flag");
179 }
180
181 if (flags & fArgs) {
182 BOOST_CHECK_EQUAL(url.GetArgs().GetQueryString(CUrlArgs::eAmp_Char), kArgs);
183 }
184 else {
185 BOOST_CHECK(url.GetArgs().GetArgs().empty());
186 }
187
188
189 if (flags & fFragment) {
190 BOOST_CHECK_EQUAL(url.GetFragment(), kFragment);
191 }
192 else {
193 BOOST_CHECK(url.GetFragment().empty());
194 }
195
196 BOOST_CHECK_EQUAL(url_str, url.ComposeUrl(CUrlArgs::eAmp_Char));
197 }
198
199
BOOST_AUTO_TEST_CASE(s_UrlTestParsed)200 BOOST_AUTO_TEST_CASE(s_UrlTestParsed)
201 {
202 cout << "*** Testing parsed URLs" << endl;
203 string surl;
204 for (int flags = 0; flags < fMax; ++flags) {
205 surl.clear();
206 if (flags & fScheme) {
207 // Skip scheme-only URLs.
208 if (flags == fScheme) continue;
209 surl = ((flags & fService) ? string(kScheme) + "+" + kSchemeLB : kScheme);
210 // When testing relative path, do not include "//".
211 surl += !(flags & (fHostMask | fService)) ? ":" : "://";
212 }
213 else if (flags & fService) {
214 // Simple service URLs do not require ncbilb scheme.
215 if (flags != fService) {
216 surl = string(kSchemeLB) + "://";
217 }
218 }
219 else if ((flags & fHostMask) != fHostNone) {
220 // If host is present, the authority part must start with "//".
221 surl = "//";
222 }
223
224 string authority;
225 if (flags & fService) {
226 // Port is not allowed for services.
227 if (flags & fPort) continue;
228 switch (flags & fHostMask) {
229 case fServiceSimple:
230 authority = kServiceSimple;
231 break;
232 case fServiceSpecial:
233 authority = NStr::URLEncode(kServiceSpecial);
234 break;
235 default:
236 continue; // invalid service type
237 }
238 }
239 else {
240 switch (flags & fHostMask) {
241 case fHostNone:
242 break;
243 case fHostShort:
244 authority = kHostShort;
245 break;
246 case fHostFull:
247 authority = kHostFull;
248 break;
249 case fHostIPv6:
250 authority = kHostIPv6;
251 break;
252 default:
253 continue; // unused host type
254 }
255 if (flags & fPort) {
256 authority += string(":") + kPort;
257 }
258 }
259
260 // Do not test user info and port if host/service is missing
261 if (!(flags & fAuthorityMask)) {
262 if (flags & (fUser | fPassword | fPort)) continue;
263 }
264
265 string user_info;
266 if (flags & fUser) {
267 user_info = kUser;
268 }
269 if (flags & fPassword) {
270 // TODO: Is password allowed without user?
271 user_info += string(":") + kPassword;
272 }
273 if (!user_info.empty()) {
274 authority = user_info + "@" + authority;
275 }
276 surl += authority;
277
278 switch (flags & fPathMask) {
279 case fPathNone:
280 break;
281 case fPathRoot:
282 surl += "/";
283 break;
284 case fPathAbs:
285 surl += kPathAbs;
286 break;
287 case fPathRel:
288 // Relative path can be combined only with scheme
289 if (flags & ~(fScheme | fPathRel)) continue;
290 surl += kPathRel;
291 break;
292 default:
293 continue;
294 }
295
296 if (flags & fArgs) {
297 surl += string("?") + kArgs;
298 }
299
300 string fragment;
301 if (flags & fFragment) {
302 surl += string("#") + kFragment;
303 }
304
305 cout << "Testing: " << surl << endl;
306 s_TestUrl(surl, flags);
307 }
308 }
309
310
311 struct SCUrlTest {
312 string m_Expected;
313 string m_UrlString;
314 string m_Scheme;
315 string m_Service;
316 string m_Host;
317 string m_Path;
318 bool m_Generic;
319
CompareSCUrlTest320 void Compare(const CUrl& url)
321 {
322 string result(url.ComposeUrl(CUrlArgs::eAmp_Char));
323 BOOST_CHECK_EQUAL(m_Expected, result);
324 BOOST_CHECK_EQUAL(m_Scheme, url.GetScheme());
325 BOOST_CHECK_EQUAL(!m_Service.empty(), url.IsService());
326 BOOST_CHECK_EQUAL(m_Service, url.GetService());
327 BOOST_CHECK_EQUAL(m_Host, url.GetHost());
328 BOOST_CHECK_EQUAL(m_Path, url.GetPath());
329 BOOST_CHECK_EQUAL(m_Generic, url.GetIsGeneric());
330 }
331 };
332
333
334 static SCUrlTest s_CUrlTests[] = {
335 { "service", "service", "", "service", "", "", false },
336 { "Some/path", "Some/path", "", "", "", "Some/path", false },
337 { "//host", "//host", "", "", "host", "", true },
338 { "//host/Some/path", "//host/Some/path", "", "", "host", "/Some/path", true },
339 { "http://host", "http://host", "http", "", "host", "", true },
340 { "http://host/Some/path", "http://host/Some/path", "http", "", "host", "/Some/path", true },
341
342 { "ncbilb://service", "ncbilb://service", "", "service", "", "", true },
343 { "ncbilb://service/Some/path", "ncbilb://service/Some/path", "", "service", "", "/Some/path", true },
344 { "http+ncbilb://service", "http+ncbilb://service", "http", "service", "", "", true },
345 { "http+ncbilb://service/Some/path", "http+ncbilb://service/Some/path", "http", "service", "", "/Some/path", true },
346
347 { "scheme:Some/path", "scheme:Some/path", "scheme", "", "", "Some/path", false },
348 { "scheme://host", "scheme://host", "scheme", "", "host", "", true },
349 { "scheme://host/Some/path", "scheme://host/Some/path", "scheme", "", "host", "/Some/path", true },
350 { "scheme+ncbilb://service", "scheme+ncbilb://service", "scheme", "service", "", "", true },
351 { "scheme+ncbilb://service/Some/path", "scheme+ncbilb://service/Some/path", "scheme", "service", "", "/Some/path", true }
352 };
353
354
BOOST_AUTO_TEST_CASE(s_UrlTestComposed)355 BOOST_AUTO_TEST_CASE(s_UrlTestComposed)
356 {
357 cout << "*** Testing composed URLs" << endl;
358 for (auto test : s_CUrlTests) {
359 cout << "Testing: " << test.m_UrlString << endl;
360 CUrl purl(test.m_UrlString);
361 test.Compare(purl);
362
363 CUrl curl;
364 if (!test.m_Scheme.empty()) curl.SetScheme(test.m_Scheme);
365 if (!test.m_Service.empty()) curl.SetService(test.m_Service);
366 if (!test.m_Host.empty()) curl.SetHost(test.m_Host);
367 if (!test.m_Path.empty()) curl.SetPath(test.m_Path);
368 curl.SetIsGeneric(test.m_Generic);
369 test.Compare(curl);
370 }
371 }
372
373
BOOST_AUTO_TEST_CASE(s_UrlTestSpecial)374 BOOST_AUTO_TEST_CASE(s_UrlTestSpecial)
375 {
376 cout << "*** Testing special cases" << endl;
377 // Service-only URL may include ncbilb scheme.
378 string surl = string(kSchemeLB) + "://" + kServiceSimple;
379 cout << "Testing: " << surl << endl;
380 s_TestUrl(surl, fService);
381
382 {
383 cout << "Testing: ignore ncbilb scheme"<< endl;
384 CUrl url;
385 url.SetService(kServiceSimple);
386 url.SetScheme(kSchemeLB);
387 BOOST_CHECK(url.IsService());
388 BOOST_CHECK_EQUAL(url.GetService(), kServiceSimple);
389 BOOST_CHECK(url.GetScheme().empty());
390 }
391 {
392 cout << "Testing: ignore ncbilb scheme / switch to service" << endl;
393 CUrl url;
394 url.SetHost(kServiceSimple);
395 url.SetScheme(kSchemeLB);
396 BOOST_CHECK(url.IsService());
397 BOOST_CHECK_EQUAL(url.GetService(), kServiceSimple);
398 BOOST_CHECK(url.GetScheme().empty());
399 }
400 {
401 cout << "Testing: strip +ncbilb scheme" << endl;
402 CUrl url;
403 url.SetService(kServiceSimple);
404 url.SetScheme(string(kScheme) + "+" + kSchemeLB);
405 BOOST_CHECK(url.IsService());
406 BOOST_CHECK_EQUAL(url.GetService(), kServiceSimple);
407 BOOST_CHECK_EQUAL(url.GetScheme(), kScheme);
408 }
409 {
410 cout << "Testing: strip ncbilb scheme / switch to service" << endl;
411 CUrl url;
412 url.SetHost(kServiceSimple);
413 url.SetScheme(string(kScheme) + "+" + kSchemeLB);
414 BOOST_CHECK(url.IsService());
415 BOOST_CHECK_EQUAL(url.GetService(), kServiceSimple);
416 BOOST_CHECK_EQUAL(url.GetScheme(), kScheme);
417 }
418
419 cout << "Testing: parse host:port/path" << endl;
420 {
421 CUrl url(string(kHostShort) + ":" + kPort);
422 BOOST_CHECK(url.GetScheme().empty());
423 BOOST_CHECK(url.GetUser().empty());
424 BOOST_CHECK(url.GetPassword().empty());
425 BOOST_CHECK_EQUAL(url.GetHost(), kHostShort);
426 BOOST_CHECK_EQUAL(url.GetPort(), kPort);
427 BOOST_CHECK(url.GetPath().empty());
428 }
429 {
430 CUrl url(string(kHostFull) + ":" + kPort);
431 BOOST_CHECK(url.GetScheme().empty());
432 BOOST_CHECK(url.GetUser().empty());
433 BOOST_CHECK(url.GetPassword().empty());
434 BOOST_CHECK_EQUAL(url.GetHost(), kHostFull);
435 BOOST_CHECK_EQUAL(url.GetPort(), kPort);
436 BOOST_CHECK(url.GetPath().empty());
437 }
438 {
439 CUrl url(string(kHostShort) + ":" + kPort + kPathAbs);
440 BOOST_CHECK(url.GetScheme().empty());
441 BOOST_CHECK(url.GetUser().empty());
442 BOOST_CHECK(url.GetPassword().empty());
443 BOOST_CHECK_EQUAL(url.GetHost(), kHostShort);
444 BOOST_CHECK_EQUAL(url.GetPort(), kPort);
445 BOOST_CHECK_EQUAL(url.GetPath(), kPathAbs);
446 }
447 {
448 CUrl url(string(kHostFull) + ":" + kPort + kPathAbs);
449 BOOST_CHECK(url.GetScheme().empty());
450 BOOST_CHECK(url.GetUser().empty());
451 BOOST_CHECK(url.GetPassword().empty());
452 BOOST_CHECK_EQUAL(url.GetHost(), kHostFull);
453 BOOST_CHECK_EQUAL(url.GetPort(), kPort);
454 BOOST_CHECK_EQUAL(url.GetPath(), kPathAbs);
455 }
456 {
457 CUrl url("http:123");
458 BOOST_CHECK_EQUAL(url.GetScheme(), "http");
459 BOOST_CHECK(url.GetUser().empty());
460 BOOST_CHECK(url.GetPassword().empty());
461 BOOST_CHECK(url.GetHost().empty());
462 BOOST_CHECK(url.GetPort().empty());
463 BOOST_CHECK_EQUAL(url.GetPath(), "123");
464 }
465 {
466 CUrl url("https:123");
467 BOOST_CHECK_EQUAL(url.GetScheme(), "https");
468 BOOST_CHECK(url.GetUser().empty());
469 BOOST_CHECK(url.GetPassword().empty());
470 BOOST_CHECK(url.GetHost().empty());
471 BOOST_CHECK(url.GetPort().empty());
472 BOOST_CHECK_EQUAL(url.GetPath(), "123");
473 }
474 {
475 CUrl url("file:123");
476 BOOST_CHECK_EQUAL(url.GetScheme(), "file");
477 BOOST_CHECK(url.GetUser().empty());
478 BOOST_CHECK(url.GetPassword().empty());
479 BOOST_CHECK(url.GetHost().empty());
480 BOOST_CHECK(url.GetPort().empty());
481 BOOST_CHECK_EQUAL(url.GetPath(), "123");
482 }
483 {
484 CUrl url("ftp:123");
485 BOOST_CHECK_EQUAL(url.GetScheme(), "ftp");
486 BOOST_CHECK(url.GetUser().empty());
487 BOOST_CHECK(url.GetPassword().empty());
488 BOOST_CHECK(url.GetHost().empty());
489 BOOST_CHECK(url.GetPort().empty());
490 BOOST_CHECK_EQUAL(url.GetPath(), "123");
491 }
492 {
493 CUrl url(string(kScheme) + ":0123");
494 BOOST_CHECK_EQUAL(url.GetScheme(), kScheme);
495 BOOST_CHECK(url.GetUser().empty());
496 BOOST_CHECK(url.GetPassword().empty());
497 BOOST_CHECK(url.GetHost().empty());
498 BOOST_CHECK(url.GetPort().empty());
499 BOOST_CHECK_EQUAL(url.GetPath(), "0123");
500 }
501 {
502 CUrl url(string(kScheme) + ":76543");
503 BOOST_CHECK_EQUAL(url.GetScheme(), kScheme);
504 BOOST_CHECK(url.GetUser().empty());
505 BOOST_CHECK(url.GetPassword().empty());
506 BOOST_CHECK(url.GetHost().empty());
507 BOOST_CHECK(url.GetPort().empty());
508 BOOST_CHECK_EQUAL(url.GetPath(), "76543");
509 }
510 }
511