// file : tests/url/driver.cxx -*- C++ -*- // copyright : Copyright (c) 2014-2018 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include <cassert> #ifndef __cpp_lib_modules #include <string> #include <utility> // move() #include <iostream> #include <iterator> // back_inserter #include <stdexcept> // invalid_argument #endif // Other includes. #ifdef __cpp_modules #ifdef __cpp_lib_modules import std.core; import std.io; #endif import butl.url; import butl.utility; // operator<<(ostream, exception) #else #include <libbutl/url.mxx> #include <libbutl/utility.mxx> #endif using namespace std; using namespace butl; enum class scheme { http, https, file }; namespace butl { template <> struct url_traits<scheme> { using string_type = wstring; using path_type = wstring; using scheme_type = scheme; using authority_type = basic_url_authority<string_type>; static scheme_type translate_scheme (const string_type& url, string_type&& scheme, optional<authority_type>& /*authority*/, optional<path_type>& path, optional<string_type>& /*query*/, optional<string_type>& /*fragment*/) { // Note that we must compare case-insensitive in the real program. // if (scheme == L"http") return scheme_type::http; else if (scheme == L"https") return scheme_type::https; else if (scheme == L"file") return scheme_type::file; else if (scheme.empty ()) { // If the URL looks like an absolute filesystem path, then translate it // to the file URL. If it is not, then leave all the components absent // to fail with a proper exception description. // wchar_t c; if ((c = url[0]) == '/' || (url.size () > 2 && alpha (c) && url[1] == ':' && url[2] == '/')) path = url; return scheme_type::file; } else throw invalid_argument ("unknown scheme"); } // Translate scheme type back to its string representation. // static string_type translate_scheme (string_type&, /*url*/ const scheme_type& scheme, const optional<authority_type>& /*authority*/, const optional<path_type>& /*path*/, const optional<string_type>& /*query*/, const optional<string_type>& /*fragment*/) { switch (scheme) { case scheme_type::http: return L"http"; case scheme_type::https: return L"https"; case scheme_type::file: return L"file"; } assert (false); // Can't be here. return L""; } static path_type translate_path (string_type&& path) { return path_type (move (path)); } static string_type translate_path (const path_type& path) {return string_type (path);} }; } // Usages: // // argv[0] // argv[0] [-c|-s|-w] <url> // // Perform some basic tests if no URL is provided. Otherwise round-trip the URL // to STDOUT. URL must contain only ASCII characters. Exit with zero code on // success. Exit with code one on parsing failure, printing error description // to STDERR. // // -c // Print the URL components one per line. Print the special '[null]' string // for an absent components. This is the default option if URL is provided. // // -s // Print stringified url object representation. // // -w // Same as above, but use the custom wstring-based url_traits // implementation for the basic_url template. // int main (int argc, const char* argv[]) try { using butl::nullopt; // Disambiguate with std::nullopt. using wurl = basic_url<scheme>; using wurl_authority = wurl::authority_type; using wurl_host = wurl::host_type; enum class print_mode { str, wstr, comp } mode (print_mode::comp); int i (1); for (; i != argc; ++i) { string o (argv[i]); if (o == "-s") mode = print_mode::str; else if (o == "-w") mode = print_mode::wstr; else if (o == "-c") mode = print_mode::comp; else break; // End of options. } if (i == argc) { // Test ctors and operators. // { wurl u0; assert (u0.empty ()); wurl u1 (scheme::http, wurl_authority {wstring (), wurl_host (L"[123]"), 0}, wstring (L"login"), wstring (L"q="), wstring (L"f")); assert (!u1.empty ()); assert (u1 != u0); wurl u2 (scheme::http, wurl_host (L"123", url_host_kind::ipv6), wstring (L"login"), wstring (L"q="), wstring (L"f")); assert (u2 == u1); wurl u3 (scheme::http, wurl_host (L"123", url_host_kind::ipv6), 0, wstring (L"login"), wstring (L"q="), wstring (L"f")); assert (u3 == u2); wurl u4 (scheme::http, L"[123]", wstring (L"login"), wstring (L"q="), wstring (L"f")); assert (u4 == u3); wurl u5 (scheme::http, L"[123]", 0, wstring (L"login"), wstring (L"q="), wstring (L"f")); assert (u5 == u4); } // Test encode and decode. // { const char* s ("ABC +"); string es (url::encode (s)); assert (es == "ABC%20%2B"); string ds (url::decode (es)); assert (ds == s); } { const char* s ("ABC +"); string es (url::encode (s, [] (char& c) -> bool { if (c == ' ') { c = '+'; return false; } return !url::unreserved (c); })); assert (es == "ABC+%2B"); string ds (url::decode (es.c_str (), [] (char& c) { if (c == '+') c = ' '; })); assert (ds == s); } { const wchar_t s[] = L"ABC "; wstring es; wurl::encode (s, s + 4, back_inserter (es), [] (wchar_t& c) -> bool { if (!alnum (c)) return true; ++c; return false; }); assert (es == L"BCD%20"); wstring ds (wurl::decode (es, [] (wchar_t& c) { if (alnum (c)) --c; })); assert (ds == s); } } else // Round-trip the URL. { assert (i + 1 == argc); const char* ua (argv[i]); switch (mode) { case print_mode::str: { cout << (*ua != '\0' ? url (ua) : url ()) << endl; break; } case print_mode::wstr: { // Convert ASCII string to wstring. // wstring s (ua, ua + strlen (ua)); wcout << (!s.empty () ? wurl (s) : wurl ()) << endl; break; } case print_mode::comp: { // Convert ASCII string to wstring. // wstring s (ua, ua + strlen (ua)); wurl u; if (!s.empty ()) u = wurl (s); if (!u.empty ()) { wstring s; wcout << wurl::traits::translate_scheme (s, u.scheme, nullopt, nullopt, nullopt, nullopt) << endl; } else wcout << L"[null]" << endl; if (u.authority) { const wchar_t* kinds[] = {L"ipv4", L"ipv6", L"name"}; const wurl_authority& a (*u.authority); wcout << a.user << L'@' << a.host.value << L':' << a.port << " " << kinds[static_cast<size_t> (a.host.kind)] << endl; } else wcout << L"[null]" << endl; wcout << (u.path ? *u.path : L"[null]") << endl << (u.query ? *u.query : L"[null]") << endl << (u.fragment ? *u.fragment : L"[null]") << endl; break; } } } return 0; } catch (const invalid_argument& e) { cerr << e << endl; return 1; }