xref: /openbmc/bmcweb/http/utility.hpp (revision 25b54dba)
104e438cbSEd Tanous #pragma once
204e438cbSEd Tanous 
33ccb3adbSEd Tanous #include "bmcweb_config.h"
43ccb3adbSEd Tanous 
56dbe9beaSEd Tanous extern "C"
66dbe9beaSEd Tanous {
704e438cbSEd Tanous #include <openssl/crypto.h>
86dbe9beaSEd Tanous }
904e438cbSEd Tanous 
10c867a83eSEd Tanous #include <boost/callable_traits.hpp>
11079360aeSEd Tanous #include <boost/url/parse.hpp>
12eae855c7SEd Tanous #include <boost/url/url.hpp>
13079360aeSEd Tanous #include <boost/url/url_view.hpp>
144a7fbefdSEd Tanous #include <boost/url/url_view_base.hpp>
1571f2db75SEd Tanous #include <nlohmann/json.hpp>
161d8782e7SNan Zhou 
179ea15c35SEd Tanous #include <array>
1874849befSEd Tanous #include <chrono>
19c715ec29SEd Tanous #include <cstddef>
2004e438cbSEd Tanous #include <cstdint>
219ea15c35SEd Tanous #include <ctime>
2204e438cbSEd Tanous #include <functional>
239896eaedSEd Tanous #include <iomanip>
249ea15c35SEd Tanous #include <limits>
2504e438cbSEd Tanous #include <stdexcept>
2604e438cbSEd Tanous #include <string>
279ea15c35SEd Tanous #include <string_view>
2804e438cbSEd Tanous #include <tuple>
299ea15c35SEd Tanous #include <type_traits>
309ea15c35SEd Tanous #include <utility>
31ca1600c1SSzymon Dompke #include <variant>
3204e438cbSEd Tanous 
3304e438cbSEd Tanous namespace crow
3404e438cbSEd Tanous {
3547488a98SEd Tanous namespace utility
3604e438cbSEd Tanous {
3704e438cbSEd Tanous 
getParameterTag(std::string_view url)389de65b34SEd Tanous constexpr uint64_t getParameterTag(std::string_view url)
3904e438cbSEd Tanous {
401c30e500SEd Tanous     uint64_t tagValue = 0;
411c30e500SEd Tanous     size_t urlSegmentIndex = std::string_view::npos;
42b00dcc27SEd Tanous 
431c30e500SEd Tanous     for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
441c30e500SEd Tanous     {
451c30e500SEd Tanous         char character = url[urlIndex];
461c30e500SEd Tanous         if (character == '<')
471c30e500SEd Tanous         {
481c30e500SEd Tanous             if (urlSegmentIndex != std::string_view::npos)
4904e438cbSEd Tanous             {
5004e438cbSEd Tanous                 return 0;
5104e438cbSEd Tanous             }
521c30e500SEd Tanous             urlSegmentIndex = urlIndex;
531c30e500SEd Tanous         }
541c30e500SEd Tanous         if (character == '>')
5504e438cbSEd Tanous         {
561c30e500SEd Tanous             if (urlSegmentIndex == std::string_view::npos)
571c30e500SEd Tanous             {
581c30e500SEd Tanous                 return 0;
591c30e500SEd Tanous             }
6089492a15SPatrick Williams             std::string_view tag = url.substr(urlSegmentIndex,
6189492a15SPatrick Williams                                               urlIndex + 1 - urlSegmentIndex);
621c30e500SEd Tanous 
631c30e500SEd Tanous             if (tag == "<str>" || tag == "<string>")
6404e438cbSEd Tanous             {
65d9e89dfdSEd Tanous                 tagValue++;
6604e438cbSEd Tanous             }
671c30e500SEd Tanous             if (tag == "<path>")
6804e438cbSEd Tanous             {
69d9e89dfdSEd Tanous                 tagValue++;
7004e438cbSEd Tanous             }
711c30e500SEd Tanous             urlSegmentIndex = std::string_view::npos;
721c30e500SEd Tanous         }
731c30e500SEd Tanous     }
741c30e500SEd Tanous     if (urlSegmentIndex != std::string_view::npos)
751c30e500SEd Tanous     {
761c30e500SEd Tanous         return 0;
771c30e500SEd Tanous     }
781c30e500SEd Tanous     return tagValue;
7904e438cbSEd Tanous }
8004e438cbSEd Tanous 
81ee192c06SEd Tanous class Base64Encoder
82d830ff5aSAdriana Kobylak {
83ee192c06SEd Tanous     char overflow1 = '\0';
84ee192c06SEd Tanous     char overflow2 = '\0';
85ee192c06SEd Tanous     uint8_t overflowCount = 0;
86ee192c06SEd Tanous 
87ee192c06SEd Tanous     constexpr static std::array<char, 64> key = {
88d830ff5aSAdriana Kobylak         'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
89d830ff5aSAdriana Kobylak         'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
90d830ff5aSAdriana Kobylak         'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
91d830ff5aSAdriana Kobylak         'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
92d830ff5aSAdriana Kobylak         '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
93d830ff5aSAdriana Kobylak 
94ee192c06SEd Tanous     // Takes 3 ascii chars, and encodes them as 4 base64 chars
encodeTriple(char first,char second,char third,std::string & output)95ee192c06SEd Tanous     static void encodeTriple(char first, char second, char third,
96ee192c06SEd Tanous                              std::string& output)
97d830ff5aSAdriana Kobylak     {
98543f4400SEd Tanous         size_t keyIndex = 0;
99d830ff5aSAdriana Kobylak 
100ee192c06SEd Tanous         keyIndex = static_cast<size_t>(first & 0xFC) >> 2;
101ee192c06SEd Tanous         output += key[keyIndex];
102d830ff5aSAdriana Kobylak 
103ee192c06SEd Tanous         keyIndex = static_cast<size_t>(first & 0x03) << 4;
104ee192c06SEd Tanous         keyIndex += static_cast<size_t>(second & 0xF0) >> 4;
105ee192c06SEd Tanous         output += key[keyIndex];
106ee192c06SEd Tanous 
107ee192c06SEd Tanous         keyIndex = static_cast<size_t>(second & 0x0F) << 2;
108ee192c06SEd Tanous         keyIndex += static_cast<size_t>(third & 0xC0) >> 6;
109ee192c06SEd Tanous         output += key[keyIndex];
110ee192c06SEd Tanous 
111ee192c06SEd Tanous         keyIndex = static_cast<size_t>(third & 0x3F);
112ee192c06SEd Tanous         output += key[keyIndex];
113ee192c06SEd Tanous     }
114ee192c06SEd Tanous 
115ee192c06SEd Tanous   public:
116ee192c06SEd Tanous     // Accepts a partial string to encode, and writes the encoded characters to
117ee192c06SEd Tanous     // the output stream. requires subsequently calling finalize to complete
118ee192c06SEd Tanous     // stream.
encode(std::string_view data,std::string & output)119ee192c06SEd Tanous     void encode(std::string_view data, std::string& output)
120d830ff5aSAdriana Kobylak     {
121ee192c06SEd Tanous         // Encode the last round of overflow chars first
122ee192c06SEd Tanous         if (overflowCount == 2)
123d830ff5aSAdriana Kobylak         {
124ee192c06SEd Tanous             if (!data.empty())
125ee192c06SEd Tanous             {
126ee192c06SEd Tanous                 encodeTriple(overflow1, overflow2, data[0], output);
127ee192c06SEd Tanous                 overflowCount = 0;
128ee192c06SEd Tanous                 data.remove_prefix(1);
129ee192c06SEd Tanous             }
130ee192c06SEd Tanous         }
131ee192c06SEd Tanous         else if (overflowCount == 1)
132ee192c06SEd Tanous         {
133ee192c06SEd Tanous             if (data.size() >= 2)
134ee192c06SEd Tanous             {
135ee192c06SEd Tanous                 encodeTriple(overflow1, data[0], data[1], output);
136ee192c06SEd Tanous                 overflowCount = 0;
137ee192c06SEd Tanous                 data.remove_prefix(2);
138ee192c06SEd Tanous             }
139ee192c06SEd Tanous         }
140d830ff5aSAdriana Kobylak 
141ee192c06SEd Tanous         while (data.size() >= 3)
142ee192c06SEd Tanous         {
143ee192c06SEd Tanous             encodeTriple(data[0], data[1], data[2], output);
144ee192c06SEd Tanous             data.remove_prefix(3);
145ee192c06SEd Tanous         }
146ee192c06SEd Tanous 
147ee192c06SEd Tanous         if (!data.empty() && overflowCount == 0)
148ee192c06SEd Tanous         {
149ee192c06SEd Tanous             overflow1 = data[0];
150ee192c06SEd Tanous             overflowCount++;
151ee192c06SEd Tanous             data.remove_prefix(1);
152ee192c06SEd Tanous         }
153ee192c06SEd Tanous 
154ee192c06SEd Tanous         if (!data.empty() && overflowCount == 1)
155ee192c06SEd Tanous         {
156ee192c06SEd Tanous             overflow2 = data[0];
157ee192c06SEd Tanous             overflowCount++;
158ee192c06SEd Tanous             data.remove_prefix(1);
159ee192c06SEd Tanous         }
160ee192c06SEd Tanous     }
161ee192c06SEd Tanous 
162ee192c06SEd Tanous     // Completes a base64 output, by writing any MOD(3) characters to the
163ee192c06SEd Tanous     // output, as well as any required trailing =
finalize(std::string & output)164ee192c06SEd Tanous     void finalize(std::string& output)
165ee192c06SEd Tanous     {
166ee192c06SEd Tanous         if (overflowCount == 0)
167ee192c06SEd Tanous         {
168ee192c06SEd Tanous             return;
169ee192c06SEd Tanous         }
170ee192c06SEd Tanous         size_t keyIndex = static_cast<size_t>(overflow1 & 0xFC) >> 2;
171ee192c06SEd Tanous         output += key[keyIndex];
172ee192c06SEd Tanous 
173ee192c06SEd Tanous         keyIndex = static_cast<size_t>(overflow1 & 0x03) << 4;
174ee192c06SEd Tanous         if (overflowCount == 2)
175ee192c06SEd Tanous         {
176ee192c06SEd Tanous             keyIndex += static_cast<size_t>(overflow2 & 0xF0) >> 4;
177ee192c06SEd Tanous             output += key[keyIndex];
178ee192c06SEd Tanous             keyIndex = static_cast<size_t>(overflow2 & 0x0F) << 2;
179ee192c06SEd Tanous             output += key[keyIndex];
180d830ff5aSAdriana Kobylak         }
181d830ff5aSAdriana Kobylak         else
182d830ff5aSAdriana Kobylak         {
183ee192c06SEd Tanous             output += key[keyIndex];
184ee192c06SEd Tanous             output += '=';
185d830ff5aSAdriana Kobylak         }
186ee192c06SEd Tanous         output += '=';
187ee192c06SEd Tanous         overflowCount = 0;
188d830ff5aSAdriana Kobylak     }
189ee192c06SEd Tanous 
190ee192c06SEd Tanous     // Returns the required output buffer in characters for an input of size
191ee192c06SEd Tanous     // inputSize
encodedSize(size_t inputSize)192ee192c06SEd Tanous     static size_t constexpr encodedSize(size_t inputSize)
193d830ff5aSAdriana Kobylak     {
194ee192c06SEd Tanous         // Base64 encodes 3 character blocks as 4 character blocks
195ee192c06SEd Tanous         // With a possibility of 2 trailing = characters
196ee192c06SEd Tanous         return (inputSize + 2) / 3 * 4;
197d830ff5aSAdriana Kobylak     }
198ee192c06SEd Tanous };
199d830ff5aSAdriana Kobylak 
base64encode(std::string_view data)200ee192c06SEd Tanous inline std::string base64encode(std::string_view data)
201ee192c06SEd Tanous {
202ee192c06SEd Tanous     // Encodes a 3 character stream into a 4 character stream
203ee192c06SEd Tanous     std::string out;
204ee192c06SEd Tanous     Base64Encoder base64;
205ee192c06SEd Tanous     out.reserve(Base64Encoder::encodedSize(data.size()));
206ee192c06SEd Tanous     base64.encode(data, out);
207ee192c06SEd Tanous     base64.finalize(out);
208ee192c06SEd Tanous     return out;
209d830ff5aSAdriana Kobylak }
210d830ff5aSAdriana Kobylak 
21104e438cbSEd Tanous // TODO this is temporary and should be deleted once base64 is refactored out of
21204e438cbSEd Tanous // crow
base64Decode(std::string_view input,std::string & output)21326ccae32SEd Tanous inline bool base64Decode(std::string_view input, std::string& output)
21404e438cbSEd Tanous {
21504e438cbSEd Tanous     static const char nop = static_cast<char>(-1);
21604e438cbSEd Tanous     // See note on encoding_data[] in above function
21704e438cbSEd Tanous     static const std::array<char, 256> decodingData = {
21804e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
21904e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
22004e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
22104e438cbSEd Tanous         nop, 62,  nop, nop, nop, 63,  52,  53,  54,  55,  56,  57,  58,  59,
22204e438cbSEd Tanous         60,  61,  nop, nop, nop, nop, nop, nop, nop, 0,   1,   2,   3,   4,
22304e438cbSEd Tanous         5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,
22404e438cbSEd Tanous         19,  20,  21,  22,  23,  24,  25,  nop, nop, nop, nop, nop, nop, 26,
22504e438cbSEd Tanous         27,  28,  29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,
22604e438cbSEd Tanous         41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  nop, nop, nop,
22704e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
22804e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
22904e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23004e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23104e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23204e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23304e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23404e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23504e438cbSEd Tanous         nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
23604e438cbSEd Tanous         nop, nop, nop, nop};
23704e438cbSEd Tanous 
23804e438cbSEd Tanous     size_t inputLength = input.size();
23904e438cbSEd Tanous 
24004e438cbSEd Tanous     // allocate space for output string
24104e438cbSEd Tanous     output.clear();
24204e438cbSEd Tanous     output.reserve(((inputLength + 2) / 3) * 4);
24304e438cbSEd Tanous 
24404e438cbSEd Tanous     auto getCodeValue = [](char c) {
24504e438cbSEd Tanous         auto code = static_cast<unsigned char>(c);
24604e438cbSEd Tanous         // Ensure we cannot index outside the bounds of the decoding array
24704e438cbSEd Tanous         static_assert(std::numeric_limits<decltype(code)>::max() <
24804e438cbSEd Tanous                       decodingData.size());
24904e438cbSEd Tanous         return decodingData[code];
25004e438cbSEd Tanous     };
25104e438cbSEd Tanous 
25204e438cbSEd Tanous     // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
25304e438cbSEd Tanous     // dropping first two bits
25404e438cbSEd Tanous     // and regenerate into 3 8-bits sequences
25504e438cbSEd Tanous 
25604e438cbSEd Tanous     for (size_t i = 0; i < inputLength; i++)
25704e438cbSEd Tanous     {
258543f4400SEd Tanous         char base64code0 = 0;
259543f4400SEd Tanous         char base64code1 = 0;
26004e438cbSEd Tanous         char base64code2 = 0; // initialized to 0 to suppress warnings
26104e438cbSEd Tanous 
26204e438cbSEd Tanous         base64code0 = getCodeValue(input[i]);
26304e438cbSEd Tanous         if (base64code0 == nop)
26404e438cbSEd Tanous         { // non base64 character
26504e438cbSEd Tanous             return false;
26604e438cbSEd Tanous         }
26704e438cbSEd Tanous         if (!(++i < inputLength))
26804e438cbSEd Tanous         { // we need at least two input bytes for first
26904e438cbSEd Tanous           // byte output
27004e438cbSEd Tanous             return false;
27104e438cbSEd Tanous         }
27204e438cbSEd Tanous         base64code1 = getCodeValue(input[i]);
27304e438cbSEd Tanous         if (base64code1 == nop)
27404e438cbSEd Tanous         { // non base64 character
27504e438cbSEd Tanous             return false;
27604e438cbSEd Tanous         }
27704e438cbSEd Tanous         output +=
27804e438cbSEd Tanous             static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
27904e438cbSEd Tanous 
28004e438cbSEd Tanous         if (++i < inputLength)
28104e438cbSEd Tanous         {
28204e438cbSEd Tanous             char c = input[i];
28304e438cbSEd Tanous             if (c == '=')
28404e438cbSEd Tanous             { // padding , end of input
28504e438cbSEd Tanous                 return (base64code1 & 0x0f) == 0;
28604e438cbSEd Tanous             }
28704e438cbSEd Tanous             base64code2 = getCodeValue(input[i]);
28804e438cbSEd Tanous             if (base64code2 == nop)
28904e438cbSEd Tanous             { // non base64 character
29004e438cbSEd Tanous                 return false;
29104e438cbSEd Tanous             }
29204e438cbSEd Tanous             output += static_cast<char>(((base64code1 << 4) & 0xf0) |
29304e438cbSEd Tanous                                         ((base64code2 >> 2) & 0x0f));
29404e438cbSEd Tanous         }
29504e438cbSEd Tanous 
29604e438cbSEd Tanous         if (++i < inputLength)
29704e438cbSEd Tanous         {
29804e438cbSEd Tanous             char c = input[i];
29904e438cbSEd Tanous             if (c == '=')
30004e438cbSEd Tanous             { // padding , end of input
30104e438cbSEd Tanous                 return (base64code2 & 0x03) == 0;
30204e438cbSEd Tanous             }
303f8fe53e7SEd Tanous             char base64code3 = getCodeValue(input[i]);
30404e438cbSEd Tanous             if (base64code3 == nop)
30504e438cbSEd Tanous             { // non base64 character
30604e438cbSEd Tanous                 return false;
30704e438cbSEd Tanous             }
30804e438cbSEd Tanous             output +=
30904e438cbSEd Tanous                 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
31004e438cbSEd Tanous         }
31104e438cbSEd Tanous     }
31204e438cbSEd Tanous 
31304e438cbSEd Tanous     return true;
31404e438cbSEd Tanous }
31504e438cbSEd Tanous 
constantTimeStringCompare(std::string_view a,std::string_view b)31626ccae32SEd Tanous inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
31704e438cbSEd Tanous {
31804e438cbSEd Tanous     // Important note, this function is ONLY constant time if the two input
31904e438cbSEd Tanous     // sizes are the same
32004e438cbSEd Tanous     if (a.size() != b.size())
32104e438cbSEd Tanous     {
32204e438cbSEd Tanous         return false;
32304e438cbSEd Tanous     }
32404e438cbSEd Tanous     return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
32504e438cbSEd Tanous }
32604e438cbSEd Tanous 
32704e438cbSEd Tanous struct ConstantTimeCompare
32804e438cbSEd Tanous {
operator ()crow::utility::ConstantTimeCompare32926ccae32SEd Tanous     bool operator()(std::string_view a, std::string_view b) const
33004e438cbSEd Tanous     {
33104e438cbSEd Tanous         return constantTimeStringCompare(a, b);
33204e438cbSEd Tanous     }
33304e438cbSEd Tanous };
33404e438cbSEd Tanous 
335eae855c7SEd Tanous namespace details
336eae855c7SEd Tanous {
337eae855c7SEd Tanous inline boost::urls::url
appendUrlPieces(boost::urls::url & url,const std::initializer_list<std::string_view> args)338c6bcedc6SWilly Tu     appendUrlPieces(boost::urls::url& url,
339c6bcedc6SWilly Tu                     const std::initializer_list<std::string_view> args)
340eae855c7SEd Tanous {
34126ccae32SEd Tanous     for (std::string_view arg : args)
342eae855c7SEd Tanous     {
343eae855c7SEd Tanous         url.segments().push_back(arg);
344eae855c7SEd Tanous     }
345eae855c7SEd Tanous     return url;
346eae855c7SEd Tanous }
347c6bcedc6SWilly Tu 
348eae855c7SEd Tanous } // namespace details
349eae855c7SEd Tanous 
3507f8d8fa9SEd Tanous class OrMorePaths
3517f8d8fa9SEd Tanous {};
3527f8d8fa9SEd Tanous 
353eae855c7SEd Tanous template <typename... AV>
appendUrlPieces(boost::urls::url & url,const AV...args)354c6bcedc6SWilly Tu inline void appendUrlPieces(boost::urls::url& url, const AV... args)
355c6bcedc6SWilly Tu {
356c6bcedc6SWilly Tu     details::appendUrlPieces(url, {args...});
357c6bcedc6SWilly Tu }
358c6bcedc6SWilly Tu 
359ca1600c1SSzymon Dompke namespace details
360ca1600c1SSzymon Dompke {
361ca1600c1SSzymon Dompke 
362ca1600c1SSzymon Dompke // std::reference_wrapper<std::string> - extracts segment to variable
363ca1600c1SSzymon Dompke //                    std::string_view - checks if segment is equal to variable
3647f8d8fa9SEd Tanous using UrlSegment = std::variant<std::reference_wrapper<std::string>,
3657f8d8fa9SEd Tanous                                 std::string_view, OrMorePaths>;
3667f8d8fa9SEd Tanous 
3677f8d8fa9SEd Tanous enum class UrlParseResult
3687f8d8fa9SEd Tanous {
3697f8d8fa9SEd Tanous     Continue,
3707f8d8fa9SEd Tanous     Fail,
3717f8d8fa9SEd Tanous     Done,
3727f8d8fa9SEd Tanous };
373ca1600c1SSzymon Dompke 
374ca1600c1SSzymon Dompke class UrlSegmentMatcherVisitor
375ca1600c1SSzymon Dompke {
376ca1600c1SSzymon Dompke   public:
operator ()(std::string & output)3777f8d8fa9SEd Tanous     UrlParseResult operator()(std::string& output)
378ca1600c1SSzymon Dompke     {
379079360aeSEd Tanous         output = segment;
3807f8d8fa9SEd Tanous         return UrlParseResult::Continue;
381ca1600c1SSzymon Dompke     }
382ca1600c1SSzymon Dompke 
operator ()(std::string_view expected)3837f8d8fa9SEd Tanous     UrlParseResult operator()(std::string_view expected)
384ca1600c1SSzymon Dompke     {
385079360aeSEd Tanous         if (segment == expected)
3867f8d8fa9SEd Tanous         {
3877f8d8fa9SEd Tanous             return UrlParseResult::Continue;
3887f8d8fa9SEd Tanous         }
3897f8d8fa9SEd Tanous         return UrlParseResult::Fail;
3907f8d8fa9SEd Tanous     }
3917f8d8fa9SEd Tanous 
operator ()(OrMorePaths)3927f8d8fa9SEd Tanous     UrlParseResult operator()(OrMorePaths /*unused*/)
3937f8d8fa9SEd Tanous     {
3947f8d8fa9SEd Tanous         return UrlParseResult::Done;
395ca1600c1SSzymon Dompke     }
396ca1600c1SSzymon Dompke 
UrlSegmentMatcherVisitor(std::string_view segmentIn)397079360aeSEd Tanous     explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
398ca1600c1SSzymon Dompke         segment(segmentIn)
399ca1600c1SSzymon Dompke     {}
400ca1600c1SSzymon Dompke 
401ca1600c1SSzymon Dompke   private:
402079360aeSEd Tanous     std::string_view segment;
403ca1600c1SSzymon Dompke };
404ca1600c1SSzymon Dompke 
readUrlSegments(const boost::urls::url_view_base & url,std::initializer_list<UrlSegment> segments)4054a7fbefdSEd Tanous inline bool readUrlSegments(const boost::urls::url_view_base& url,
4065be2b14aSEd Tanous                             std::initializer_list<UrlSegment> segments)
407ca1600c1SSzymon Dompke {
4084a7fbefdSEd Tanous     const boost::urls::segments_view& urlSegments = url.segments();
409ca1600c1SSzymon Dompke 
4107f8d8fa9SEd Tanous     if (!urlSegments.is_absolute())
411ca1600c1SSzymon Dompke     {
412ca1600c1SSzymon Dompke         return false;
413ca1600c1SSzymon Dompke     }
414ca1600c1SSzymon Dompke 
4154a7fbefdSEd Tanous     boost::urls::segments_view::const_iterator it = urlSegments.begin();
4164a7fbefdSEd Tanous     boost::urls::segments_view::const_iterator end = urlSegments.end();
417ca1600c1SSzymon Dompke 
418ca1600c1SSzymon Dompke     for (const auto& segment : segments)
419ca1600c1SSzymon Dompke     {
4207f8d8fa9SEd Tanous         if (it == end)
4217f8d8fa9SEd Tanous         {
4227f8d8fa9SEd Tanous             // If the request ends with an "any" path, this was successful
4237f8d8fa9SEd Tanous             return std::holds_alternative<OrMorePaths>(segment);
4247f8d8fa9SEd Tanous         }
4257f8d8fa9SEd Tanous         UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
4267f8d8fa9SEd Tanous         if (res == UrlParseResult::Done)
4277f8d8fa9SEd Tanous         {
4287f8d8fa9SEd Tanous             return true;
4297f8d8fa9SEd Tanous         }
4307f8d8fa9SEd Tanous         if (res == UrlParseResult::Fail)
431ca1600c1SSzymon Dompke         {
432ca1600c1SSzymon Dompke             return false;
433ca1600c1SSzymon Dompke         }
434ca1600c1SSzymon Dompke         it++;
435ca1600c1SSzymon Dompke     }
4364c30e226SCarson Labrado 
4374c30e226SCarson Labrado     // There will be an empty segment at the end if the URI ends with a "/"
4384c30e226SCarson Labrado     // e.g. /redfish/v1/Chassis/
4394c30e226SCarson Labrado     if ((it != end) && urlSegments.back().empty())
4404c30e226SCarson Labrado     {
4414c30e226SCarson Labrado         it++;
4424c30e226SCarson Labrado     }
4437f8d8fa9SEd Tanous     return it == end;
444ca1600c1SSzymon Dompke }
445ca1600c1SSzymon Dompke 
446ca1600c1SSzymon Dompke } // namespace details
447ca1600c1SSzymon Dompke 
448ca1600c1SSzymon Dompke template <typename... Args>
readUrlSegments(const boost::urls::url_view_base & url,Args &&...args)4494a7fbefdSEd Tanous inline bool readUrlSegments(const boost::urls::url_view_base& url,
4504a7fbefdSEd Tanous                             Args&&... args)
451ca1600c1SSzymon Dompke {
45239662a3bSEd Tanous     return details::readUrlSegments(url, {std::forward<Args>(args)...});
453ca1600c1SSzymon Dompke }
454ca1600c1SSzymon Dompke 
4554a7fbefdSEd Tanous inline boost::urls::url
replaceUrlSegment(const boost::urls::url_view_base & urlView,const uint replaceLoc,std::string_view newSegment)4564a7fbefdSEd Tanous     replaceUrlSegment(const boost::urls::url_view_base& urlView,
4574a7fbefdSEd Tanous                       const uint replaceLoc, std::string_view newSegment)
4581c0bb5c6SCarson Labrado {
4594a7fbefdSEd Tanous     const boost::urls::segments_view& urlSegments = urlView.segments();
4601c0bb5c6SCarson Labrado     boost::urls::url url("/");
4611c0bb5c6SCarson Labrado 
4621c0bb5c6SCarson Labrado     if (!urlSegments.is_absolute())
4631c0bb5c6SCarson Labrado     {
4641c0bb5c6SCarson Labrado         return url;
4651c0bb5c6SCarson Labrado     }
4661c0bb5c6SCarson Labrado 
4671c0bb5c6SCarson Labrado     boost::urls::segments_view::iterator it = urlSegments.begin();
4681c0bb5c6SCarson Labrado     boost::urls::segments_view::iterator end = urlSegments.end();
4691c0bb5c6SCarson Labrado 
4701c0bb5c6SCarson Labrado     for (uint idx = 0; it != end; it++, idx++)
4711c0bb5c6SCarson Labrado     {
4721c0bb5c6SCarson Labrado         if (idx == replaceLoc)
4731c0bb5c6SCarson Labrado         {
4741c0bb5c6SCarson Labrado             url.segments().push_back(newSegment);
4751c0bb5c6SCarson Labrado         }
4761c0bb5c6SCarson Labrado         else
4771c0bb5c6SCarson Labrado         {
4781c0bb5c6SCarson Labrado             url.segments().push_back(*it);
4791c0bb5c6SCarson Labrado         }
4801c0bb5c6SCarson Labrado     }
4811c0bb5c6SCarson Labrado 
4821c0bb5c6SCarson Labrado     return url;
4831c0bb5c6SCarson Labrado }
4841c0bb5c6SCarson Labrado 
setProtocolDefaults(boost::urls::url & url,std::string_view protocol)485a716aa74SEd Tanous inline void setProtocolDefaults(boost::urls::url& url,
486a716aa74SEd Tanous                                 std::string_view protocol)
487eb1c47d3SEd Tanous {
488a716aa74SEd Tanous     if (url.has_scheme())
489eb1c47d3SEd Tanous     {
490a716aa74SEd Tanous         return;
491eb1c47d3SEd Tanous     }
492a716aa74SEd Tanous     if (protocol == "Redfish" || protocol.empty())
493a716aa74SEd Tanous     {
494a716aa74SEd Tanous         if (url.port_number() == 443)
495a716aa74SEd Tanous         {
496a716aa74SEd Tanous             url.set_scheme("https");
497a716aa74SEd Tanous         }
498a716aa74SEd Tanous         if (url.port_number() == 80)
499eb1c47d3SEd Tanous         {
500*25b54dbaSEd Tanous             if constexpr (BMCWEB_INSECURE_PUSH_STYLE_NOTIFICATION)
501eb1c47d3SEd Tanous             {
502a716aa74SEd Tanous                 url.set_scheme("http");
503eb1c47d3SEd Tanous             }
504eb1c47d3SEd Tanous         }
505a716aa74SEd Tanous     }
506a716aa74SEd Tanous     else if (protocol == "SNMPv2c")
5073d30708fSChicago Duan     {
508a716aa74SEd Tanous         url.set_scheme("snmp");
5093d30708fSChicago Duan     }
510eb1c47d3SEd Tanous }
511eb1c47d3SEd Tanous 
setPortDefaults(boost::urls::url & url)512a716aa74SEd Tanous inline void setPortDefaults(boost::urls::url& url)
513eb1c47d3SEd Tanous {
514eb1c47d3SEd Tanous     uint16_t port = url.port_number();
515eb1c47d3SEd Tanous     if (port != 0)
516eb1c47d3SEd Tanous     {
517a716aa74SEd Tanous         return;
518eb1c47d3SEd Tanous     }
519eb1c47d3SEd Tanous 
520eb1c47d3SEd Tanous     // If the user hasn't explicitly stated a port, pick one explicitly for them
521eb1c47d3SEd Tanous     // based on the protocol defaults
522eb1c47d3SEd Tanous     if (url.scheme() == "http")
523eb1c47d3SEd Tanous     {
524a716aa74SEd Tanous         url.set_port_number(80);
525eb1c47d3SEd Tanous     }
526eb1c47d3SEd Tanous     if (url.scheme() == "https")
527eb1c47d3SEd Tanous     {
528a716aa74SEd Tanous         url.set_port_number(443);
529eb1c47d3SEd Tanous     }
5303d30708fSChicago Duan     if (url.scheme() == "snmp")
5313d30708fSChicago Duan     {
532a716aa74SEd Tanous         url.set_port_number(162);
5333d30708fSChicago Duan     }
53411baefe4SEd Tanous }
53511baefe4SEd Tanous 
53604e438cbSEd Tanous } // namespace utility
53704e438cbSEd Tanous } // namespace crow
53871f2db75SEd Tanous 
53971f2db75SEd Tanous namespace nlohmann
54071f2db75SEd Tanous {
5414a7fbefdSEd Tanous template <std::derived_from<boost::urls::url_view_base> URL>
5424a7fbefdSEd Tanous struct adl_serializer<URL>
54371f2db75SEd Tanous {
54471f2db75SEd Tanous     // NOLINTNEXTLINE(readability-identifier-naming)
to_jsonnlohmann::adl_serializer5454a7fbefdSEd Tanous     static void to_json(json& j, const URL& url)
54671f2db75SEd Tanous     {
547079360aeSEd Tanous         j = url.buffer();
54871f2db75SEd Tanous     }
54971f2db75SEd Tanous };
55071f2db75SEd Tanous } // namespace nlohmann
551