xref: /openbmc/bmcweb/include/http_utility.hpp (revision 478b7adf)
1 #pragma once
2 
3 #include <algorithm>
4 #include <cctype>
5 #include <iomanip>
6 #include <ostream>
7 #include <ranges>
8 #include <span>
9 #include <string>
10 #include <string_view>
11 #include <vector>
12 
13 namespace http_helpers
14 {
15 
16 enum class ContentType
17 {
18     NoMatch,
19     ANY, // Accepts: */*
20     CBOR,
21     HTML,
22     JSON,
23     OctetStream,
24     EventStream,
25 };
26 
27 struct ContentTypePair
28 {
29     std::string_view contentTypeString;
30     ContentType contentTypeEnum;
31 };
32 
33 constexpr std::array<ContentTypePair, 5> contentTypes{{
34     {"application/cbor", ContentType::CBOR},
35     {"application/json", ContentType::JSON},
36     {"application/octet-stream", ContentType::OctetStream},
37     {"text/html", ContentType::HTML},
38     {"text/event-stream", ContentType::EventStream},
39 }};
40 
getPreferredContentType(std::string_view header,std::span<const ContentType> preferedOrder)41 inline ContentType getPreferredContentType(
42     std::string_view header, std::span<const ContentType> preferedOrder)
43 {
44     size_t lastIndex = 0;
45     while (lastIndex < header.size() + 1)
46     {
47         size_t index = header.find(',', lastIndex);
48         if (index == std::string_view::npos)
49         {
50             index = header.size();
51         }
52         std::string_view encoding = header.substr(lastIndex, index);
53 
54         if (!header.empty())
55         {
56             header.remove_prefix(1);
57         }
58         lastIndex = index + 1;
59         // ignore any q-factor weighting (;q=)
60         std::size_t separator = encoding.find(";q=");
61 
62         if (separator != std::string_view::npos)
63         {
64             encoding = encoding.substr(0, separator);
65         }
66         // If the client allows any encoding, given them the first one on the
67         // servers list
68         if (encoding == "*/*")
69         {
70             return ContentType::ANY;
71         }
72         const auto* knownContentType = std::ranges::find_if(
73             contentTypes, [encoding](const ContentTypePair& pair) {
74                 return pair.contentTypeString == encoding;
75             });
76 
77         if (knownContentType == contentTypes.end())
78         {
79             // not able to find content type in list
80             continue;
81         }
82 
83         // Not one of the types requested
84         if (std::ranges::find(preferedOrder,
85                               knownContentType->contentTypeEnum) ==
86             preferedOrder.end())
87         {
88             continue;
89         }
90         return knownContentType->contentTypeEnum;
91     }
92     return ContentType::NoMatch;
93 }
94 
isContentTypeAllowed(std::string_view header,ContentType type,bool allowWildcard)95 inline bool isContentTypeAllowed(std::string_view header, ContentType type,
96                                  bool allowWildcard)
97 {
98     auto types = std::to_array({type});
99     ContentType allowed = getPreferredContentType(header, types);
100     if (allowed == ContentType::ANY)
101     {
102         return allowWildcard;
103     }
104 
105     return type == allowed;
106 }
107 
108 } // namespace http_helpers
109