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 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 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