#pragma once #include #include #include namespace Incart::Net::WebApi { class Uri final { // URI = scheme ":" ["//" authority] path ["?" query] ["#" fragment] // authority = [userinfo "@"] host [":" port] // query = [parameter1=value2][(;|&)parameter2=value2]... // userinfo = username[:password] private: QString m_scheme; QString m_userName; QString m_password; QString m_host; uint16_t m_port; QString m_path; QMap m_query; QString m_fragment; bool m_hasAuthority = false; bool m_hasQuery = false; bool m_hasFragment = false; bool m_hasUserInfo = false; bool m_hasPort = false; bool m_hasPassword = false; QString m_uri; bool m_isValid = true; public: Uri(const QString& uri_) : m_uri(uri_) { int searchIndex = findFirst(m_uri, ':', 0, m_uri.size()); if (searchIndex < 0) { m_isValid = false; return; } QString scheme = m_uri.mid(0, searchIndex); bool hasAuthorityFlag = false; bool hasUserInfoFlag = false; bool hasPortFlag = false; bool hasPasswordFlag = false; bool hasFragmentFlag = false; bool hasQueryFlag = false; uint16_t port = 0; QString userName = ""; QString password = ""; QString host = ""; QString fragment = ""; QMap query; bool isOk = true; int pathStartIndex = searchIndex + 1; // try parse authority if ((m_uri.size() > searchIndex + 2) && (m_uri[searchIndex + 1] == '/') && (m_uri[searchIndex + 2] == '/')) { hasAuthorityFlag = true; pathStartIndex = findFirst(m_uri, '/', searchIndex + 3, m_uri.size()); if (pathStartIndex < 0) { pathStartIndex = m_uri.size(); } if (pathStartIndex <= searchIndex + 3) { m_isValid = false; return; } QString authorityText = m_uri.mid(searchIndex + 3, pathStartIndex - searchIndex - 3); // try parse userInfo searchIndex = findFirst(authorityText, '@', 0, authorityText.size()); int startSearchPortIndex = 0; if (searchIndex >= 0) { startSearchPortIndex = searchIndex + 1; hasUserInfoFlag = true; QString userInfoText = authorityText.mid(0, searchIndex); // try parse password int userNameEndIndex = userInfoText.size(); searchIndex = findFirst(userInfoText, ':', 0, userInfoText.size()); if (searchIndex >= 0) { hasPasswordFlag = true; userNameEndIndex = searchIndex; password = userInfoText.mid(searchIndex + 1); } userName = userInfoText.mid(0, userNameEndIndex); } // try parse port searchIndex = findFirst(m_uri, ':', startSearchPortIndex, authorityText.size()); int hostEndIndex = authorityText.size(); if (searchIndex >= 0) { hostEndIndex = searchIndex; QString portText = authorityText.mid(searchIndex + 1); port = portText.toUShort(&isOk); if (!isOk) { m_isValid = false; return; } hasPortFlag = true; } host = authorityText.mid(startSearchPortIndex, hostEndIndex - startSearchPortIndex + 1); } // try parse fragment int fragmentIndex = findFirst(m_uri, '#', pathStartIndex, m_uri.size()); int pathEndIndex = m_uri.size(); if (fragmentIndex >= 0) { pathEndIndex = fragmentIndex; hasFragmentFlag = true; fragment = m_uri.mid(fragmentIndex + 1); } // try parse query int queryIndex = findFirst(m_uri, '?', pathStartIndex, pathEndIndex); if (queryIndex >= 0) { QString queryText = m_uri.mid(queryIndex + 1, pathEndIndex - queryIndex - 1); if (!parseQuery(queryText, query)) { m_isValid = false; return; } hasQueryFlag = true; } // parse path m_path = m_uri.mid(pathStartIndex, pathEndIndex - pathStartIndex); m_scheme = scheme; m_userName = userName; m_password = password; m_host = host; m_port = port; m_query = query; m_fragment = fragment; m_hasAuthority = hasAuthorityFlag; m_hasQuery = hasQueryFlag; m_hasFragment = hasFragmentFlag; m_hasUserInfo = hasUserInfoFlag; m_hasPort = hasPortFlag; m_hasPassword = hasPasswordFlag; } QString getScheme() { return m_scheme; } QString getUserName() { return m_userName; } QString getPassword() { return m_password; } QString getHost() { return m_host; } uint16_t getPort() { return m_port; } QString getPath() { return m_path; } QMap getQuery() { return m_query; } QString getFragment() { return m_fragment; } bool hasAuthority() { return m_hasAuthority; } bool hasQuery() { return m_hasQuery; } bool hasFragment() { return m_hasFragment; } bool hasUserInfo() { return m_hasUserInfo; } bool hasPort() { return m_hasPort; } bool hasPassword() { return m_hasPassword; } bool isValid() { return m_isValid; } bool parse(const QString& text) { int searchIndex = findFirst(text, ':', 0, text.size()); if (searchIndex < 0) { return false; } QString scheme = text.mid(0, searchIndex); bool hasAuthorityFlag = false; bool hasUserInfoFlag = false; bool hasPortFlag = false; bool hasPasswordFlag = false; bool hasFragmentFlag = false; bool hasQueryFlag = false; uint16_t port = 0; QString userName = ""; QString password = ""; QString host = ""; QString fragment = ""; QMap query; bool isOk = true; int pathStartIndex = searchIndex + 1; // try parse authority if ((text.size() > searchIndex + 2) && (text[searchIndex + 1] == '/') && (text[searchIndex + 2] == '/')) { hasAuthorityFlag = true; pathStartIndex = findFirst(text, '/', searchIndex + 3, text.size()); if (pathStartIndex < 0) { pathStartIndex = text.size(); } if (pathStartIndex <= searchIndex + 3) { return false; } QString authorityText = text.mid(searchIndex + 3, pathStartIndex - searchIndex - 3); // try parse userInfo searchIndex = findFirst(authorityText, '@', 0, authorityText.size()); int startSearchPortIndex = 0; if (searchIndex >= 0) { startSearchPortIndex = searchIndex + 1; hasUserInfoFlag = true; QString userInfoText = authorityText.mid(0, searchIndex); // try parse password int userNameEndIndex = userInfoText.size(); searchIndex = findFirst(userInfoText, ':', 0, userInfoText.size()); if (searchIndex >= 0) { hasPasswordFlag = true; userNameEndIndex = searchIndex; password = userInfoText.mid(searchIndex + 1); } userName = userInfoText.mid(0, userNameEndIndex); } // try parse port searchIndex = findFirst(text, ':', startSearchPortIndex, authorityText.size()); int hostEndIndex = authorityText.size(); if (searchIndex >= 0) { hostEndIndex = searchIndex; QString portText = authorityText.mid(searchIndex + 1); port = portText.toUShort(&isOk); if (!isOk) { return false; } hasPortFlag = true; } host = authorityText.mid(startSearchPortIndex, hostEndIndex - startSearchPortIndex + 1); } // try parse fragment int fragmentIndex = findFirst(text, '#', pathStartIndex, text.size()); int pathEndIndex = text.size(); if (fragmentIndex >= 0) { pathEndIndex = fragmentIndex; hasFragmentFlag = true; fragment = text.mid(fragmentIndex + 1); } // try parse query int queryIndex = findFirst(text, '?', pathStartIndex, pathEndIndex); if (queryIndex >= 0) { QString queryText = text.mid(queryIndex + 1, pathEndIndex - queryIndex - 1); if (!parseQuery(queryText, query)) { return false; } hasQueryFlag = true; } // parse path m_path = text.mid(pathStartIndex, pathEndIndex - pathStartIndex); m_scheme = scheme; m_userName = userName; m_password = password; m_host = host; m_port = port; m_query = query; m_fragment = fragment; m_hasAuthority = hasAuthorityFlag; m_hasQuery = hasQueryFlag; m_hasFragment = hasFragmentFlag; m_hasUserInfo = hasUserInfoFlag; m_hasPort = hasPortFlag; m_hasPassword = hasPasswordFlag; return true; } QString toString() { return m_uri; } private: bool parseQuery(const QString& text, QMap& query) { int state = 0; // 0 - c != '='; c != ';'; c != '&' - start read parameter // 1 - c == '=' - start read value // 2 - c == ';' || c == '&' - start read next pair QString currParameter; QString currValue; int parameterStartIndex = 0; int parameterEndIndex = 0; int valueStartIndex = 0; int valueEndIndex = 0; for (int i = 0; i < text.size(); i++) { char symbol = text[i].digitValue(); if ((i == text.size() - 1) && (state != 2)) { return false; } switch (state) { case 0: if (symbol == '=' || symbol == ';' || symbol == '&') { return false; } state = 1; currParameter = ""; parameterStartIndex = i; break; case 1: if (symbol == ';' || symbol == '&') { return false; } else if (symbol == '=') { parameterEndIndex = i; if (parameterEndIndex == parameterStartIndex) { return false; } currParameter = text.mid(parameterStartIndex, parameterEndIndex - parameterStartIndex); valueStartIndex = i + 1; state = 2; } break; case 2: if ((i == text.size() - 1) || symbol == ';' || symbol == '&') { valueEndIndex = i - valueStartIndex; if (i != text.size() - 1) { valueEndIndex++; } currValue = text.mid(valueStartIndex, valueEndIndex); query.insert(currParameter, currValue); state = 0; } break; } } return true; } static int findFirst(const QString& text_, char symbol, int pos_, int count_) { int maxIndex = text_.size() - 1; int endIndex = pos_ + count_; endIndex = (endIndex > maxIndex) ? maxIndex : endIndex; for (int i = pos_; i < endIndex; i++) { if (text_[i] == symbol) { return i; } } return -1; } }; } // namespace Incart::Net::WebApi