| #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 |