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