#pragma once #include #include #include #include #include #include #include #include #include #include namespace http_helpers { enum class ContentType { NoMatch, ANY, // Accepts: */* CBOR, HTML, JSON, OctetStream, EventStream, }; inline ContentType getPreferredContentType( std::string_view header, std::span preferredOrder) { using boost::spirit::x3::char_; using boost::spirit::x3::lit; using boost::spirit::x3::omit; using boost::spirit::x3::parse; using boost::spirit::x3::space; using boost::spirit::x3::symbols; using boost::spirit::x3::uint_; const symbols knownMimeType{ {"application/cbor", ContentType::CBOR}, {"application/json", ContentType::JSON}, {"application/octet-stream", ContentType::OctetStream}, {"text/html", ContentType::HTML}, {"text/event-stream", ContentType::EventStream}, {"*/*", ContentType::ANY}}; std::vector ct; auto parameters = *(lit(';') >> lit("q=") >> uint_ >> -(lit('.') >> uint_)); auto typeCharset = char_("a-zA-Z.+-"); auto mimeType = knownMimeType | omit[+typeCharset >> lit('/') >> +typeCharset]; auto parser = +(mimeType >> omit[parameters >> -char_(',') >> *space]); if (!parse(header.begin(), header.end(), parser, ct)) { return ContentType::NoMatch; } for (const ContentType parsedType : ct) { if (parsedType == ContentType::ANY) { return parsedType; } auto it = std::ranges::find(preferredOrder, parsedType); if (it != preferredOrder.end()) { return *it; } } return ContentType::NoMatch; } inline bool isContentTypeAllowed(std::string_view header, ContentType type, bool allowWildcard) { auto types = std::to_array({type}); ContentType allowed = getPreferredContentType(header, types); if (allowed == ContentType::ANY) { return allowWildcard; } return type == allowed; } enum class Encoding { ParseError, NoMatch, UnencodedBytes, GZIP, ZSTD, ANY, // represents *. Never returned. Only used for string matching }; inline Encoding getPreferredEncoding(std::string_view acceptEncoding, const std::span availableEncodings) { if (acceptEncoding.empty()) { return Encoding::UnencodedBytes; } using boost::spirit::x3::char_; using boost::spirit::x3::lit; using boost::spirit::x3::omit; using boost::spirit::x3::parse; using boost::spirit::x3::space; using boost::spirit::x3::symbols; using boost::spirit::x3::uint_; const symbols knownAcceptEncoding{{"gzip", Encoding::GZIP}, {"zstd", Encoding::ZSTD}, {"*", Encoding::ANY}}; std::vector ct; auto parameters = *(lit(';') >> lit("q=") >> uint_ >> -(lit('.') >> uint_)); auto typeCharset = char_("a-zA-Z.+-"); auto encodeType = knownAcceptEncoding | omit[+typeCharset]; auto parser = +(encodeType >> omit[parameters >> -char_(',') >> *space]); if (!parse(acceptEncoding.begin(), acceptEncoding.end(), parser, ct)) { return Encoding::ParseError; } for (const Encoding parsedType : ct) { if (parsedType == Encoding::ANY) { if (!availableEncodings.empty()) { return *availableEncodings.begin(); } } auto it = std::ranges::find(availableEncodings, parsedType); if (it != availableEncodings.end()) { return *it; } } // Fall back to raw bytes if it was allowed auto it = std::ranges::find(availableEncodings, Encoding::UnencodedBytes); if (it != availableEncodings.end()) { return *it; } return Encoding::NoMatch; } } // namespace http_helpers