blob: 94d89fb1a7ddcff37253ac19c5426119bcf2c00f [file] [log] [blame]
Ed Tanous7045c8d2017-04-03 10:04:37 -07001#pragma once
Ed Tanous1abe55e2018-09-05 08:30:59 -07002
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "bmcweb_config.h"
4
Ed Tanous51dae672018-09-05 16:07:32 -07005#include <openssl/crypto.h>
6
Ed Tanousc867a832022-03-10 14:17:00 -08007#include <boost/callable_traits.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -07008#include <boost/url/parse.hpp>
Ed Tanouseae855c2021-10-26 11:26:02 -07009#include <boost/url/url.hpp>
Ed Tanous079360a2022-06-29 10:05:19 -070010#include <boost/url/url_view.hpp>
Ed Tanous71f2db72022-05-25 12:28:09 -070011#include <nlohmann/json.hpp>
Nan Zhou1d8782e2021-11-29 22:23:18 -080012
Ed Tanous9ea15c32022-01-04 14:18:22 -080013#include <array>
Ed Tanous74849be2021-02-05 09:47:47 -080014#include <chrono>
Ed Tanousc715ec22022-03-10 15:38:01 -080015#include <cstddef>
Ed Tanous7045c8d2017-04-03 10:04:37 -070016#include <cstdint>
Ed Tanous9ea15c32022-01-04 14:18:22 -080017#include <ctime>
Ed Tanous7045c8d2017-04-03 10:04:37 -070018#include <functional>
Ed Tanous9896eae2022-07-23 15:07:33 -070019#include <iomanip>
Ed Tanous9ea15c32022-01-04 14:18:22 -080020#include <limits>
Ed Tanous7045c8d2017-04-03 10:04:37 -070021#include <stdexcept>
22#include <string>
Ed Tanous9ea15c32022-01-04 14:18:22 -080023#include <string_view>
Ed Tanous7045c8d2017-04-03 10:04:37 -070024#include <tuple>
Ed Tanous9ea15c32022-01-04 14:18:22 -080025#include <type_traits>
26#include <utility>
Szymon Dompkeca1600c2022-03-03 14:42:52 +010027#include <variant>
Ed Tanous7045c8d2017-04-03 10:04:37 -070028
Ed Tanous1abe55e2018-09-05 08:30:59 -070029namespace crow
30{
31namespace black_magic
32{
Ed Tanous7045c8d2017-04-03 10:04:37 -070033
Ed Tanousc715ec22022-03-10 15:38:01 -080034enum class TypeCode : uint8_t
35{
36 Unspecified = 0,
Ed Tanous15a42df2023-02-09 18:08:23 -080037 String = 1,
38 Path = 2,
39 Max = 3,
Ed Tanousc715ec22022-03-10 15:38:01 -080040};
41
42// Remove when we have c++23
43template <typename E>
44constexpr typename std::underlying_type<E>::type toUnderlying(E e) noexcept
45{
46 return static_cast<typename std::underlying_type<E>::type>(e);
47}
48
Ed Tanous1c30e502022-03-08 18:02:24 -080049constexpr inline uint64_t getParameterTag(std::string_view url)
Ed Tanous1abe55e2018-09-05 08:30:59 -070050{
Ed Tanous1c30e502022-03-08 18:02:24 -080051 uint64_t tagValue = 0;
52 size_t urlSegmentIndex = std::string_view::npos;
Ed Tanousb00dcc22021-02-23 12:52:50 -080053
Ed Tanous1c30e502022-03-08 18:02:24 -080054 size_t paramIndex = 0;
55
56 for (size_t urlIndex = 0; urlIndex < url.size(); urlIndex++)
57 {
58 char character = url[urlIndex];
59 if (character == '<')
60 {
61 if (urlSegmentIndex != std::string_view::npos)
62 {
63 return 0;
64 }
65 urlSegmentIndex = urlIndex;
66 }
67 if (character == '>')
68 {
69 if (urlSegmentIndex == std::string_view::npos)
70 {
71 return 0;
72 }
Patrick Williams89492a12023-05-10 07:51:34 -050073 std::string_view tag = url.substr(urlSegmentIndex,
74 urlIndex + 1 - urlSegmentIndex);
Ed Tanous1c30e502022-03-08 18:02:24 -080075
76 // Note, this is a really lame way to do std::pow(6, paramIndex)
77 // std::pow doesn't work in constexpr in clang.
78 // Ideally in the future we'd move this to use a power of 2 packing
79 // (probably 8 instead of 6) so that these just become bit shifts
80 uint64_t insertIndex = 1;
81 for (size_t unused = 0; unused < paramIndex; unused++)
82 {
Ed Tanous15a42df2023-02-09 18:08:23 -080083 insertIndex *= 3;
Ed Tanous1c30e502022-03-08 18:02:24 -080084 }
85
Ed Tanous1c30e502022-03-08 18:02:24 -080086 if (tag == "<str>" || tag == "<string>")
87 {
Ed Tanousc715ec22022-03-10 15:38:01 -080088 tagValue += insertIndex * toUnderlying(TypeCode::String);
Ed Tanous1c30e502022-03-08 18:02:24 -080089 }
90 if (tag == "<path>")
91 {
Ed Tanousc715ec22022-03-10 15:38:01 -080092 tagValue += insertIndex * toUnderlying(TypeCode::Path);
Ed Tanous1c30e502022-03-08 18:02:24 -080093 }
94 paramIndex++;
95 urlSegmentIndex = std::string_view::npos;
96 }
97 }
98 if (urlSegmentIndex != std::string_view::npos)
Ed Tanous988403c2020-08-24 11:29:49 -070099 {
100 return 0;
101 }
Ed Tanous1c30e502022-03-08 18:02:24 -0800102 return tagValue;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700103}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700104} // namespace black_magic
Ed Tanous7045c8d2017-04-03 10:04:37 -0700105
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106namespace utility
107{
Ed Tanous7045c8d2017-04-03 10:04:37 -0700108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109template <typename T>
Ed Tanousc867a832022-03-10 14:17:00 -0800110struct FunctionTraits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700111{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500112 template <size_t i>
Ed Tanousc867a832022-03-10 14:17:00 -0800113 using arg = std::tuple_element_t<i, boost::callable_traits::args_t<T>>;
Ed Tanous7045c8d2017-04-03 10:04:37 -0700114};
115
Ed Tanouscfe3bc02023-06-26 12:47:24 -0700116constexpr size_t numArgsFromTag(int tag)
117{
118 size_t ret = 0;
119 while (tag > 0)
120 {
121 // Move to the next tag by removing the bottom bits from the number
122 tag /= black_magic::toUnderlying(black_magic::TypeCode::Max);
123 ret++;
124 }
125 return ret;
126};
127
Ed Tanous26ccae32023-02-16 10:28:44 -0800128inline std::string base64encode(std::string_view data)
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600129{
130 const std::array<char, 64> key = {
131 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
132 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
133 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
134 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
135 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
136
137 size_t size = data.size();
138 std::string ret;
139 ret.resize((size + 2) / 3 * 4);
140 auto it = ret.begin();
141
142 size_t i = 0;
143 while (i < size)
144 {
Ed Tanous543f4402022-01-06 13:12:53 -0800145 size_t keyIndex = 0;
Adriana Kobylakd830ff52021-01-27 14:15:27 -0600146
147 keyIndex = static_cast<size_t>(data[i] & 0xFC) >> 2;
148 *it++ = key[keyIndex];
149
150 if (i + 1 < size)
151 {
152 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
153 keyIndex += static_cast<size_t>(data[i + 1] & 0xF0) >> 4;
154 *it++ = key[keyIndex];
155
156 if (i + 2 < size)
157 {
158 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
159 keyIndex += static_cast<size_t>(data[i + 2] & 0xC0) >> 6;
160 *it++ = key[keyIndex];
161
162 keyIndex = static_cast<size_t>(data[i + 2] & 0x3F);
163 *it++ = key[keyIndex];
164 }
165 else
166 {
167 keyIndex = static_cast<size_t>(data[i + 1] & 0x0F) << 2;
168 *it++ = key[keyIndex];
169 *it++ = '=';
170 }
171 }
172 else
173 {
174 keyIndex = static_cast<size_t>(data[i] & 0x03) << 4;
175 *it++ = key[keyIndex];
176 *it++ = '=';
177 *it++ = '=';
178 }
179
180 i += 3;
181 }
182
183 return ret;
184}
185
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100186// TODO this is temporary and should be deleted once base64 is refactored out of
187// crow
Ed Tanous26ccae32023-02-16 10:28:44 -0800188inline bool base64Decode(std::string_view input, std::string& output)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700189{
Ed Tanous271584a2019-07-09 16:24:22 -0700190 static const char nop = static_cast<char>(-1);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700191 // See note on encoding_data[] in above function
Jonathan Doman5beaf842020-08-14 11:23:33 -0700192 static const std::array<char, 256> decodingData = {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700193 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
194 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
195 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
196 nop, 62, nop, nop, nop, 63, 52, 53, 54, 55, 56, 57, 58, 59,
197 60, 61, nop, nop, nop, nop, nop, nop, nop, 0, 1, 2, 3, 4,
198 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
199 19, 20, 21, 22, 23, 24, 25, nop, nop, nop, nop, nop, nop, 26,
200 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
201 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, nop, nop, nop,
202 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
203 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
204 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
205 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
206 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
207 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
208 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
209 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
210 nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop, nop,
211 nop, nop, nop, nop};
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100212
Ed Tanous1abe55e2018-09-05 08:30:59 -0700213 size_t inputLength = input.size();
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100214
Ed Tanous1abe55e2018-09-05 08:30:59 -0700215 // allocate space for output string
216 output.clear();
217 output.reserve(((inputLength + 2) / 3) * 4);
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100218
Jonathan Doman5beaf842020-08-14 11:23:33 -0700219 auto getCodeValue = [](char c) {
220 auto code = static_cast<unsigned char>(c);
221 // Ensure we cannot index outside the bounds of the decoding array
222 static_assert(std::numeric_limits<decltype(code)>::max() <
223 decodingData.size());
224 return decodingData[code];
225 };
226
Ed Tanous1abe55e2018-09-05 08:30:59 -0700227 // for each 4-bytes sequence from the input, extract 4 6-bits sequences by
Gunnar Millscaa3ce32020-07-08 14:46:53 -0500228 // dropping first two bits
Ed Tanous1abe55e2018-09-05 08:30:59 -0700229 // and regenerate into 3 8-bits sequences
James Feist5a806642020-07-31 16:40:33 +0000230
Ed Tanous1abe55e2018-09-05 08:30:59 -0700231 for (size_t i = 0; i < inputLength; i++)
232 {
Ed Tanous543f4402022-01-06 13:12:53 -0800233 char base64code0 = 0;
234 char base64code1 = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700235 char base64code2 = 0; // initialized to 0 to suppress warnings
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100236
Jonathan Doman5beaf842020-08-14 11:23:33 -0700237 base64code0 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700238 if (base64code0 == nop)
239 { // non base64 character
240 return false;
241 }
242 if (!(++i < inputLength))
243 { // we need at least two input bytes for first
244 // byte output
245 return false;
246 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700247 base64code1 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700248 if (base64code1 == nop)
249 { // non base64 character
250 return false;
251 }
252 output +=
253 static_cast<char>((base64code0 << 2) | ((base64code1 >> 4) & 0x3));
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100254
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 if (++i < inputLength)
256 {
257 char c = input[i];
258 if (c == '=')
259 { // padding , end of input
260 return (base64code1 & 0x0f) == 0;
261 }
Jonathan Doman5beaf842020-08-14 11:23:33 -0700262 base64code2 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700263 if (base64code2 == nop)
264 { // non base64 character
265 return false;
266 }
267 output += static_cast<char>(((base64code1 << 4) & 0xf0) |
268 ((base64code2 >> 2) & 0x0f));
269 }
270
271 if (++i < inputLength)
272 {
273 char c = input[i];
274 if (c == '=')
275 { // padding , end of input
276 return (base64code2 & 0x03) == 0;
277 }
Ed Tanousf8fe53e2022-06-30 15:55:45 -0700278 char base64code3 = getCodeValue(input[i]);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700279 if (base64code3 == nop)
280 { // non base64 character
281 return false;
282 }
283 output +=
284 static_cast<char>((((base64code2 << 6) & 0xc0) | base64code3));
285 }
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100286 }
287
Ed Tanous1abe55e2018-09-05 08:30:59 -0700288 return true;
Borawski.Lukasz9d8fd302018-01-05 14:56:09 +0100289}
290
Ed Tanous26ccae32023-02-16 10:28:44 -0800291inline bool constantTimeStringCompare(std::string_view a, std::string_view b)
Ed Tanous51dae672018-09-05 16:07:32 -0700292{
293 // Important note, this function is ONLY constant time if the two input
294 // sizes are the same
295 if (a.size() != b.size())
296 {
297 return false;
298 }
299 return CRYPTO_memcmp(a.data(), b.data(), a.size()) == 0;
300}
301
302struct ConstantTimeCompare
303{
Ed Tanous26ccae32023-02-16 10:28:44 -0800304 bool operator()(std::string_view a, std::string_view b) const
Ed Tanous51dae672018-09-05 16:07:32 -0700305 {
306 return constantTimeStringCompare(a, b);
307 }
308};
309
Ed Tanouseae855c2021-10-26 11:26:02 -0700310namespace details
311{
312inline boost::urls::url
Willy Tuc6bcedc2022-09-27 05:36:59 +0000313 appendUrlPieces(boost::urls::url& url,
314 const std::initializer_list<std::string_view> args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700315{
Ed Tanous26ccae32023-02-16 10:28:44 -0800316 for (std::string_view arg : args)
Ed Tanouseae855c2021-10-26 11:26:02 -0700317 {
318 url.segments().push_back(arg);
319 }
320 return url;
321}
Willy Tuc6bcedc2022-09-27 05:36:59 +0000322
Ed Tanouseae855c2021-10-26 11:26:02 -0700323} // namespace details
324
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700325class OrMorePaths
326{};
327
Ed Tanouseae855c2021-10-26 11:26:02 -0700328template <typename... AV>
Willy Tuc6bcedc2022-09-27 05:36:59 +0000329inline void appendUrlPieces(boost::urls::url& url, const AV... args)
330{
331 details::appendUrlPieces(url, {args...});
332}
333
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100334namespace details
335{
336
337// std::reference_wrapper<std::string> - extracts segment to variable
338// std::string_view - checks if segment is equal to variable
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700339using UrlSegment = std::variant<std::reference_wrapper<std::string>,
340 std::string_view, OrMorePaths>;
341
342enum class UrlParseResult
343{
344 Continue,
345 Fail,
346 Done,
347};
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100348
349class UrlSegmentMatcherVisitor
350{
351 public:
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700352 UrlParseResult operator()(std::string& output)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100353 {
Ed Tanous079360a2022-06-29 10:05:19 -0700354 output = segment;
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700355 return UrlParseResult::Continue;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100356 }
357
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700358 UrlParseResult operator()(std::string_view expected)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100359 {
Ed Tanous079360a2022-06-29 10:05:19 -0700360 if (segment == expected)
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700361 {
362 return UrlParseResult::Continue;
363 }
364 return UrlParseResult::Fail;
365 }
366
367 UrlParseResult operator()(OrMorePaths /*unused*/)
368 {
369 return UrlParseResult::Done;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100370 }
371
Ed Tanous079360a2022-06-29 10:05:19 -0700372 explicit UrlSegmentMatcherVisitor(std::string_view segmentIn) :
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100373 segment(segmentIn)
374 {}
375
376 private:
Ed Tanous079360a2022-06-29 10:05:19 -0700377 std::string_view segment;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100378};
379
Ed Tanousd9f466b2023-03-06 15:04:25 -0800380inline bool readUrlSegments(boost::urls::url_view url,
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100381 std::initializer_list<UrlSegment>&& segments)
382{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800383 boost::urls::segments_view urlSegments = url.segments();
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100384
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700385 if (!urlSegments.is_absolute())
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100386 {
387 return false;
388 }
389
390 boost::urls::segments_view::iterator it = urlSegments.begin();
391 boost::urls::segments_view::iterator end = urlSegments.end();
392
393 for (const auto& segment : segments)
394 {
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700395 if (it == end)
396 {
397 // If the request ends with an "any" path, this was successful
398 return std::holds_alternative<OrMorePaths>(segment);
399 }
400 UrlParseResult res = std::visit(UrlSegmentMatcherVisitor(*it), segment);
401 if (res == UrlParseResult::Done)
402 {
403 return true;
404 }
405 if (res == UrlParseResult::Fail)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100406 {
407 return false;
408 }
409 it++;
410 }
Carson Labrado4c30e222022-06-24 22:16:00 +0000411
412 // There will be an empty segment at the end if the URI ends with a "/"
413 // e.g. /redfish/v1/Chassis/
414 if ((it != end) && urlSegments.back().empty())
415 {
416 it++;
417 }
Ed Tanous7f8d8fa2022-08-19 07:00:38 -0700418 return it == end;
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100419}
420
421} // namespace details
422
423template <typename... Args>
Ed Tanousd9f466b2023-03-06 15:04:25 -0800424inline bool readUrlSegments(boost::urls::url_view url, Args&&... args)
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100425{
Ed Tanous39662a32023-02-06 15:09:46 -0800426 return details::readUrlSegments(url, {std::forward<Args>(args)...});
Szymon Dompkeca1600c2022-03-03 14:42:52 +0100427}
428
Ed Tanousd9f466b2023-03-06 15:04:25 -0800429inline boost::urls::url replaceUrlSegment(boost::urls::url_view urlView,
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000430 const uint replaceLoc,
Ed Tanous26ccae32023-02-16 10:28:44 -0800431 std::string_view newSegment)
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000432{
Ed Tanousd9f466b2023-03-06 15:04:25 -0800433 boost::urls::segments_view urlSegments = urlView.segments();
Carson Labrado1c0bb5c2022-05-18 00:12:52 +0000434 boost::urls::url url("/");
435
436 if (!urlSegments.is_absolute())
437 {
438 return url;
439 }
440
441 boost::urls::segments_view::iterator it = urlSegments.begin();
442 boost::urls::segments_view::iterator end = urlSegments.end();
443
444 for (uint idx = 0; it != end; it++, idx++)
445 {
446 if (idx == replaceLoc)
447 {
448 url.segments().push_back(newSegment);
449 }
450 else
451 {
452 url.segments().push_back(*it);
453 }
454 }
455
456 return url;
457}
458
Ed Tanousd9f466b2023-03-06 15:04:25 -0800459inline std::string setProtocolDefaults(boost::urls::url_view urlView)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800460{
Ed Tanous39662a32023-02-06 15:09:46 -0800461 if (urlView.scheme() == "https")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800462 {
463 return "https";
464 }
Ed Tanous39662a32023-02-06 15:09:46 -0800465 if (urlView.scheme() == "http")
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800466 {
467 if (bmcwebInsecureEnableHttpPushStyleEventing)
468 {
469 return "http";
470 }
471 return "";
472 }
Chicago Duan3d307082020-11-26 14:12:12 +0800473 if (urlView.scheme() == "snmp")
474 {
475 return "snmp";
476 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800477 return "";
478}
479
Ed Tanousd9f466b2023-03-06 15:04:25 -0800480inline uint16_t setPortDefaults(boost::urls::url_view url)
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800481{
482 uint16_t port = url.port_number();
483 if (port != 0)
484 {
485 // user picked a port already.
486 return port;
487 }
488
489 // If the user hasn't explicitly stated a port, pick one explicitly for them
490 // based on the protocol defaults
491 if (url.scheme() == "http")
492 {
493 return 80;
494 }
495 if (url.scheme() == "https")
496 {
497 return 443;
498 }
Chicago Duan3d307082020-11-26 14:12:12 +0800499 if (url.scheme() == "snmp")
500 {
501 return 162;
502 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800503 return 0;
504}
505
Ed Tanous11baefe2022-02-09 12:14:12 -0800506inline bool validateAndSplitUrl(std::string_view destUrl, std::string& urlProto,
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800507 std::string& host, uint16_t& port,
Ed Tanous11baefe2022-02-09 12:14:12 -0800508 std::string& path)
509{
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800510 boost::urls::result<boost::urls::url_view> url =
Ed Tanous079360a2022-06-29 10:05:19 -0700511 boost::urls::parse_uri(destUrl);
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800512 if (!url)
513 {
514 return false;
515 }
516 urlProto = setProtocolDefaults(url.value());
517 if (urlProto.empty())
Ed Tanous11baefe2022-02-09 12:14:12 -0800518 {
519 return false;
520 }
521
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800522 port = setPortDefaults(url.value());
Ed Tanous11baefe2022-02-09 12:14:12 -0800523
Ed Tanous079360a2022-06-29 10:05:19 -0700524 host = url->encoded_host();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800525
Ed Tanous079360a2022-06-29 10:05:19 -0700526 path = url->encoded_path();
Ed Tanous11baefe2022-02-09 12:14:12 -0800527 if (path.empty())
528 {
529 path = "/";
530 }
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800531 if (url->has_fragment())
532 {
533 path += '#';
Ed Tanous079360a2022-06-29 10:05:19 -0700534 path += url->encoded_fragment();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800535 }
536
537 if (url->has_query())
538 {
539 path += '?';
Ed Tanous079360a2022-06-29 10:05:19 -0700540 path += url->encoded_query();
Ed Tanouseb1c47d2022-02-09 11:47:27 -0800541 }
542
Ed Tanous11baefe2022-02-09 12:14:12 -0800543 return true;
544}
545
Ed Tanous1abe55e2018-09-05 08:30:59 -0700546} // namespace utility
547} // namespace crow
Ed Tanous71f2db72022-05-25 12:28:09 -0700548
549namespace nlohmann
550{
551template <>
552struct adl_serializer<boost::urls::url>
553{
554 // nlohmann requires a specific casing to look these up in adl
555 // NOLINTNEXTLINE(readability-identifier-naming)
556 static void to_json(json& j, const boost::urls::url& url)
557 {
Ed Tanous079360a2022-06-29 10:05:19 -0700558 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700559 }
560};
561
562template <>
563struct adl_serializer<boost::urls::url_view>
564{
565 // NOLINTNEXTLINE(readability-identifier-naming)
Ed Tanousd9f466b2023-03-06 15:04:25 -0800566 static void to_json(json& j, boost::urls::url_view url)
Ed Tanous71f2db72022-05-25 12:28:09 -0700567 {
Ed Tanous079360a2022-06-29 10:05:19 -0700568 j = url.buffer();
Ed Tanous71f2db72022-05-25 12:28:09 -0700569 }
570};
571} // namespace nlohmann