171 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#if !defined(MIJIN_NET_URL_HPP_INCLUDED)
 | 
						|
#define MIJIN_NET_URL_HPP_INCLUDED 1
 | 
						|
 | 
						|
#include <cstdint>
 | 
						|
#include <string>
 | 
						|
#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() noexcept = default;
 | 
						|
    constexpr URLBase(const URLBase&) = default;
 | 
						|
    constexpr URLBase(URLBase&&) noexcept = default;
 | 
						|
    constexpr URLBase(string_t base) 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&&) noexcept = default;
 | 
						|
 | 
						|
    [[nodiscard]] constexpr bool isValid() const noexcept { return !base_.empty(); }
 | 
						|
    [[nodiscard]] constexpr string_view_t getScheme() const noexcept { return scheme_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getUserInfo() const noexcept { return userinfo_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getHost() const noexcept { return host_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getPath() const noexcept { return path_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getQuery() const noexcept { return query_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getFragment() const noexcept { return fragment_; }
 | 
						|
    [[nodiscard]] constexpr string_view_t getPathQueryFragment() const noexcept { return pathQueryFragment_; }
 | 
						|
    [[nodiscard]] constexpr std::uint16_t getPort() const noexcept { return port_; }
 | 
						|
 | 
						|
 | 
						|
    constexpr void clear() noexcept;
 | 
						|
private:
 | 
						|
    constexpr void parse() noexcept;
 | 
						|
    constexpr bool parseAuthority(string_view_t authority) noexcept;
 | 
						|
};
 | 
						|
 | 
						|
using URL = URLBase<char>;
 | 
						|
 | 
						|
template<typename TChar, typename TTraits, typename TAllocator>
 | 
						|
constexpr void URLBase<TChar, TTraits, TAllocator>::clear() 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() 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) 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)
 |