xref: /openbmc/bmcweb/http/utility.hpp (revision c6bcedc6)
104e438cbSEd Tanous #pragma once
204e438cbSEd Tanous 
3eb1c47d3SEd Tanous #include <bmcweb_config.h>
404e438cbSEd Tanous #include <openssl/crypto.h>
504e438cbSEd Tanous 
6c867a83eSEd Tanous #include <boost/callable_traits.hpp>
7eae855c7SEd Tanous #include <boost/url/url.hpp>
871f2db75SEd Tanous #include <nlohmann/json.hpp>
91d8782e7SNan Zhou 
109ea15c35SEd Tanous #include <array>
1174849befSEd Tanous #include <chrono>
12c715ec29SEd Tanous #include <cstddef>
1304e438cbSEd Tanous #include <cstdint>
149ea15c35SEd Tanous #include <ctime>
1504e438cbSEd Tanous #include <functional>
169896eaedSEd Tanous #include <iomanip>
179ea15c35SEd Tanous #include <limits>
1804e438cbSEd Tanous #include <stdexcept>
1904e438cbSEd Tanous #include <string>
209ea15c35SEd Tanous #include <string_view>
2104e438cbSEd Tanous #include <tuple>
229ea15c35SEd Tanous #include <type_traits>
239ea15c35SEd Tanous #include <utility>
24ca1600c1SSzymon Dompke #include <variant>
2504e438cbSEd Tanous 
2604e438cbSEd Tanous namespace crow
2704e438cbSEd Tanous {
2804e438cbSEd Tanous namespace black_magic
2904e438cbSEd Tanous {
3004e438cbSEd Tanous 
31c715ec29SEd Tanous enum class TypeCode : uint8_t
32c715ec29SEd Tanous {
33c715ec29SEd Tanous     Unspecified = 0,
34c715ec29SEd Tanous     Integer = 1,
35c715ec29SEd Tanous     UnsignedInteger = 2,
36c715ec29SEd Tanous     Float = 3,
37c715ec29SEd Tanous     String = 4,
38c715ec29SEd Tanous     Path = 5,
39c715ec29SEd Tanous     Max = 6,
40c715ec29SEd Tanous };
41c715ec29SEd Tanous 
42c715ec29SEd Tanous // Remove when we have c++23
43c715ec29SEd Tanous template <typename E>
44c715ec29SEd Tanous constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
45c715ec29SEd Tanous {
46c715ec29SEd Tanous     return static_cast<typename std::underlying_type<E>::type>(e);
47c715ec29SEd Tanous }
48c715ec29SEd Tanous 
4904e438cbSEd Tanous template <typename T>
50c715ec29SEd Tanous constexpr TypeCode getParameterTag()
5104e438cbSEd Tanous {
5204e438cbSEd Tanous     if constexpr (std::is_same_v<int, T>)
5304e438cbSEd Tanous     {
54c715ec29SEd Tanous         return TypeCode::Integer;
5504e438cbSEd Tanous     }
5604e438cbSEd Tanous     if constexpr (std::is_same_v<char, T>)
5704e438cbSEd Tanous     {
58c715ec29SEd Tanous         return TypeCode::Integer;
5904e438cbSEd Tanous     }
6004e438cbSEd Tanous     if constexpr (std::is_same_v<short, T>)
6104e438cbSEd Tanous     {
62c715ec29SEd Tanous         return TypeCode::Integer;
6304e438cbSEd Tanous     }
6404e438cbSEd Tanous     if constexpr (std::is_same_v<long, T>)
6504e438cbSEd Tanous     {
66c715ec29SEd Tanous         return TypeCode::Integer;
6704e438cbSEd Tanous     }
6804e438cbSEd Tanous     if constexpr (std::is_same_v<long long, T>)
6904e438cbSEd Tanous     {
70c715ec29SEd Tanous         return TypeCode::Integer;
7104e438cbSEd Tanous     }
7204e438cbSEd Tanous     if constexpr (std::is_same_v<unsigned int, T>)
7304e438cbSEd Tanous     {
74c715ec29SEd Tanous         return TypeCode::UnsignedInteger;
7504e438cbSEd Tanous     }
7604e438cbSEd Tanous     if constexpr (std::is_same_v<unsigned char, T>)
7704e438cbSEd Tanous     {
78c715ec29SEd Tanous         return TypeCode::UnsignedInteger;
7904e438cbSEd Tanous     }
8004e438cbSEd Tanous     if constexpr (std::is_same_v<unsigned short, T>)
8104e438cbSEd Tanous     {
82c715ec29SEd Tanous         return TypeCode::UnsignedInteger;
8304e438cbSEd Tanous     }
8404e438cbSEd Tanous     if constexpr (std::is_same_v<unsigned long, T>)
8504e438cbSEd Tanous     {
86c715ec29SEd Tanous         return TypeCode::UnsignedInteger;
8704e438cbSEd Tanous     }
8804e438cbSEd Tanous     if constexpr (std::is_same_v<unsigned long long, T>)
8904e438cbSEd Tanous     {
90c715ec29SEd Tanous         return TypeCode::UnsignedInteger;
9104e438cbSEd Tanous     }
9204e438cbSEd Tanous     if constexpr (std::is_same_v<double, T>)
9304e438cbSEd Tanous     {
94c715ec29SEd Tanous         return TypeCode::Float;
9504e438cbSEd Tanous     }
9604e438cbSEd Tanous     if constexpr (std::is_same_v<std::string, T>)
9704e438cbSEd Tanous     {
98c715ec29SEd Tanous         return TypeCode::String;
9904e438cbSEd Tanous     }
100c715ec29SEd Tanous     return TypeCode::Unspecified;
10104e438cbSEd Tanous }
10204e438cbSEd Tanous 
10304e438cbSEd Tanous template <typename... Args>
10404e438cbSEd Tanous struct computeParameterTagFromArgsList;
10504e438cbSEd Tanous 
10604e438cbSEd Tanous template <>
10704e438cbSEd Tanous struct computeParameterTagFromArgsList<>
10804e438cbSEd Tanous {
10904e438cbSEd Tanous     static constexpr int value = 0;
11004e438cbSEd Tanous };
11104e438cbSEd Tanous 
11204e438cbSEd Tanous template <typename Arg, typename... Args>
11304e438cbSEd Tanous struct computeParameterTagFromArgsList<Arg, Args...>
11404e438cbSEd Tanous {
11504e438cbSEd Tanous     static constexpr int subValue =
11604e438cbSEd Tanous         computeParameterTagFromArgsList<Args...>::value;
11704e438cbSEd Tanous     static constexpr int value =
118c715ec29SEd Tanous         getParameterTag<typename std::decay<Arg>::type>() !=
119c715ec29SEd Tanous                 TypeCode::Unspecified
120c715ec29SEd Tanous             ? static_cast<unsigned long>(subValue *
121c715ec29SEd Tanous                                          toUnderlying(TypeCode::Max)) +
122c715ec29SEd Tanous                   static_cast<uint64_t>(
123c715ec29SEd Tanous                       getParameterTag<typename std::decay<Arg>::type>())
12404e438cbSEd Tanous             : subValue;
12504e438cbSEd Tanous };
12604e438cbSEd Tanous 
12704e438cbSEd Tanous inline bool isParameterTagCompatible(uint64_t a, uint64_t b)
12804e438cbSEd Tanous {
1291c30e500SEd Tanous     while (true)
1301c30e500SEd Tanous     {
131ef641b65SEd Tanous         if (a == 0 && b == 0)
13204e438cbSEd Tanous         {
133ef641b65SEd Tanous             // Both tags were equivalent, parameters are compatible
134ef641b65SEd Tanous             return true;
13504e438cbSEd Tanous         }
136ef641b65SEd Tanous         if (a == 0 || b == 0)
13704e438cbSEd Tanous         {
138ef641b65SEd Tanous             // one of the tags had more parameters than the other
139ef641b65SEd Tanous             return false;
14004e438cbSEd Tanous         }
141c715ec29SEd Tanous         TypeCode sa = static_cast<TypeCode>(a % toUnderlying(TypeCode::Max));
142c715ec29SEd Tanous         TypeCode sb = static_cast<TypeCode>(b % toUnderlying(TypeCode::Max));
143c715ec29SEd Tanous 
144c715ec29SEd Tanous         if (sa == TypeCode::Path)
14504e438cbSEd Tanous         {
146c715ec29SEd Tanous             sa = TypeCode::String;
14704e438cbSEd Tanous         }
148c715ec29SEd Tanous         if (sb == TypeCode::Path)
14904e438cbSEd Tanous         {
150c715ec29SEd Tanous             sb = TypeCode::String;
15104e438cbSEd Tanous         }
15204e438cbSEd Tanous         if (sa != sb)
15304e438cbSEd Tanous         {
15404e438cbSEd Tanous             return false;
15504e438cbSEd Tanous         }
156c715ec29SEd Tanous         a /= toUnderlying(TypeCode::Max);
157c715ec29SEd Tanous         b /= toUnderlying(TypeCode::Max);
1581c30e500SEd Tanous     }
1591c30e500SEd Tanous     return false;
16004e438cbSEd Tanous }
16104e438cbSEd Tanous 
1621c30e500SEd Tanous constexpr inline uint64_t getParameterTag(std::string_view url)
16304e438cbSEd Tanous {
1641c30e500SEd Tanous     uint64_t tagValue = 0;
1651c30e500SEd Tanous     size_t urlSegmentIndex = std::string_view::npos;
166b00dcc27SEd Tanous 
1671c30e500SEd Tanous     size_t paramIndex = 0;
1681c30e500SEd Tanous 
1691c30e500SEd Tanous     for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
1701c30e500SEd Tanous     {
1711c30e500SEd Tanous         char character = url[urlIndex];
1721c30e500SEd Tanous         if (character == '<')
1731c30e500SEd Tanous         {
1741c30e500SEd Tanous             if (urlSegmentIndex != std::string_view::npos)
17504e438cbSEd Tanous             {
17604e438cbSEd Tanous                 return 0;
17704e438cbSEd Tanous             }
1781c30e500SEd Tanous             urlSegmentIndex = urlIndex;
1791c30e500SEd Tanous         }
1801c30e500SEd Tanous         if (character == '>')
18104e438cbSEd Tanous         {
1821c30e500SEd Tanous             if (urlSegmentIndex == std::string_view::npos)
1831c30e500SEd Tanous             {
1841c30e500SEd Tanous                 return 0;
1851c30e500SEd Tanous             }
1861c30e500SEd Tanous             std::string_view tag =
1871c30e500SEd Tanous                 url.substr(urlSegmentIndex, urlIndex + 1 - urlSegmentIndex);
1881c30e500SEd Tanous 
1891c30e500SEd Tanous             // Note, this is a really lame way to do std::pow(6, paramIndex)
1901c30e500SEd Tanous             // std::pow doesn't work in constexpr in clang.
1911c30e500SEd Tanous             // Ideally in the future we'd move this to use a power of 2 packing
1921c30e500SEd Tanous             // (probably 8 instead of 6) so that these just become bit shifts
1931c30e500SEd Tanous             uint64_t insertIndex = 1;
1941c30e500SEd Tanous             for (size_t unused = 0; unused < paramIndex; unused++)
1951c30e500SEd Tanous             {
1961c30e500SEd Tanous                 insertIndex *= 6;
19704e438cbSEd Tanous             }
19804e438cbSEd Tanous 
1991c30e500SEd Tanous             if (tag == "<int>")
20004e438cbSEd Tanous             {
201c715ec29SEd Tanous                 tagValue += insertIndex * toUnderlying(TypeCode::Integer);
20204e438cbSEd Tanous             }
2031c30e500SEd Tanous             if (tag == "<uint>")
20404e438cbSEd Tanous             {
205c715ec29SEd Tanous                 tagValue +=
206c715ec29SEd Tanous                     insertIndex * toUnderlying(TypeCode::UnsignedInteger);
20704e438cbSEd Tanous             }
2081c30e500SEd Tanous             if (tag == "<float>" || tag == "<double>")
20904e438cbSEd Tanous             {
210c715ec29SEd Tanous                 tagValue += insertIndex * toUnderlying(TypeCode::Float);
21104e438cbSEd Tanous             }
2121c30e500SEd Tanous             if (tag == "<str>" || tag == "<string>")
21304e438cbSEd Tanous             {
214c715ec29SEd Tanous                 tagValue += insertIndex * toUnderlying(TypeCode::String);
21504e438cbSEd Tanous             }
2161c30e500SEd Tanous             if (tag == "<path>")
21704e438cbSEd Tanous             {
218c715ec29SEd Tanous                 tagValue += insertIndex * toUnderlying(TypeCode::Path);
21904e438cbSEd Tanous             }
2201c30e500SEd Tanous             paramIndex++;
2211c30e500SEd Tanous             urlSegmentIndex = std::string_view::npos;
2221c30e500SEd Tanous         }
2231c30e500SEd Tanous     }
2241c30e500SEd Tanous     if (urlSegmentIndex != std::string_view::npos)
2251c30e500SEd Tanous     {
2261c30e500SEd Tanous         return 0;
2271c30e500SEd Tanous     }
2281c30e500SEd Tanous     return tagValue;
22904e438cbSEd Tanous }
23004e438cbSEd Tanous 
23104e438cbSEd Tanous template <typename... T>
23204e438cbSEd Tanous struct S
23304e438cbSEd Tanous {
23404e438cbSEd Tanous     template <typename U>
23504e438cbSEd Tanous     using push = S<U, T...>;
23604e438cbSEd Tanous     template <typename U>
23704e438cbSEd Tanous     using push_back = S<T..., U>;
23804e438cbSEd Tanous     template <template <typename... Args> class U>
23904e438cbSEd Tanous     using rebind = U<T...>;
24004e438cbSEd Tanous };
24104e438cbSEd Tanous 
24204e438cbSEd Tanous template <typename F, typename Set>
24304e438cbSEd Tanous struct CallHelper;
24404e438cbSEd Tanous 
24504e438cbSEd Tanous template <typename F, typename... Args>
24604e438cbSEd Tanous struct CallHelper<F, S<Args...>>
24704e438cbSEd Tanous {
24804e438cbSEd Tanous     template <typename F1, typename... Args1,
24904e438cbSEd Tanous               typename = decltype(std::declval<F1>()(std::declval<Args1>()...))>
25004e438cbSEd Tanous     static char test(int);
25104e438cbSEd Tanous 
25204e438cbSEd Tanous     template <typename...>
25304e438cbSEd Tanous     static int test(...);
25404e438cbSEd Tanous 
25504e438cbSEd Tanous     static constexpr bool value = sizeof(test<F, Args...>(0)) == sizeof(char);
25604e438cbSEd Tanous };
25704e438cbSEd Tanous 
25804e438cbSEd Tanous template <uint64_t N>
25904e438cbSEd Tanous struct SingleTagToType
26004e438cbSEd Tanous {};
26104e438cbSEd Tanous 
26204e438cbSEd Tanous template <>
26304e438cbSEd Tanous struct SingleTagToType<1>
26404e438cbSEd Tanous {
26504e438cbSEd Tanous     using type = int64_t;
26604e438cbSEd Tanous };
26704e438cbSEd Tanous 
26804e438cbSEd Tanous template <>
26904e438cbSEd Tanous struct SingleTagToType<2>
27004e438cbSEd Tanous {
27104e438cbSEd Tanous     using type = uint64_t;
27204e438cbSEd Tanous };
27304e438cbSEd Tanous 
27404e438cbSEd Tanous template <>
27504e438cbSEd Tanous struct SingleTagToType<3>
27604e438cbSEd Tanous {
27704e438cbSEd Tanous     using type = double;
27804e438cbSEd Tanous };
27904e438cbSEd Tanous 
28004e438cbSEd Tanous template <>
28104e438cbSEd Tanous struct SingleTagToType<4>
28204e438cbSEd Tanous {
28304e438cbSEd Tanous     using type = std::string;
28404e438cbSEd Tanous };
28504e438cbSEd Tanous 
28604e438cbSEd Tanous template <>
28704e438cbSEd Tanous struct SingleTagToType<5>
28804e438cbSEd Tanous {
28904e438cbSEd Tanous     using type = std::string;
29004e438cbSEd Tanous };
29104e438cbSEd Tanous 
29204e438cbSEd Tanous template <uint64_t Tag>
29304e438cbSEd Tanous struct Arguments
29404e438cbSEd Tanous {
29504e438cbSEd Tanous     using subarguments = typename Arguments<Tag / 6>::type;
29604e438cbSEd Tanous     using type = typename subarguments::template push<
29704e438cbSEd Tanous         typename SingleTagToType<Tag % 6>::type>;
29804e438cbSEd Tanous };
29904e438cbSEd Tanous 
30004e438cbSEd Tanous template <>
30104e438cbSEd Tanous struct Arguments<0>
30204e438cbSEd Tanous {
30304e438cbSEd Tanous     using type = S<>;
30404e438cbSEd Tanous };
30504e438cbSEd Tanous 
30604e438cbSEd Tanous template <typename T>
30704e438cbSEd Tanous struct Promote
30804e438cbSEd Tanous {
30904e438cbSEd Tanous     using type = T;
31004e438cbSEd Tanous };
31104e438cbSEd Tanous 
31204e438cbSEd Tanous template <typename T>
31304e438cbSEd Tanous using PromoteT = typename Promote<T>::type;
31404e438cbSEd Tanous 
31504e438cbSEd Tanous template <>
31604e438cbSEd Tanous struct Promote<char>
31704e438cbSEd Tanous {
31804e438cbSEd Tanous     using type = int64_t;
31904e438cbSEd Tanous };
32004e438cbSEd Tanous template <>
32104e438cbSEd Tanous struct Promote<short>
32204e438cbSEd Tanous {
32304e438cbSEd Tanous     using type = int64_t;
32404e438cbSEd Tanous };
32504e438cbSEd Tanous template <>
32604e438cbSEd Tanous struct Promote<int>
32704e438cbSEd Tanous {
32804e438cbSEd Tanous     using type = int64_t;
32904e438cbSEd Tanous };
33004e438cbSEd Tanous template <>
33104e438cbSEd Tanous struct Promote<long>
33204e438cbSEd Tanous {
33304e438cbSEd Tanous     using type = int64_t;
33404e438cbSEd Tanous };
33504e438cbSEd Tanous template <>
33604e438cbSEd Tanous struct Promote<long long>
33704e438cbSEd Tanous {
33804e438cbSEd Tanous     using type = int64_t;
33904e438cbSEd Tanous };
34004e438cbSEd Tanous template <>
34104e438cbSEd Tanous struct Promote<unsigned char>
34204e438cbSEd Tanous {
34304e438cbSEd Tanous     using type = uint64_t;
34404e438cbSEd Tanous };
34504e438cbSEd Tanous template <>
34604e438cbSEd Tanous struct Promote<unsigned short>
34704e438cbSEd Tanous {
34804e438cbSEd Tanous     using type = uint64_t;
34904e438cbSEd Tanous };
35004e438cbSEd Tanous template <>
35104e438cbSEd Tanous struct Promote<unsigned int>
35204e438cbSEd Tanous {
35304e438cbSEd Tanous     using type = uint64_t;
35404e438cbSEd Tanous };
35504e438cbSEd Tanous template <>
35604e438cbSEd Tanous struct Promote<unsigned long>
35704e438cbSEd Tanous {
35804e438cbSEd Tanous     using type = uint64_t;
35904e438cbSEd Tanous };
36004e438cbSEd Tanous template <>
36104e438cbSEd Tanous struct Promote<unsigned long long>
36204e438cbSEd Tanous {
36304e438cbSEd Tanous     using type = uint64_t;
36404e438cbSEd Tanous };
36504e438cbSEd Tanous 
36604e438cbSEd Tanous } // namespace black_magic
36704e438cbSEd Tanous 
36804e438cbSEd Tanous namespace utility
36904e438cbSEd Tanous {
37004e438cbSEd Tanous 
37104e438cbSEd Tanous template <typename T>
372c867a83eSEd Tanous struct FunctionTraits
37304e438cbSEd Tanous {
37404e438cbSEd Tanous     template <size_t i>
375c867a83eSEd Tanous     using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
37604e438cbSEd Tanous };
37704e438cbSEd Tanous 
378d830ff5aSAdriana Kobylak inline std::string base64encode(const std::string_view data)
379d830ff5aSAdriana Kobylak {
380d830ff5aSAdriana Kobylak     const std::array<char, 64> key = {
381d830ff5aSAdriana Kobylak         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
382d830ff5aSAdriana Kobylak         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
383d830ff5aSAdriana Kobylak         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
384d830ff5aSAdriana Kobylak         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
385d830ff5aSAdriana Kobylak         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
386d830ff5aSAdriana Kobylak 
387d830ff5aSAdriana Kobylak     size_t size = data.size();
388d830ff5aSAdriana Kobylak     std::string ret;
389d830ff5aSAdriana Kobylak     ret.resize((size + 2) / 3 * 4);
390d830ff5aSAdriana Kobylak     auto it = ret.begin();
391d830ff5aSAdriana Kobylak 
392d830ff5aSAdriana Kobylak     size_t i = 0;
393d830ff5aSAdriana Kobylak     while (i < size)
394d830ff5aSAdriana Kobylak     {
395543f4400SEd Tanous         size_t keyIndex = 0;
396d830ff5aSAdriana Kobylak 
397d830ff5aSAdriana Kobylak         keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
398d830ff5aSAdriana Kobylak         *it++ = key[keyIndex];
399d830ff5aSAdriana Kobylak 
400d830ff5aSAdriana Kobylak         if (i + 1 < size)
401d830ff5aSAdriana Kobylak         {
402d830ff5aSAdriana Kobylak             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
403d830ff5aSAdriana Kobylak             keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
404d830ff5aSAdriana Kobylak             *it++ = key[keyIndex];
405d830ff5aSAdriana Kobylak 
406d830ff5aSAdriana Kobylak             if (i + 2 < size)
407d830ff5aSAdriana Kobylak             {
408d830ff5aSAdriana Kobylak                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
409d830ff5aSAdriana Kobylak                 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
410d830ff5aSAdriana Kobylak                 *it++ = key[keyIndex];
411d830ff5aSAdriana Kobylak 
412d830ff5aSAdriana Kobylak                 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
413d830ff5aSAdriana Kobylak                 *it++ = key[keyIndex];
414d830ff5aSAdriana Kobylak             }
415d830ff5aSAdriana Kobylak             else
416d830ff5aSAdriana Kobylak             {
417d830ff5aSAdriana Kobylak                 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
418d830ff5aSAdriana Kobylak                 *it++ = key[keyIndex];
419d830ff5aSAdriana Kobylak                 *it++ = '=';
420d830ff5aSAdriana Kobylak             }
421d830ff5aSAdriana Kobylak         }
422d830ff5aSAdriana Kobylak         else
423d830ff5aSAdriana Kobylak         {
424d830ff5aSAdriana Kobylak             keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
425d830ff5aSAdriana Kobylak             *it++ = key[keyIndex];
426d830ff5aSAdriana Kobylak             *it++ = '=';
427d830ff5aSAdriana Kobylak             *it++ = '=';
428d830ff5aSAdriana Kobylak         }
429d830ff5aSAdriana Kobylak 
430d830ff5aSAdriana Kobylak         i += 3;
431d830ff5aSAdriana Kobylak     }
432d830ff5aSAdriana Kobylak 
433d830ff5aSAdriana Kobylak     return ret;
434d830ff5aSAdriana Kobylak }
435d830ff5aSAdriana Kobylak 
43604e438cbSEd Tanous // TODO this is temporary and should be deleted once base64 is refactored out of
43704e438cbSEd Tanous // crow
43804e438cbSEd Tanous inline bool base64Decode(const std::string_view input, std::string& output)
43904e438cbSEd Tanous {
44004e438cbSEd Tanous     static const char nop = static_cast<char>(-1);
44104e438cbSEd Tanous     // See note on encoding_data[] in above function
44204e438cbSEd Tanous     static const std::array<char, 256> decodingData = {
44304e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
44404e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
44504e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
44604e438cbSEd Tanous         nop, 62,  nop, nop, nop, 63,  52,  53,  54,  55,  56,  57,  58,  59,
44704e438cbSEd Tanous         60,  61,  nop, nop, nop, nop, nop, nop, nop, 0,   1,   2,   3,   4,
44804e438cbSEd Tanous         5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,
44904e438cbSEd Tanous         19,  20,  21,  22,  23,  24,  25,  nop, nop, nop, nop, nop, nop, 26,
45004e438cbSEd Tanous         27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
45104e438cbSEd Tanous         41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  nop, nop, nop,
45204e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45304e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45404e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45504e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45604e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45704e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45804e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
45904e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
46004e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
46104e438cbSEd Tanous         nop, nop, nop, nop};
46204e438cbSEd Tanous 
46304e438cbSEd Tanous     size_t inputLength = input.size();
46404e438cbSEd Tanous 
46504e438cbSEd Tanous     // allocate space for output string
46604e438cbSEd Tanous     output.clear();
46704e438cbSEd Tanous     output.reserve(((inputLength + 2) / 3) * 4);
46804e438cbSEd Tanous 
46904e438cbSEd Tanous     auto getCodeValue = [](char c) {
47004e438cbSEd Tanous         auto code = static_cast<unsigned char>(c);
47104e438cbSEd Tanous         // Ensure we cannot index outside the bounds of the decoding array
47204e438cbSEd Tanous         static_assert(std::numeric_limits<decltype(code)>::max() <
47304e438cbSEd Tanous                       decodingData.size());
47404e438cbSEd Tanous         return decodingData[code];
47504e438cbSEd Tanous     };
47604e438cbSEd Tanous 
47704e438cbSEd Tanous     // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
47804e438cbSEd Tanous     // dropping first two bits
47904e438cbSEd Tanous     // and regenerate into 3 8-bits sequences
48004e438cbSEd Tanous 
48104e438cbSEd Tanous     for (size_t i = 0; i < inputLength; i++)
48204e438cbSEd Tanous     {
483543f4400SEd Tanous         char base64code0 = 0;
484543f4400SEd Tanous         char base64code1 = 0;
48504e438cbSEd Tanous         char base64code2 = 0; // initialized to 0 to suppress warnings
486543f4400SEd Tanous         char base64code3 = 0;
48704e438cbSEd Tanous 
48804e438cbSEd Tanous         base64code0 = getCodeValue(input[i]);
48904e438cbSEd Tanous         if (base64code0 == nop)
49004e438cbSEd Tanous         { // non base64 character
49104e438cbSEd Tanous             return false;
49204e438cbSEd Tanous         }
49304e438cbSEd Tanous         if (!(++i < inputLength))
49404e438cbSEd Tanous         { // we need at least two input bytes for first
49504e438cbSEd Tanous           // byte output
49604e438cbSEd Tanous             return false;
49704e438cbSEd Tanous         }
49804e438cbSEd Tanous         base64code1 = getCodeValue(input[i]);
49904e438cbSEd Tanous         if (base64code1 == nop)
50004e438cbSEd Tanous         { // non base64 character
50104e438cbSEd Tanous             return false;
50204e438cbSEd Tanous         }
50304e438cbSEd Tanous         output +=
50404e438cbSEd Tanous             static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
50504e438cbSEd Tanous 
50604e438cbSEd Tanous         if (++i < inputLength)
50704e438cbSEd Tanous         {
50804e438cbSEd Tanous             char c = input[i];
50904e438cbSEd Tanous             if (c == '=')
51004e438cbSEd Tanous             { // padding , end of input
51104e438cbSEd Tanous                 return (base64code1 & 0x0f) == 0;
51204e438cbSEd Tanous             }
51304e438cbSEd Tanous             base64code2 = getCodeValue(input[i]);
51404e438cbSEd Tanous             if (base64code2 == nop)
51504e438cbSEd Tanous             { // non base64 character
51604e438cbSEd Tanous                 return false;
51704e438cbSEd Tanous             }
51804e438cbSEd Tanous             output += static_cast<char>(((base64code1 << 4) & 0xf0) |
51904e438cbSEd Tanous                                         ((base64code2 >> 2) & 0x0f));
52004e438cbSEd Tanous         }
52104e438cbSEd Tanous 
52204e438cbSEd Tanous         if (++i < inputLength)
52304e438cbSEd Tanous         {
52404e438cbSEd Tanous             char c = input[i];
52504e438cbSEd Tanous             if (c == '=')
52604e438cbSEd Tanous             { // padding , end of input
52704e438cbSEd Tanous                 return (base64code2 & 0x03) == 0;
52804e438cbSEd Tanous             }
52904e438cbSEd Tanous             base64code3 = getCodeValue(input[i]);
53004e438cbSEd Tanous             if (base64code3 == nop)
53104e438cbSEd Tanous             { // non base64 character
53204e438cbSEd Tanous                 return false;
53304e438cbSEd Tanous             }
53404e438cbSEd Tanous             output +=
53504e438cbSEd Tanous                 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
53604e438cbSEd Tanous         }
53704e438cbSEd Tanous     }
53804e438cbSEd Tanous 
53904e438cbSEd Tanous     return true;
54004e438cbSEd Tanous }
54104e438cbSEd Tanous 
54204e438cbSEd Tanous inline bool constantTimeStringCompare(const std::string_view a,
54304e438cbSEd Tanous                                       const std::string_view b)
54404e438cbSEd Tanous {
54504e438cbSEd Tanous     // Important note, this function is ONLY constant time if the two input
54604e438cbSEd Tanous     // sizes are the same
54704e438cbSEd Tanous     if (a.size() != b.size())
54804e438cbSEd Tanous     {
54904e438cbSEd Tanous         return false;
55004e438cbSEd Tanous     }
55104e438cbSEd Tanous     return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
55204e438cbSEd Tanous }
55304e438cbSEd Tanous 
55404e438cbSEd Tanous struct ConstantTimeCompare
55504e438cbSEd Tanous {
55604e438cbSEd Tanous     bool operator()(const std::string_view a, const std::string_view b) const
55704e438cbSEd Tanous     {
55804e438cbSEd Tanous         return constantTimeStringCompare(a, b);
55904e438cbSEd Tanous     }
56004e438cbSEd Tanous };
56104e438cbSEd Tanous 
562eae855c7SEd Tanous namespace details
563eae855c7SEd Tanous {
564eae855c7SEd Tanous inline boost::urls::url
565*c6bcedc6SWilly Tu     appendUrlPieces(boost::urls::url& url,
566*c6bcedc6SWilly Tu                     const std::initializer_list<std::string_view> args)
567eae855c7SEd Tanous {
568eae855c7SEd Tanous     for (const std::string_view& arg : args)
569eae855c7SEd Tanous     {
570eae855c7SEd Tanous         url.segments().push_back(arg);
571eae855c7SEd Tanous     }
572eae855c7SEd Tanous     return url;
573eae855c7SEd Tanous }
574*c6bcedc6SWilly Tu 
575*c6bcedc6SWilly Tu inline boost::urls::url
576*c6bcedc6SWilly Tu     urlFromPiecesDetail(const std::initializer_list<std::string_view> args)
577*c6bcedc6SWilly Tu {
578*c6bcedc6SWilly Tu     boost::urls::url url("/");
579*c6bcedc6SWilly Tu     appendUrlPieces(url, args);
580*c6bcedc6SWilly Tu     return url;
581*c6bcedc6SWilly Tu }
582eae855c7SEd Tanous } // namespace details
583eae855c7SEd Tanous 
5847f8d8fa9SEd Tanous class OrMorePaths
5857f8d8fa9SEd Tanous {};
5867f8d8fa9SEd Tanous 
587eae855c7SEd Tanous template <typename... AV>
588eae855c7SEd Tanous inline boost::urls::url urlFromPieces(const AV... args)
589eae855c7SEd Tanous {
590eae855c7SEd Tanous     return details::urlFromPiecesDetail({args...});
591eae855c7SEd Tanous }
592eae855c7SEd Tanous 
593*c6bcedc6SWilly Tu template <typename... AV>
594*c6bcedc6SWilly Tu inline void appendUrlPieces(boost::urls::url& url, const AV... args)
595*c6bcedc6SWilly Tu {
596*c6bcedc6SWilly Tu     details::appendUrlPieces(url, {args...});
597*c6bcedc6SWilly Tu }
598*c6bcedc6SWilly Tu 
599ca1600c1SSzymon Dompke namespace details
600ca1600c1SSzymon Dompke {
601ca1600c1SSzymon Dompke 
602ca1600c1SSzymon Dompke // std::reference_wrapper<std::string> - extracts segment to variable
603ca1600c1SSzymon Dompke //                    std::string_view - checks if segment is equal to variable
6047f8d8fa9SEd Tanous using UrlSegment = std::variant<std::reference_wrapper<std::string>,
6057f8d8fa9SEd Tanous                                 std::string_view, OrMorePaths>;
6067f8d8fa9SEd Tanous 
6077f8d8fa9SEd Tanous enum class UrlParseResult
6087f8d8fa9SEd Tanous {
6097f8d8fa9SEd Tanous     Continue,
6107f8d8fa9SEd Tanous     Fail,
6117f8d8fa9SEd Tanous     Done,
6127f8d8fa9SEd Tanous };
613ca1600c1SSzymon Dompke 
614ca1600c1SSzymon Dompke class UrlSegmentMatcherVisitor
615ca1600c1SSzymon Dompke {
616ca1600c1SSzymon Dompke   public:
6177f8d8fa9SEd Tanous     UrlParseResult operator()(std::string& output)
618ca1600c1SSzymon Dompke     {
619ca1600c1SSzymon Dompke         output = std::string_view(segment.data(), segment.size());
6207f8d8fa9SEd Tanous         return UrlParseResult::Continue;
621ca1600c1SSzymon Dompke     }
622ca1600c1SSzymon Dompke 
6237f8d8fa9SEd Tanous     UrlParseResult operator()(std::string_view expected)
624ca1600c1SSzymon Dompke     {
6257f8d8fa9SEd Tanous         if (std::string_view(segment.data(), segment.size()) == expected)
6267f8d8fa9SEd Tanous         {
6277f8d8fa9SEd Tanous             return UrlParseResult::Continue;
6287f8d8fa9SEd Tanous         }
6297f8d8fa9SEd Tanous         return UrlParseResult::Fail;
6307f8d8fa9SEd Tanous     }
6317f8d8fa9SEd Tanous 
6327f8d8fa9SEd Tanous     UrlParseResult operator()(OrMorePaths /*unused*/)
6337f8d8fa9SEd Tanous     {
6347f8d8fa9SEd Tanous         return UrlParseResult::Done;
635ca1600c1SSzymon Dompke     }
636ca1600c1SSzymon Dompke 
6374e23a444SEd Tanous     explicit UrlSegmentMatcherVisitor(
6384e23a444SEd Tanous         const boost::urls::string_value& segmentIn) :
639ca1600c1SSzymon Dompke         segment(segmentIn)
640ca1600c1SSzymon Dompke     {}
641ca1600c1SSzymon Dompke 
642ca1600c1SSzymon Dompke   private:
643ca1600c1SSzymon Dompke     const boost::urls::string_value& segment;
644ca1600c1SSzymon Dompke };
645ca1600c1SSzymon Dompke 
646ca1600c1SSzymon Dompke inline bool readUrlSegments(const boost::urls::url_view& urlView,
647ca1600c1SSzymon Dompke                             std::initializer_list<UrlSegment>&& segments)
648ca1600c1SSzymon Dompke {
649ca1600c1SSzymon Dompke     const boost::urls::segments_view& urlSegments = urlView.segments();
650ca1600c1SSzymon Dompke 
6517f8d8fa9SEd Tanous     if (!urlSegments.is_absolute())
652ca1600c1SSzymon Dompke     {
653ca1600c1SSzymon Dompke         return false;
654ca1600c1SSzymon Dompke     }
655ca1600c1SSzymon Dompke 
656ca1600c1SSzymon Dompke     boost::urls::segments_view::iterator it = urlSegments.begin();
657ca1600c1SSzymon Dompke     boost::urls::segments_view::iterator end = urlSegments.end();
658ca1600c1SSzymon Dompke 
659ca1600c1SSzymon Dompke     for (const auto& segment : segments)
660ca1600c1SSzymon Dompke     {
6617f8d8fa9SEd Tanous         if (it == end)
6627f8d8fa9SEd Tanous         {
6637f8d8fa9SEd Tanous             // If the request ends with an "any" path, this was successful
6647f8d8fa9SEd Tanous             return std::holds_alternative<OrMorePaths>(segment);
6657f8d8fa9SEd Tanous         }
6667f8d8fa9SEd Tanous         UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
6677f8d8fa9SEd Tanous         if (res == UrlParseResult::Done)
6687f8d8fa9SEd Tanous         {
6697f8d8fa9SEd Tanous             return true;
6707f8d8fa9SEd Tanous         }
6717f8d8fa9SEd Tanous         if (res == UrlParseResult::Fail)
672ca1600c1SSzymon Dompke         {
673ca1600c1SSzymon Dompke             return false;
674ca1600c1SSzymon Dompke         }
675ca1600c1SSzymon Dompke         it++;
676ca1600c1SSzymon Dompke     }
6774c30e226SCarson Labrado 
6784c30e226SCarson Labrado     // There will be an empty segment at the end if the URI ends with a "/"
6794c30e226SCarson Labrado     // e.g. /redfish/v1/Chassis/
6804c30e226SCarson Labrado     if ((it != end) && urlSegments.back().empty())
6814c30e226SCarson Labrado     {
6824c30e226SCarson Labrado         it++;
6834c30e226SCarson Labrado     }
6847f8d8fa9SEd Tanous     return it == end;
685ca1600c1SSzymon Dompke }
686ca1600c1SSzymon Dompke 
687ca1600c1SSzymon Dompke } // namespace details
688ca1600c1SSzymon Dompke 
689ca1600c1SSzymon Dompke template <typename... Args>
690ca1600c1SSzymon Dompke inline bool readUrlSegments(const boost::urls::url_view& urlView,
691ca1600c1SSzymon Dompke                             Args&&... args)
692ca1600c1SSzymon Dompke {
693ca1600c1SSzymon Dompke     return details::readUrlSegments(urlView, {std::forward<Args>(args)...});
694ca1600c1SSzymon Dompke }
695ca1600c1SSzymon Dompke 
6961c0bb5c6SCarson Labrado inline boost::urls::url replaceUrlSegment(const boost::urls::url_view& urlView,
6971c0bb5c6SCarson Labrado                                           const uint replaceLoc,
6981c0bb5c6SCarson Labrado                                           const std::string_view newSegment)
6991c0bb5c6SCarson Labrado {
7001c0bb5c6SCarson Labrado     const boost::urls::segments_view& urlSegments = urlView.segments();
7011c0bb5c6SCarson Labrado     boost::urls::url url("/");
7021c0bb5c6SCarson Labrado 
7031c0bb5c6SCarson Labrado     if (!urlSegments.is_absolute())
7041c0bb5c6SCarson Labrado     {
7051c0bb5c6SCarson Labrado         return url;
7061c0bb5c6SCarson Labrado     }
7071c0bb5c6SCarson Labrado 
7081c0bb5c6SCarson Labrado     boost::urls::segments_view::iterator it = urlSegments.begin();
7091c0bb5c6SCarson Labrado     boost::urls::segments_view::iterator end = urlSegments.end();
7101c0bb5c6SCarson Labrado 
7111c0bb5c6SCarson Labrado     for (uint idx = 0; it != end; it++, idx++)
7121c0bb5c6SCarson Labrado     {
7131c0bb5c6SCarson Labrado         if (idx == replaceLoc)
7141c0bb5c6SCarson Labrado         {
7151c0bb5c6SCarson Labrado             url.segments().push_back(newSegment);
7161c0bb5c6SCarson Labrado         }
7171c0bb5c6SCarson Labrado         else
7181c0bb5c6SCarson Labrado         {
7191c0bb5c6SCarson Labrado             url.segments().push_back(*it);
7201c0bb5c6SCarson Labrado         }
7211c0bb5c6SCarson Labrado     }
7221c0bb5c6SCarson Labrado 
7231c0bb5c6SCarson Labrado     return url;
7241c0bb5c6SCarson Labrado }
7251c0bb5c6SCarson Labrado 
726eb1c47d3SEd Tanous inline std::string setProtocolDefaults(const boost::urls::url_view& url)
727eb1c47d3SEd Tanous {
728eb1c47d3SEd Tanous     if (url.scheme() == "https")
729eb1c47d3SEd Tanous     {
730eb1c47d3SEd Tanous         return "https";
731eb1c47d3SEd Tanous     }
732eb1c47d3SEd Tanous     if (url.scheme() == "http")
733eb1c47d3SEd Tanous     {
734eb1c47d3SEd Tanous         if (bmcwebInsecureEnableHttpPushStyleEventing)
735eb1c47d3SEd Tanous         {
736eb1c47d3SEd Tanous             return "http";
737eb1c47d3SEd Tanous         }
738eb1c47d3SEd Tanous         return "";
739eb1c47d3SEd Tanous     }
740eb1c47d3SEd Tanous     return "";
741eb1c47d3SEd Tanous }
742eb1c47d3SEd Tanous 
743eb1c47d3SEd Tanous inline uint16_t setPortDefaults(const boost::urls::url_view& url)
744eb1c47d3SEd Tanous {
745eb1c47d3SEd Tanous     uint16_t port = url.port_number();
746eb1c47d3SEd Tanous     if (port != 0)
747eb1c47d3SEd Tanous     {
748eb1c47d3SEd Tanous         // user picked a port already.
749eb1c47d3SEd Tanous         return port;
750eb1c47d3SEd Tanous     }
751eb1c47d3SEd Tanous 
752eb1c47d3SEd Tanous     // If the user hasn't explicitly stated a port, pick one explicitly for them
753eb1c47d3SEd Tanous     // based on the protocol defaults
754eb1c47d3SEd Tanous     if (url.scheme() == "http")
755eb1c47d3SEd Tanous     {
756eb1c47d3SEd Tanous         return 80;
757eb1c47d3SEd Tanous     }
758eb1c47d3SEd Tanous     if (url.scheme() == "https")
759eb1c47d3SEd Tanous     {
760eb1c47d3SEd Tanous         return 443;
761eb1c47d3SEd Tanous     }
762eb1c47d3SEd Tanous     return 0;
763eb1c47d3SEd Tanous }
764eb1c47d3SEd Tanous 
76511baefe4SEd Tanous inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
766eb1c47d3SEd Tanous                                 std::string& host, uint16_t& port,
76711baefe4SEd Tanous                                 std::string& path)
76811baefe4SEd Tanous {
769eb1c47d3SEd Tanous     boost::string_view urlBoost(destUrl.data(), destUrl.size());
770eb1c47d3SEd Tanous     boost::urls::result<boost::urls::url_view> url =
771eb1c47d3SEd Tanous         boost::urls::parse_uri(urlBoost);
772eb1c47d3SEd Tanous     if (!url)
773eb1c47d3SEd Tanous     {
774eb1c47d3SEd Tanous         return false;
775eb1c47d3SEd Tanous     }
776eb1c47d3SEd Tanous     urlProto = setProtocolDefaults(url.value());
777eb1c47d3SEd Tanous     if (urlProto.empty())
77811baefe4SEd Tanous     {
77911baefe4SEd Tanous         return false;
78011baefe4SEd Tanous     }
78111baefe4SEd Tanous 
782eb1c47d3SEd Tanous     port = setPortDefaults(url.value());
78311baefe4SEd Tanous 
784eb1c47d3SEd Tanous     host = std::string_view(url->encoded_host().data(),
785eb1c47d3SEd Tanous                             url->encoded_host().size());
786eb1c47d3SEd Tanous 
787eb1c47d3SEd Tanous     path = std::string_view(url->encoded_path().data(),
788eb1c47d3SEd Tanous                             url->encoded_path().size());
78911baefe4SEd Tanous     if (path.empty())
79011baefe4SEd Tanous     {
79111baefe4SEd Tanous         path = "/";
79211baefe4SEd Tanous     }
793eb1c47d3SEd Tanous     if (url->has_fragment())
794eb1c47d3SEd Tanous     {
795eb1c47d3SEd Tanous         path += '#';
796eb1c47d3SEd Tanous         path += std::string_view(url->encoded_fragment().data(),
797eb1c47d3SEd Tanous                                  url->encoded_fragment().size());
798eb1c47d3SEd Tanous     }
799eb1c47d3SEd Tanous 
800eb1c47d3SEd Tanous     if (url->has_query())
801eb1c47d3SEd Tanous     {
802eb1c47d3SEd Tanous         path += '?';
803eb1c47d3SEd Tanous         path += std::string_view(url->encoded_query().data(),
804eb1c47d3SEd Tanous                                  url->encoded_query().size());
805eb1c47d3SEd Tanous     }
806eb1c47d3SEd Tanous 
80711baefe4SEd Tanous     return true;
80811baefe4SEd Tanous }
80911baefe4SEd Tanous 
81004e438cbSEd Tanous } // namespace utility
81104e438cbSEd Tanous } // namespace crow
81271f2db75SEd Tanous 
81371f2db75SEd Tanous namespace nlohmann
81471f2db75SEd Tanous {
81571f2db75SEd Tanous template <>
81671f2db75SEd Tanous struct adl_serializer<boost::urls::url>
81771f2db75SEd Tanous {
81871f2db75SEd Tanous     // nlohmann requires a specific casing to look these up in adl
81971f2db75SEd Tanous     // NOLINTNEXTLINE(readability-identifier-naming)
82071f2db75SEd Tanous     static void to_json(json& j, const boost::urls::url& url)
82171f2db75SEd Tanous     {
82271f2db75SEd Tanous         j = url.string();
82371f2db75SEd Tanous     }
82471f2db75SEd Tanous };
82571f2db75SEd Tanous 
82671f2db75SEd Tanous template <>
82771f2db75SEd Tanous struct adl_serializer<boost::urls::url_view>
82871f2db75SEd Tanous {
82971f2db75SEd Tanous     // NOLINTNEXTLINE(readability-identifier-naming)
83071f2db75SEd Tanous     static void to_json(json& j, const boost::urls::url_view& url)
83171f2db75SEd Tanous     {
83271f2db75SEd Tanous         j = url.string();
83371f2db75SEd Tanous     }
83471f2db75SEd Tanous };
83571f2db75SEd Tanous } // namespace nlohmann
836