blob: c6c0e3ed1077bb9d0ddc08153897a7935322b21b [file] [log] [blame]
Ed Tanous9bd21fc2018-04-26 16:08:56 -07001#pragma once
Tanousf00032d2018-11-05 01:18:10 -03002
Ed Tanous18f8f602023-07-18 10:07:23 -07003#include <algorithm>
Nan Zhoud5c80ad2022-07-11 01:16:31 +00004#include <cctype>
5#include <iomanip>
6#include <ostream>
Ed Tanous3544d2a2023-08-06 18:12:20 -07007#include <ranges>
Ed Tanous99351cd2022-08-07 16:42:51 -07008#include <span>
Nan Zhoud5c80ad2022-07-11 01:16:31 +00009#include <string>
10#include <string_view>
11#include <vector>
12
13// IWYU pragma: no_include <ctype.h>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050014
Ed Tanous1abe55e2018-09-05 08:30:59 -070015namespace http_helpers
16{
George Liu647b3cd2021-07-05 12:43:56 +080017
Ed Tanous99351cd2022-08-07 16:42:51 -070018enum class ContentType
George Liu647b3cd2021-07-05 12:43:56 +080019{
Ed Tanous99351cd2022-08-07 16:42:51 -070020 NoMatch,
Ed Tanous4a0e1a02022-09-21 15:28:04 -070021 ANY, // Accepts: */*
Ed Tanous99351cd2022-08-07 16:42:51 -070022 CBOR,
23 HTML,
24 JSON,
25 OctetStream,
Ed Tanous6fde95f2023-06-01 07:33:34 -070026 EventStream,
Ed Tanous99351cd2022-08-07 16:42:51 -070027};
28
29struct ContentTypePair
30{
31 std::string_view contentTypeString;
32 ContentType contentTypeEnum;
33};
34
Ed Tanous6fde95f2023-06-01 07:33:34 -070035constexpr std::array<ContentTypePair, 5> contentTypes{{
Ed Tanous99351cd2022-08-07 16:42:51 -070036 {"application/cbor", ContentType::CBOR},
37 {"application/json", ContentType::JSON},
38 {"application/octet-stream", ContentType::OctetStream},
39 {"text/html", ContentType::HTML},
Ed Tanous6fde95f2023-06-01 07:33:34 -070040 {"text/event-stream", ContentType::EventStream},
Ed Tanous99351cd2022-08-07 16:42:51 -070041}};
42
Patrick Williamsbd79bce2024-08-16 15:22:20 -040043inline ContentType getPreferredContentType(
44 std::string_view header, std::span<const ContentType> preferedOrder)
Ed Tanous99351cd2022-08-07 16:42:51 -070045{
Ed Tanous99351cd2022-08-07 16:42:51 -070046 size_t lastIndex = 0;
47 while (lastIndex < header.size() + 1)
Ed Tanous1abe55e2018-09-05 08:30:59 -070048 {
Ed Tanousf8fe53e2022-06-30 15:55:45 -070049 size_t index = header.find(',', lastIndex);
Ed Tanous99351cd2022-08-07 16:42:51 -070050 if (index == std::string_view::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -070051 {
Ed Tanous99351cd2022-08-07 16:42:51 -070052 index = header.size();
Ed Tanous1abe55e2018-09-05 08:30:59 -070053 }
Ed Tanous99351cd2022-08-07 16:42:51 -070054 std::string_view encoding = header.substr(lastIndex, index);
Ed Tanous6b5e77d2018-11-16 14:52:56 -080055
Ed Tanous99351cd2022-08-07 16:42:51 -070056 if (!header.empty())
57 {
58 header.remove_prefix(1);
59 }
60 lastIndex = index + 1;
Gunnar Millsa3526fe2022-02-02 21:56:44 +000061 // ignore any q-factor weighting (;q=)
62 std::size_t separator = encoding.find(";q=");
63
64 if (separator != std::string_view::npos)
65 {
66 encoding = encoding.substr(0, separator);
67 }
Ed Tanous99351cd2022-08-07 16:42:51 -070068 // If the client allows any encoding, given them the first one on the
69 // servers list
70 if (encoding == "*/*")
George Liu647b3cd2021-07-05 12:43:56 +080071 {
Ed Tanous4a0e1a02022-09-21 15:28:04 -070072 return ContentType::ANY;
George Liu647b3cd2021-07-05 12:43:56 +080073 }
Ed Tanous3544d2a2023-08-06 18:12:20 -070074 const auto* knownContentType = std::ranges::find_if(
75 contentTypes, [encoding](const ContentTypePair& pair) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040076 return pair.contentTypeString == encoding;
77 });
Ed Tanous99351cd2022-08-07 16:42:51 -070078
79 if (knownContentType == contentTypes.end())
80 {
81 // not able to find content type in list
82 continue;
83 }
84
85 // Not one of the types requested
Ed Tanous3544d2a2023-08-06 18:12:20 -070086 if (std::ranges::find(preferedOrder,
87 knownContentType->contentTypeEnum) ==
88 preferedOrder.end())
Ed Tanous99351cd2022-08-07 16:42:51 -070089 {
90 continue;
91 }
92 return knownContentType->contentTypeEnum;
George Liu647b3cd2021-07-05 12:43:56 +080093 }
Ed Tanous99351cd2022-08-07 16:42:51 -070094 return ContentType::NoMatch;
95}
96
Ed Tanous4a0e1a02022-09-21 15:28:04 -070097inline bool isContentTypeAllowed(std::string_view header, ContentType type,
98 bool allowWildcard)
Ed Tanous99351cd2022-08-07 16:42:51 -070099{
100 auto types = std::to_array({type});
Ed Tanous8ece0e42024-01-02 13:16:50 -0800101 ContentType allowed = getPreferredContentType(header, types);
Ed Tanous4a0e1a02022-09-21 15:28:04 -0700102 if (allowed == ContentType::ANY)
103 {
104 return allowWildcard;
105 }
106
107 return type == allowed;
George Liu647b3cd2021-07-05 12:43:56 +0800108}
109
Ed Tanous23a21a12020-07-25 04:45:05 +0000110} // namespace http_helpers