Url Url::ResolveReference(const Url& ref) const { if (ref.IsAbsolute()) return ref; Url url; url.SetScheme(m_strScheme); url.SetUser(m_strUserName); url.SetPassword(m_strPassword); url.SetHost(m_strHost); url.SetPort(m_iPort); url.SetRawQuery(ref.GetRawQuery()); url.SetFragment(ref.GetFragment()); auto pathDirList = base::strings::Split(m_strPath, "/"); auto relDirList = base::strings::Split(ref.GetPath(), "/"); std::stack<std::string> pathDirStack; for (auto v : pathDirList) { if (v.empty()) continue; pathDirStack.push(v); } for (auto v : relDirList) { if (v.empty() || "." == v) continue; if (".." == v) { if (!pathDirStack.empty()) pathDirStack.pop(); } else pathDirStack.push(v); } if (pathDirStack.empty()) url.SetPath("/"); else { std::string strPath; while (!pathDirStack.empty()) { strPath = pathDirStack.top() + "/" + strPath; pathDirStack.pop(); } url.SetPath("/" + strPath); } return url; }
Url Url::Parse(const std::string& strRawUrl) { // <scheme>://<user>:<password>@<host>:<port>/<path>?<query>#<frag> // <scheme>:opaque?query#fragment Url url; if (strRawUrl.empty()) return url; do { // Firstly, we will split fragment and query string. std::string strPrefix; auto spList = base::strings::Split(strRawUrl, "#"); if (spList.size() > 2) break; strPrefix = spList[0]; if (spList.size() == 2) url.m_strFragment = base::Unescape(spList[1]); spList = base::strings::Split(strPrefix, "?"); if (spList.size() > 2) break; strPrefix = spList[0]; if (spList.size() == 2) url.m_strRawQuery = spList[1]; // Parse scheme. std::string strRest; if (!ParseScheme(strPrefix, url.m_strScheme, strRest)) break; if (url.GetScheme() == "http") url.SetPort(80); else if (url.GetScheme() == "https") url.SetPort(443); if (strRest.empty()) break; // <scheme>:opaque if (!url.m_strScheme.empty() && strRest[0] != '/') { url.m_strOpaque = strRest; break; } // !if <scheme>:opaque // Invalid URL. if (!url.m_strScheme.empty() && !base::strings::StartsWith(strRest, "//")) break; // Relative path. if (url.m_strScheme.empty() && !base::strings::StartsWith(strRest, "//")) { url.m_strPath = base::Unescape(strRest); break; } // Secondly, we will parse the particular case. // <scheme>:///<path> if (strRest[2] == '/') { auto pos1 = strRest.find_first_not_of('/'); if (std::string::npos == pos1) break; url.m_strPath = base::Unescape(strRest.substr(pos1 - 1)); break; } // !if <scheme>:///<path> // <scheme>://<user>:<password>@<host>:<port>/<path> auto pos1 = strRest.find_first_not_of('/'); if (std::string::npos == pos1) break; auto pos2 = strRest.find_first_of("/", pos1); std::string strAuthority; if (std::string::npos == pos2) strAuthority = strRest.substr(pos1); else strAuthority = strRest.substr(pos1, pos2 - pos1); if (!ParseAuthority(strAuthority, url.m_strUserName, url.m_strPassword, url.m_strHost, url.m_iPort)) { url.m_strUserName = ""; url.m_strPassword = ""; url.m_strHost = ""; url.m_iPort = 0; break; } if (pos2 == std::string::npos) { url.m_strPath = "/"; break; } url.m_strPath = base::Unescape(strRest.substr(pos2)); } while (0); return url; }