blob: 8ea057be89e581fb607a50c5965fbe86a4a5c56b [file] [log] [blame]
#pragma once
#include <boost/callable_traits.hpp>
#include <boost/url/decode_view.hpp>
#include <boost/url/parse.hpp>
#include <boost/url/url.hpp>
#include <boost/url/url_view.hpp>
#include <nlohmann/json.hpp>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <ctime>
#include <functional>
#include <iomanip>
#include <limits>
#include <stdexcept>
#include <string>
#include <string_view>
#include <tuple>
#include <type_traits>
#include <utility>
#include <variant>
namespace crow
{
namespace black_magic
{
enum class TypeCode : std::uint8_t
{
Unspecified = 0,
Integer = 1,
UnsignedInteger = 2,
Float = 3,
String = 4,
Path = 5,
Max = 6,
};
// Remove when we have c++23
template <typename E>
constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
{
return static_cast<typename std::underlying_type<E>::type>(e);
}
template <typename T>
constexpr TypeCode getParameterTag()
{
if constexpr (std::is_same_v<int, T>)
{
return TypeCode::Integer;
}
if constexpr (std::is_same_v<char, T>)
{
return TypeCode::Integer;
}
if constexpr (std::is_same_v<int16_t, T>)
{
return TypeCode::Integer;
}
if constexpr (std::is_same_v<int32_t, T>)
{
return TypeCode::Integer;
}
if constexpr (std::is_same_v<int64_t, T>)
{
return TypeCode::Integer;
}
if constexpr (std::is_same_v<unsigned int, T>)
{
return TypeCode::UnsignedInteger;
}
if constexpr (std::is_same_v<unsigned char, T>)
{
return TypeCode::UnsignedInteger;
}
if constexpr (std::is_same_v<uint16_t, T>)
{
return TypeCode::UnsignedInteger;
}
if constexpr (std::is_same_v<uint32_t, T>)
{
return TypeCode::UnsignedInteger;
}
if constexpr (std::is_same_v<uint64_t, T>)
{
return TypeCode::UnsignedInteger;
}
if constexpr (std::is_same_v<double, T>)
{
return TypeCode::Float;
}
if constexpr (std::is_same_v<std::string, T>)
{
return TypeCode::String;
}
return TypeCode::Unspecified;
}
template <typename... Args>
struct computeParameterTagFromArgsList;
template <>
struct computeParameterTagFromArgsList<>
{
static constexpr int value = 0;
};
constexpr uint64_t getParameterTag(std::string_view url)
{
uint64_t tagValue = 0;
size_t urlSegmentIndex = std::string_view::npos;
size_t paramIndex = 0;
for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
{
char character = url[urlIndex];
if (character == '<')
{
if (urlSegmentIndex != std::string_view::npos)
{
return 0;
}
urlSegmentIndex = urlIndex;
}
if (character == '>')
{
if (urlSegmentIndex == std::string_view::npos)
{
return 0;
}
std::string_view tag =
url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
// Note, this is a really lame way to do std::pow(6, paramIndex)
// std::pow doesn't work in constexpr in clang.
// Ideally in the future we'd move this to use a power of 2 packing
// (probably 8 instead of 6) so that these just become bit shifts
uint64_t insertIndex = 1;
for (size_t unused = 0; unused < paramIndex; unused++)
{
insertIndex *= 6;
}
if (tag == "<int>")
{
tagValue += insertIndex * toUnderlying(TypeCode::Integer);
}
if (tag == "<uint>")
{
tagValue +=
insertIndex * toUnderlying(TypeCode::UnsignedInteger);
}
if (tag == "<float>" || tag == "<double>")
{
tagValue += insertIndex * toUnderlying(TypeCode::Float);
}
if (tag == "<str>" || tag == "<string>")
{
tagValue += insertIndex * toUnderlying(TypeCode::String);
}
if (tag == "<path>")
{
tagValue += insertIndex * toUnderlying(TypeCode::Path);
}
paramIndex++;
urlSegmentIndex = std::string_view::npos;
}
}
if (urlSegmentIndex != std::string_view::npos)
{
return 0;
}
return tagValue;
}
template <typename Arg, typename... Args>
struct computeParameterTagFromArgsList<Arg, Args...>
{
static constexpr int subValue =
computeParameterTagFromArgsList<Args...>::value;
static constexpr int value =
getParameterTag<typename std::decay<Arg>::type>() !=
TypeCode::Unspecified
? static_cast<uint64_t>(subValue *
toUnderlying(TypeCode::Max)) +
static_cast<uint64_t>(
getParameterTag<typename std::decay<Arg>::type>())
: subValue;
};
bool isParameterTagCompatible(uint64_t a, uint64_t b);
template <typename... T>
struct S
{
template <typename U>
using push = S<U, T...>;
template <typename U>
using push_back = S<T..., U>;
template <template <typename... Args> class U>
using rebind = U<T...>;
};
template <typename F, typename Set>
struct CallHelper;
template <typename F, typename... Args>
struct CallHelper<F, S<Args...>>
{
template <typename F1, typename... Args1,
typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
static char test(int);
template <typename...>
static int test(...);
static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
};
template <uint64_t N>
struct SingleTagToType
{};
template <>
struct SingleTagToType<1>
{
using type = int64_t;
};
template <>
struct SingleTagToType<2>
{
using type = uint64_t;
};
template <>
struct SingleTagToType<3>
{
using type = double;
};
template <>
struct SingleTagToType<4>
{
using type = std::string;
};
template <>
struct SingleTagToType<5>
{
using type = std::string;
};
template <uint64_t Tag>
struct Arguments
{
using subarguments = typename Arguments<Tag / 6>::type;
using type = typename subarguments::template push<
typename SingleTagToType<Tag % 6>::type>;
};
template <>
struct Arguments<0>
{
using type = S<>;
};
template <typename T>
struct Promote
{
using type = T;
};
template <typename T>
using PromoteT = typename Promote<T>::type;
template <>
struct Promote<char>
{
using type = int64_t;
};
template <>
struct Promote<int16_t>
{
using type = int64_t;
};
template <>
struct Promote<int>
{
using type = int64_t;
};
template <>
struct Promote<int64_t>
{
using type = int64_t;
};
template <>
struct Promote<unsigned char>
{
using type = uint64_t;
};
template <>
struct Promote<uint16_t>
{
using type = uint64_t;
};
template <>
struct Promote<unsigned int>
{
using type = uint64_t;
};
template <>
struct Promote<uint64_t>
{
using type = uint64_t;
};
} // namespace black_magic
namespace utility
{
template <typename T>
struct FunctionTraits
{
template <size_t i>
using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
};
std::string base64encode(std::string_view data);
// TODO this is temporary and should be deleted once base64 is refactored out of
// crow
bool base64Decode(std::string_view input, std::string& output);
bool constantTimeStringCompare(std::string_view a, std::string_view b);
struct ConstantTimeCompare
{
bool operator()(std::string_view a, std::string_view b) const
{
return constantTimeStringCompare(a, b);
}
};
namespace details
{
inline boost::urls::url
appendUrlPieces(boost::urls::url& url,
const std::initializer_list<std::string_view> args)
{
for (std::string_view arg : args)
{
url.segments().push_back(arg);
}
return url;
}
inline boost::urls::url
urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
{
boost::urls::url url("/");
appendUrlPieces(url, args);
return url;
}
} // namespace details
class OrMorePaths
{};
template <typename... AV>
inline boost::urls::url urlFromPieces(const AV... args)
{
return details::urlFromPiecesDetail({args...});
}
template <typename... AV>
inline void appendUrlPieces(boost::urls::url& url, const AV... args)
{
details::appendUrlPieces(url, {args...});
}
namespace details
{
// std::reference_wrapper<std::string> - extracts segment to variable
// std::string_view - checks if segment is equal to variable
using UrlSegment = std::variant<std::reference_wrapper<std::string>,
std::string_view, OrMorePaths>;
enum class UrlParseResult : uint8_t
{
Continue,
Fail,
Done,
};
class UrlSegmentMatcherVisitor
{
public:
UrlParseResult operator()(std::string& output)
{
output = segment;
return UrlParseResult::Continue;
}
UrlParseResult operator()(std::string_view expected)
{
if (segment == expected)
{
return UrlParseResult::Continue;
}
return UrlParseResult::Fail;
}
UrlParseResult operator()(OrMorePaths /*unused*/)
{
return UrlParseResult::Done;
}
explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
segment(segmentIn)
{}
private:
std::string_view segment;
};
bool readUrlSegments(boost::urls::url_view url, // NOLINT
std::initializer_list<UrlSegment>&& segments);
} // namespace details
template <typename... Args>
inline bool readUrlSegments(boost::urls::url_view url, Args&&... args) // NOLINT
{
return details::readUrlSegments(url, {std::forward<Args>(args)...});
}
boost::urls::url replaceUrlSegment(boost::urls::url_view urlView, // NOLINT
uint replaceLoc,
std::string_view newSegment);
std::string setProtocolDefaults(boost::urls::url_view urlView); // NOLINT
uint16_t setPortDefaults(boost::urls::url_view url); // NOLINT
bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
std::string& host, uint16_t& port, std::string& path);
} // namespace utility
} // namespace crow
namespace nlohmann
{
template <>
struct adl_serializer<boost::urls::url>
{
// nlohmann requires a specific casing to look these up in adl
// NOLINTNEXTLINE(readability-identifier-naming)
static void to_json(json& j, const boost::urls::url& url)
{
boost::urls::decode_view url_decoded{url};
j = std::string(url_decoded.begin(), url_decoded.end());
}
};
template <>
struct adl_serializer<boost::urls::url_view>
{
// NOLINTNEXTLINE(readability-identifier-naming, performance-unnecessary-value-param)
static void to_json(json& j, boost::urls::url_view url)
{
boost::urls::decode_view url_decoded{url};
j = std::string(url_decoded.begin(), url_decoded.end());
}
};
} // namespace nlohmann