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