xref: /openbmc/bmcweb/include/http_utility.hpp (revision 463b2934e2857195fa56003fdff0127d130089d0)
1 #pragma once
2 
3 #include <boost/spirit/home/x3.hpp>
4 
5 #include <algorithm>
6 #include <cctype>
7 #include <iomanip>
8 #include <ostream>
9 #include <ranges>
10 #include <span>
11 #include <string>
12 #include <string_view>
13 #include <vector>
14 
15 namespace http_helpers
16 {
17 
18 enum class ContentType
19 {
20     NoMatch,
21     ANY, // Accepts: */*
22     CBOR,
23     HTML,
24     JSON,
25     OctetStream,
26     EventStream,
27 };
28 
29 inline ContentType getPreferredContentType(
30     std::string_view header, std::span<const ContentType> preferedOrder)
31 {
32     using boost::spirit::x3::char_;
33     using boost::spirit::x3::lit;
34     using boost::spirit::x3::omit;
35     using boost::spirit::x3::parse;
36     using boost::spirit::x3::space;
37     using boost::spirit::x3::symbols;
38     using boost::spirit::x3::uint_;
39 
40     const symbols<ContentType> knownMimeType{
41         {"application/cbor", ContentType::CBOR},
42         {"application/json", ContentType::JSON},
43         {"application/octet-stream", ContentType::OctetStream},
44         {"text/html", ContentType::HTML},
45         {"text/event-stream", ContentType::EventStream},
46         {"*/*", ContentType::ANY}};
47 
48     std::vector<ContentType> ct;
49 
50     auto parameters = *(lit(';') >> lit("q=") >> uint_ >> -(lit('.') >> uint_));
51     auto typeCharset = char_("a-zA-Z.+-");
52     auto mimeType = knownMimeType |
53                     omit[+typeCharset >> lit('/') >> +typeCharset];
54     auto parser = +(mimeType >> omit[parameters >> -char_(',') >> *space]);
55     if (!parse(header.begin(), header.end(), parser, ct))
56     {
57         return ContentType::NoMatch;
58     }
59 
60     for (const ContentType parsedType : ct)
61     {
62         if (parsedType == ContentType::ANY)
63         {
64             return parsedType;
65         }
66         auto it = std::ranges::find(preferedOrder, parsedType);
67         if (it != preferedOrder.end())
68         {
69             return *it;
70         }
71     }
72 
73     return ContentType::NoMatch;
74 }
75 
76 inline bool isContentTypeAllowed(std::string_view header, ContentType type,
77                                  bool allowWildcard)
78 {
79     auto types = std::to_array({type});
80     ContentType allowed = getPreferredContentType(header, types);
81     if (allowed == ContentType::ANY)
82     {
83         return allowWildcard;
84     }
85 
86     return type == allowed;
87 }
88 
89 } // namespace http_helpers
90