blob: c28f1d1dab8f79fb7450aa8e655c1bbff8cec12c [file] [log] [blame]
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <algorithm>
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <concepts>
#include "ts/ts.h"
#include "ts/remap.h"
#include "cripts/Headers.hpp"
namespace cripts
{
class Context;
class Url
{
using self_type = Url;
class Component
{
using self_type = Component;
public:
Component() = default;
Component(Url *owner) : _owner(owner) {}
virtual cripts::string_view GetSV() = 0;
std::vector<cripts::string_view> Split(char delim);
operator cripts::string_view() { return GetSV(); } // Should not be explicit
bool
operator==(cripts::string_view const &rhs)
{
return GetSV() == rhs;
}
bool
operator!=(cripts::string_view const &rhs)
{
return GetSV() != rhs;
}
virtual void
Reset()
{
_data.clear();
}
cripts::string_view::const_pointer
data()
{
return GetSV().data();
}
cripts::string_view::size_type
size()
{
return GetSV().size();
}
cripts::string_view::size_type
length()
{
return GetSV().size();
}
cripts::string_view::const_pointer
Data()
{
return GetSV().data();
}
cripts::string_view::size_type
Size()
{
return GetSV().size();
}
cripts::string_view::size_type
Length()
{
return GetSV().size();
}
// This is not ideal, but best way I can think of for now to mixin the cripts::string_view mixin class
// Remember to add things here when added to the Lulu.hpp file for the mixin class... :/
[[nodiscard]] constexpr cripts::string_view
substr(cripts::string_view::size_type pos = 0, cripts::string_view::size_type count = cripts::string_view::npos) const
{
return _data.substr(pos, count);
}
void
remove_prefix(cripts::string_view::size_type n)
{
_data.remove_prefix(n);
}
void
remove_suffix(cripts::string_view::size_type n)
{
_data.remove_suffix(n);
}
cripts::string_view &
ltrim(char c)
{
return _data.ltrim(c);
}
cripts::string_view &
rtrim(char c)
{
return _data.rtrim(c);
}
cripts::string_view &
trim(char c)
{
return _data.trim(c);
}
cripts::string_view &
ltrim(const char *chars = " \t\r\n")
{
return _data.ltrim(chars);
}
cripts::string_view &
rtrim(const char *chars = " \t\r\n")
{
return _data.rtrim(chars);
}
cripts::string_view &
trim(const char *chars = " \t")
{
return _data.trim(chars);
}
[[nodiscard]] constexpr char const *
data_end() const noexcept
{
return _data.data_end();
}
[[nodiscard]] bool
ends_with(cripts::string_view suffix) const
{
return _data.ends_with(suffix);
}
[[nodiscard]] bool
starts_with(cripts::string_view const prefix) const
{
return _data.starts_with(prefix);
}
[[nodiscard]] constexpr cripts::string_view::size_type
find(cripts::string_view const substr, cripts::string_view::size_type pos = 0) const
{
return _data.find(substr, pos);
}
[[nodiscard]] constexpr cripts::string_view::size_type
rfind(cripts::string_view const substr, cripts::string_view::size_type pos = 0) const
{
return _data.rfind(substr, pos);
}
[[nodiscard]] constexpr bool
contains(cripts::string_view const substr) const
{
return (_data.find(substr) != _data.npos);
}
protected:
mutable cripts::string_view _data;
Url *_owner = nullptr;
bool _loaded = false;
}; // End class Url::Component
public:
// Each of the members of a URL has its own class, very convenient. Is this
// great C++? Dunno.
class Scheme : public Component
{
using super_type = Component;
using self_type = Scheme;
public:
using Component::Component;
cripts::string_view GetSV() override;
self_type operator=(cripts::string_view scheme);
}; // End class Url::Scheme
class Host : public Component
{
using super_type = Component;
using self_type = Host;
public:
using Component::Component;
cripts::string_view GetSV() override;
self_type operator=(cripts::string_view host);
}; // End class Url::Host
class Port
{
using self_type = Port;
public:
Port(Url *owner) : _owner(owner){};
void
Reset()
{
_port = -1;
}
operator integer(); // This should not be explicit, and should not be const
self_type operator=(int port);
self_type
operator=(cripts::string_view str)
{
uint32_t port = 80;
auto result = std::from_chars(str.data(), str.data() + str.size(), port);
if (result.ec != std::errc::invalid_argument) {
return *this;
} else {
return operator=(port);
}
}
private:
Url *_owner = nullptr;
integer _port = -1;
}; // End class Url::Port
class Path : public Component
{
public:
using Segments = std::vector<cripts::string_view>;
private:
using super_type = Component;
using self_type = Path;
class String : public cripts::StringViewMixin<String>
{
using super_type = cripts::StringViewMixin<String>;
using self_type = String;
public:
String() = default;
// Implemented in the Urls.cc file, bigger function
self_type &operator=(const cripts::string_view str) override;
// These specialized assignment operators all use the above
template <size_t N>
self_type &
operator=(const char (&str)[N])
{
return operator=(cripts::string_view(str, str[N - 1] ? N : N - 1));
}
self_type &
operator=(char *&str)
{
return operator=(cripts::string_view(str, strlen(str)));
}
self_type &
operator=(char const *&str)
{
return operator=(cripts::string_view(str, strlen(str)));
}
self_type &
operator=(const std::string &str)
{
return operator=(cripts::string_view(str));
}
private:
friend class Path;
void
_initialize(cripts::string_view source, Path *owner, Segments::size_type ix)
{
_setSV(source);
_owner = owner;
_ix = ix;
}
Path *_owner = nullptr;
Segments::size_type _ix = 0;
}; // End class Url::Path::String
public:
friend struct fmt::formatter<Path::String>;
using Component::Component;
void Reset() override;
cripts::string_view GetSV() override;
cripts::string operator+=(cripts::string_view add);
self_type operator=(cripts::string_view path);
String operator[](Segments::size_type ix);
void
Erase(Segments::size_type ix)
{
auto p = operator[](ix);
_size -= p.size();
p.operator=("");
}
void
Erase()
{
operator=("");
}
void
Clear()
{
Erase();
}
void Push(cripts::string_view val);
void Insert(Segments::size_type ix, cripts::string_view val);
void
Flush()
{
if (_modified) {
operator=(GetSV());
}
}
private:
void _parser();
bool _modified = false;
Segments _segments; // Lazy loading on this
cripts::string _storage; // Used when recombining the segments into a full path
cripts::string::size_type _size = 0; // Mostly a guestimate for managing _storage
}; // End class Url::Path
class Query : public Component
{
using super_type = Component;
using self_type = Query;
using OrderedParams = std::vector<cripts::string_view>; // Ordered parameter nmes
using HashParams = std::unordered_map<cripts::string_view, cripts::string_view>; // Hash lookups
class Parameter : public cripts::StringViewMixin<Parameter>
{
using super_type = cripts::StringViewMixin<Parameter>;
using self_type = Parameter;
public:
Parameter() = default;
[[nodiscard]] cripts::string_view
Name() const
{
return _name;
}
void
Erase()
{
_owner->Erase(_name);
}
// Implemented in the Urls.cc file, bigger function
self_type &operator=(const cripts::string_view str) override;
// These specialized assignment operators all use the above
template <size_t N>
self_type &
operator=(const char (&str)[N])
{
return operator=(cripts::string_view(str, str[N - 1] ? N : N - 1));
}
self_type &
operator=(char *&str)
{
return operator=(cripts::string_view(str, strlen(str)));
}
self_type &
operator=(char const *&str)
{
return operator=(cripts::string_view(str, strlen(str)));
}
self_type &
operator=(const std::string &str)
{
return operator=(cripts::string_view(str));
}
private:
friend class Query;
void
_initialize(cripts::string_view name, cripts::string_view source, Query *owner)
{
_setSV(source);
_name = name;
_owner = owner;
}
Query *_owner = nullptr;
cripts::string_view _name;
}; // End class Url::Query::Parameter
public:
friend struct fmt::formatter<Query::Parameter>;
using Component::Component;
Query(cripts::string_view load)
{
_data = load;
_size = load.size();
_loaded = true;
_standalone = true;
}
void Reset() override;
cripts::string_view GetSV() override;
self_type operator=(cripts::string_view query);
cripts::string operator+=(cripts::string_view add);
Parameter operator[](cripts::string_view param);
void Erase(cripts::string_view param);
void Erase(std::initializer_list<cripts::string_view> list, bool keep = false);
void
Erase()
{
operator=("");
_size = 0;
}
void
Keep(std::initializer_list<cripts::string_view> list)
{
Erase(list, true);
}
void
Clear()
{
return Erase();
}
void
Sort()
{
// Make sure the hash and vector are populated
_parser();
std::ranges::sort(_ordered);
_modified = true;
}
void
Flush()
{
if (_modified) {
operator=(GetSV());
}
}
private:
void _parser();
bool _modified = false;
bool _standalone = false; // This component is used outside of a URL owner, not common
OrderedParams _ordered; // Ordered vector of all parameters, can be sorted etc.
HashParams _hashed; // Unordered map to go from "name" to the query parameter
cripts::string _storage; // Used when recombining the query params into a
// full query string
cripts::string::size_type _size = 0; // Mostly a guesttimate
}; // End class Url::Query
public:
Url() : scheme(this), host(this), port(this), path(this), query(this) {}
// Clear anything "cached" in the Url, this is rather draconian, but it's safe...
virtual void
Reset()
{
if (_bufp && _urlp) {
TSHandleMLocRelease(_bufp, TS_NULL_MLOC, _urlp);
_urlp = nullptr;
_bufp = nullptr;
query.Reset();
path.Reset();
}
_initialized = false;
_modified = false;
}
[[nodiscard]] bool
Initialized() const
{
return _initialized;
}
[[nodiscard]] bool
Modified() const
{
return _modified;
}
[[nodiscard]] TSMLoc
UrlP() const
{
return _urlp;
}
[[nodiscard]] virtual bool
ReadOnly() const
{
return false;
}
// Some of these URL objects needs the full Context, making life miserable.
void
set_context(cripts::Context *context)
{
_context = context;
}
// This is the full string for a URL, which needs allocations.
[[nodiscard]] cripts::string String();
Scheme scheme;
Host host;
Port port;
Path path;
Query query;
protected:
static void
_ensure_initialized(self_type *ptr)
{
if (!ptr->Initialized()) [[unlikely]] {
ptr->_initialize();
}
}
virtual void
_initialize()
{
_initialized = true;
_modified = false;
}
TSMBuffer _bufp = nullptr; // These two gets setup via initializing, to appropriate headers
TSMLoc _hdr_loc = nullptr; // Do not release any of this within the URL classes!
TSMLoc _urlp = nullptr; // This is owned by us.
cripts::Transaction *_state = nullptr; // Pointer into the owning Context's State
cripts::Context *_context = nullptr; // Pointer to the owning Context
bool _modified = false; // We have pending changes on the path/query components
bool _initialized = false; // Have we been initialized ?
}; // End class Url
// The Pristine URL is immutable, we should "delete" the operator= methods
namespace Pristine
{
class URL : public cripts::Url
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
static self_type &_get(cripts::Context *context);
[[nodiscard]] bool
ReadOnly() const override
{
return true;
}
protected:
void _initialize() override;
}; // End class Pristine::URL
} // namespace Pristine
namespace Client
{
class URL : public cripts::Url
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
// We must not release the bufp etc. since it comes from the RRI structure
// However, we still need to clear cached data in query and path components
void
Reset() override
{
query.Reset();
path.Reset();
_initialized = false;
_modified = false;
}
static self_type &_get(cripts::Context *context);
bool _update();
protected:
void _initialize() override;
}; // End class Client::URL
} // namespace Client
namespace Remap
{
namespace From
{
class URL : public cripts::Url
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
// We must not release the bufp etc. since it comes from the RRI structure
void
Reset() override
{
}
[[nodiscard]] bool
ReadOnly() const override
{
return true;
}
static self_type &_get(cripts::Context *context);
bool Update();
private:
void _initialize() override;
}; // End class Client::URL
} // namespace From
namespace To
{
class URL : public cripts::Url
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
// We must not release the bufp etc. since it comes from the RRI structure
void
Reset() override
{
}
[[nodiscard]] bool
ReadOnly() const override
{
return true;
}
static self_type &_get(cripts::Context *context);
bool Update();
private:
void _initialize() override;
}; // End class Client::URL
} // namespace To
} // namespace Remap
namespace Cache
{
class URL : public cripts::Url // ToDo: This can maybe be a subclass of Client::URL ?
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
static self_type &_get(cripts::Context *context);
bool _update();
protected:
void _initialize() override;
}; // End class Cache::URL
} // namespace Cache
namespace Parent
{
class URL : public cripts::Url
{
using super_type = cripts::Url;
using self_type = URL;
public:
URL() = default;
URL(const self_type &) = delete;
void operator=(const self_type &) = delete;
static self_type &_get(cripts::Context *context);
bool Update();
protected:
void _initialize() override;
}; // End class Cache::URL
} // namespace Parent
} // namespace cripts
// Formatters for {fmt}
namespace fmt
{
template <std::derived_from<cripts::Url> T> struct formatter<T> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(T &url, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", url.String());
}
};
template <> struct formatter<cripts::Url::Scheme> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Scheme &scheme, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", scheme.GetSV());
}
};
template <> struct formatter<cripts::Url::Host> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Host &host, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", host.GetSV());
}
};
template <> struct formatter<cripts::Url::Port> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Port &port, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", integer(port));
}
};
template <> struct formatter<cripts::Url::Path::String> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Path::String &path, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", path.GetSV());
}
};
template <> struct formatter<cripts::Url::Path> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Path &path, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", path.GetSV());
}
};
template <> struct formatter<cripts::Url::Query::Parameter> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Query::Parameter &param, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", param.GetSV());
}
};
template <> struct formatter<cripts::Url::Query> {
constexpr auto
parse(format_parse_context &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template <typename FormatContext>
auto
format(cripts::Url::Query &query, FormatContext &ctx) const -> decltype(ctx.out())
{
return fmt::format_to(ctx.out(), "{}", query.GetSV());
}
};
} // namespace fmt