#pragma once #if !defined(MIJIN_NET_URL_HPP_INCLUDED) #define MIJIN_NET_URL_HPP_INCLUDED 1 #include #include #include "../internal/common.hpp" #include "../util/string.hpp" namespace mijin { template, typename TAllocator = MIJIN_DEFAULT_ALLOCATOR> class URLBase { public: using string_t = std::basic_string; using string_view_t = std::basic_string_view; private: string_t base_; string_view_t scheme_; string_view_t userinfo_; string_view_t host_; string_view_t path_; string_view_t query_; string_view_t fragment_; string_view_t pathQueryFragment_; std::uint16_t port_ = 0; public: constexpr URLBase() MIJIN_NOEXCEPT = default; constexpr URLBase(const URLBase&) = default; constexpr URLBase(URLBase&&) MIJIN_NOEXCEPT = default; constexpr URLBase(string_t base) MIJIN_NOEXCEPT : base_(std::move(base)) { parse(); } constexpr URLBase(string_view_t base, TAllocator allocator = {}) : URLBase(string_t(base.begin(), base.end(), std::move(allocator))) {} constexpr URLBase(const TChar* base, TAllocator allocator = {}) : URLBase(string_t(base, std::move(allocator))) {} constexpr URLBase& operator=(const URLBase&) = default; constexpr URLBase& operator=(URLBase&&) MIJIN_NOEXCEPT = default; [[nodiscard]] constexpr bool isValid() const MIJIN_NOEXCEPT { return !base_.empty(); } [[nodiscard]] constexpr const string_t& getBase() const MIJIN_NOEXCEPT { return base_; } [[nodiscard]] constexpr string_view_t getScheme() const MIJIN_NOEXCEPT { return scheme_; } [[nodiscard]] constexpr string_view_t getUserInfo() const MIJIN_NOEXCEPT { return userinfo_; } [[nodiscard]] constexpr string_view_t getHost() const MIJIN_NOEXCEPT { return host_; } [[nodiscard]] constexpr string_view_t getPath() const MIJIN_NOEXCEPT { return path_; } [[nodiscard]] constexpr string_view_t getQuery() const MIJIN_NOEXCEPT { return query_; } [[nodiscard]] constexpr string_view_t getFragment() const MIJIN_NOEXCEPT { return fragment_; } [[nodiscard]] constexpr string_view_t getPathQueryFragment() const MIJIN_NOEXCEPT { return pathQueryFragment_; } [[nodiscard]] constexpr std::uint16_t getPort() const MIJIN_NOEXCEPT { return port_; } constexpr void clear() MIJIN_NOEXCEPT; private: constexpr void parse() MIJIN_NOEXCEPT; constexpr bool parseAuthority(string_view_t authority) MIJIN_NOEXCEPT; }; using URL = URLBase; template constexpr void URLBase::clear() MIJIN_NOEXCEPT { base_.clear(); scheme_ = {}; userinfo_ = {}; host_ = {}; path_ = {}; query_ = {}; fragment_ = {}; port_ = 0; } template constexpr void URLBase::parse() MIJIN_NOEXCEPT { if (base_.empty()) { return; } string_view_t toParse = base_; typename string_view_t::size_type pos = toParse.find(TChar(':')); if (pos == string_t::npos) { clear(); return; } scheme_ = toParse.substr(0, pos); toParse = toParse.substr(pos + 1); const TChar DOUBLE_SLASH[] = {TChar('/'), TChar('/'), TChar(0)}; if (!toParse.starts_with(DOUBLE_SLASH)) { userinfo_ = host_ = {}; port_ = 0; } else { toParse = toParse.substr(2); // skip the slashes pos = toParse.find(TChar('/')); if (!parseAuthority(toParse.substr(0, pos))) { clear(); return; } if (pos == string_view_t::npos) { path_ = query_ = fragment_ = pathQueryFragment_ = {}; return; } toParse = toParse.substr(pos); } pathQueryFragment_ = toParse; pos = toParse.find(TChar('#')); if (pos == string_view_t::npos) { fragment_ = {}; } else { fragment_ = toParse.substr(pos + 1); toParse = toParse.substr(0, pos); } pos = toParse.find(TChar('?')); if (pos == string_view_t::npos) { query_ = {}; path_ = toParse; } else { query_ = toParse.substr(pos + 1); path_ = toParse.substr(0, pos); } } template constexpr bool URLBase::parseAuthority(string_view_t authority) MIJIN_NOEXCEPT { string_view_t toParse = authority; typename string_view_t::size_type pos = toParse.find(TChar('@')); if (pos == string_view_t::npos) { userinfo_ = {}; } else { userinfo_ = toParse.substr(0, pos); toParse = toParse.substr(pos + 1); } pos = toParse.find(TChar(':')); // TODO: IPv6 if (pos == string_view_t::npos) { port_ = 0; host_ = toParse; } else { if (!toNumber(toParse.substr(pos + 1), port_)) { return false; } host_ = toParse.substr(0, pos); } return true; } } #endif // !defined(MIJIN_NET_URL_HPP_INCLUDED)