173 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| 
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #if !defined(MIJIN_NET_URL_HPP_INCLUDED)
 | |
| #define MIJIN_NET_URL_HPP_INCLUDED 1
 | |
| 
 | |
| #include <cstdint>
 | |
| #include <string>
 | |
| #include "../internal/common.hpp"
 | |
| #include "../util/string.hpp"
 | |
| 
 | |
| namespace mijin
 | |
| {
 | |
| template<typename TChar, typename TTraits = std::char_traits<TChar>, typename TAllocator = std::allocator<TChar>>
 | |
| class URLBase
 | |
| {
 | |
| public:
 | |
|     using string_t = std::basic_string<TChar, TTraits, TAllocator>;
 | |
|     using string_view_t = std::basic_string_view<TChar, TTraits>;
 | |
| 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) : URLBase(string_t(base.begin(), base.end())) {}
 | |
|     constexpr URLBase(const TChar* base) : URLBase(string_t(base)) {}
 | |
| 
 | |
|     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<char>;
 | |
| 
 | |
| template<typename TChar, typename TTraits, typename TAllocator>
 | |
| constexpr void URLBase<TChar, TTraits, TAllocator>::clear() MIJIN_NOEXCEPT
 | |
| {
 | |
|     base_.clear();
 | |
|     scheme_ = {};
 | |
|     userinfo_ = {};
 | |
|     host_ = {};
 | |
|     path_ = {};
 | |
|     query_ = {};
 | |
|     fragment_ = {};
 | |
|     port_ = 0;
 | |
| }
 | |
| 
 | |
| template<typename TChar, typename TTraits, typename TAllocator>
 | |
| constexpr void URLBase<TChar, TTraits, TAllocator>::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<typename TChar, typename TTraits, typename TAllocator>
 | |
| constexpr bool URLBase<TChar, TTraits, TAllocator>::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)
 |