diff options
Diffstat (limited to 'libadhocutil/uriParse.cpp')
-rw-r--r-- | libadhocutil/uriParse.cpp | 228 |
1 files changed, 95 insertions, 133 deletions
diff --git a/libadhocutil/uriParse.cpp b/libadhocutil/uriParse.cpp index 489747e..e2cff82 100644 --- a/libadhocutil/uriParse.cpp +++ b/libadhocutil/uriParse.cpp @@ -3,178 +3,140 @@ #include <boost/algorithm/string/case_conv.hpp> #include <boost/lexical_cast.hpp> #include <cctype> +#include <charconv> #include <cstdint> #include <cstring> #include <utility> namespace AdHoc { - static inline int - _is_scheme_char(int c) - { - return (!isalpha(c) && '+' != c && '-' != c && '.' != c) ? 0 : 1; - } - Uri::Uri(const std::string & uri) { - auto * puri = this; - const char * curstr; - int userpass_flag; - int bracket_flag; - - curstr = uri.c_str(); + auto is_scheme_char = [](int c) { + return (!std::isalpha(c) && '+' != c && '-' != c && '.' != c) ? 0 : 1; + }; + auto endor = [](std::string_view haystack, auto needle) { + if (const auto n = haystack.find_first_of(needle); n != std::string_view::npos) { + return n; + } + return haystack.length(); + }; + auto parsePort = [&uri, this](const std::string_view in) { + if (in.empty()) { + throw InvalidUri("Invalid format; no port after :", uri); + } + port.emplace(); + if (const auto rc = std::from_chars(in.begin(), in.end(), *port); + rc.ptr != in.end() || rc.ec != std::errc {}) { + throw InvalidUri("Invalid port", uri); + } + }; - const char * tmpstr = ::strchr(curstr, ':'); - if (!tmpstr) { - throw InvalidUri("Schema marker not found", uri); + std::string_view curstr = uri; + if (auto colon = curstr.find(':'); colon == std::string_view::npos) { + throw InvalidUri("Scheme marker not found", uri); } - auto len = tmpstr - curstr; - for (decltype(len) i = 0; i < len; i++) { - if (!_is_scheme_char(curstr[i])) { - throw InvalidUri("Invalid format", uri); + else { + if (!std::all_of(curstr.begin(), curstr.begin() + colon, is_scheme_char)) { + throw InvalidUri("Invalid format; no scheme end", uri); } + scheme = curstr.substr(0, colon); + boost::algorithm::to_lower(scheme); + curstr.remove_prefix(colon + 1); } - puri->scheme = std::string(curstr, len); - boost::algorithm::to_lower(puri->scheme); - tmpstr++; - curstr = tmpstr; - for (int i = 0; i < 2; i++) { - if ('/' != *curstr) { - throw InvalidUri("Invalid format", uri); - } - curstr++; + if (!curstr.starts_with("//")) { + throw InvalidUri("Invalid format; // not where expected", uri); } + curstr.remove_prefix(2); - userpass_flag = 0; - tmpstr = curstr; - while ('\0' != *tmpstr) { - if ('@' == *tmpstr) { - userpass_flag = 1; - break; + if (const auto n = curstr.find_first_of("@/"); n != std::string_view::npos && curstr[n] == '@') { + if (const auto colon = curstr.find_first_of("@:"); curstr[colon] == ':') { + username = curstr.substr(0, colon); + password = curstr.substr(colon + 1, n - colon - 1); } - else if ('/' == *tmpstr) { - userpass_flag = 0; - break; + else { + username = curstr.substr(0, n); } - tmpstr++; + curstr.remove_prefix(n + 1); } - tmpstr = curstr; - if (userpass_flag) { - while ('\0' != *tmpstr && ':' != *tmpstr && '@' != *tmpstr) { - tmpstr++; - } - len = tmpstr - curstr; - puri->username = std::string(curstr, len); - curstr = tmpstr; - if (':' == *curstr) { - curstr++; - tmpstr = curstr; - while ('\0' != *tmpstr && '@' != *tmpstr) { - tmpstr++; - } - len = tmpstr - curstr; - puri->password = std::string(curstr, len); - curstr = tmpstr; + if (curstr.empty()) { + throw InvalidUri("Invalid format", uri); + } + if (curstr.starts_with('[')) { + if (const auto closeb = curstr.find(']'); closeb == std::string_view::npos) { + throw InvalidUri("IPv6 address not terminated", uri); } - if ('@' != *curstr) { - throw InvalidUri("Invalid format", uri); + else { + host = curstr.substr(0, closeb + 1); + curstr.remove_prefix(closeb + 1); } - curstr++; - } - - if ('[' == *curstr) { - bracket_flag = 1; } else { - bracket_flag = 0; - } - - tmpstr = curstr; - while ('\0' != *tmpstr) { - if (bracket_flag && ']' == *tmpstr) { - tmpstr++; - break; + if (const auto hostend = curstr.find_first_of(":/"); hostend == std::string_view::npos) { + host = curstr; + return; } - else if (!bracket_flag && (':' == *tmpstr || '/' == *tmpstr)) { - break; + else { + if (hostend == 0) { + throw InvalidUri("Host cannot be blank", uri); + } + host = curstr.substr(0, hostend); + boost::algorithm::to_lower(host); + curstr.remove_prefix(hostend); } - tmpstr++; - } - if (tmpstr == curstr) { - throw InvalidUri("Host cannot be blank", uri); } - len = tmpstr - curstr; - puri->host = std::string(curstr, len); - boost::algorithm::to_lower(puri->host); - curstr = tmpstr; + boost::algorithm::to_lower(host); - if (':' == *curstr) { - curstr++; - tmpstr = curstr; - while ('\0' != *tmpstr && '/' != *tmpstr) { - tmpstr++; + if (curstr.empty()) { + return; + } + if (curstr.starts_with(':')) { + curstr.remove_prefix(1); + if (curstr.empty()) { + throw InvalidUri("Invalid format; no port after :", uri); + } + if (const auto portend = curstr.find('/'); portend == std::string_view::npos) { + parsePort(curstr); + return; + } + else { + parsePort(curstr.substr(0, portend)); + curstr.remove_prefix(portend); } - len = tmpstr - curstr; - puri->port = boost::lexical_cast<uint16_t>(std::string(curstr, len)); - curstr = tmpstr; } + curstr.remove_prefix(1); - if ('\0' == *curstr) { + if (const auto pathend = curstr.find_first_of("#?"); pathend == std::string_view::npos) { + path = curstr; return; } - - if ('/' != *curstr) { - throw InvalidUri("Invalid format", uri); - } - curstr++; - - tmpstr = curstr; - while ('\0' != *tmpstr && '#' != *tmpstr && '?' != *tmpstr) { - tmpstr++; + else { + path = curstr.substr(0, pathend); + curstr.remove_prefix(pathend); } - len = tmpstr - curstr; - puri->path = std::string(curstr, len); - curstr = tmpstr; - if ('?' == *curstr) { - curstr++; - tmpstr = curstr; - while ('\0' != *tmpstr && '#' != *tmpstr) { - while ('\0' != *tmpstr && '#' != *tmpstr && '=' != *tmpstr && '&' != *tmpstr) { - tmpstr++; - } - len = tmpstr - curstr; - auto q = puri->query.insert({std::string(curstr, len), std::string()}); - curstr = tmpstr; - if ('=' == *curstr) { - curstr++; - while ('\0' != *tmpstr && '#' != *tmpstr && '&' != *tmpstr) { - tmpstr++; - } - len = tmpstr - curstr; - q->second = std::string(curstr, len); - curstr = tmpstr; + if (curstr.starts_with('?')) { + curstr.remove_prefix(1); + auto params = curstr.substr(0, endor(curstr, '#')); + while (!params.empty()) { + const auto pair = params.substr(0, endor(params, '&')); + if (const auto eq = pair.find('='); eq != std::string_view::npos) { + query.emplace(pair.substr(0, eq), pair.substr(eq + 1)); } - if ('&' == *tmpstr) { - tmpstr++; - curstr = tmpstr; + else { + query.emplace(pair, std::string {}); } - else if ('\0' != *tmpstr && '#' != *tmpstr) { - throw InvalidUri("Parse error in query params", uri); + params.remove_prefix(pair.length()); + if (!params.empty()) { + params.remove_prefix(1); } } - curstr = tmpstr; } - if ('#' == *curstr) { - curstr++; - tmpstr = curstr; - while ('\0' != *tmpstr) { - tmpstr++; - } - len = tmpstr - curstr; - puri->fragment = std::string(curstr, len); + if (curstr.starts_with('#')) { + curstr.remove_prefix(1); + fragment = curstr; } } |