173 lines
5.1 KiB
C++
173 lines
5.1 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 = MIJIN_DEFAULT_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, 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<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)
|