xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/common/structured_headers.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef QUICHE_COMMON_STRUCTURED_HEADERS_H_
6 #define QUICHE_COMMON_STRUCTURED_HEADERS_H_
7 
8 #include <cstddef>
9 #include <cstdint>
10 #include <map>
11 #include <optional>
12 #include <string>
13 #include <tuple>
14 #include <utility>
15 #include <vector>
16 
17 #include "absl/strings/string_view.h"
18 #include "absl/types/variant.h"
19 #include "quiche/common/platform/api/quiche_export.h"
20 #include "quiche/common/platform/api/quiche_logging.h"
21 
22 namespace quiche {
23 namespace structured_headers {
24 
25 // This file implements parsing of HTTP structured headers, as defined in
26 // RFC8941 (https://www.rfc-editor.org/rfc/rfc8941.html). For compatibility with
27 // the shipped implementation of Web Packaging, this file also supports a
28 // previous revision of the standard, referred to here as "Draft 9".
29 // (https://datatracker.ietf.org/doc/draft-ietf-httpbis-header-structure/09/)
30 //
31 // The major difference between the two revisions is in the various list
32 // formats: Draft 9 describes "parameterised lists" and "lists-of-lists", while
33 // the final RFC uses a single "list" syntax, whose members may be inner lists.
34 // There should be no ambiguity, however, as the code which calls this parser
35 // should be expecting only a single type for a given header.
36 //
37 // References within the code are tagged with either [SH09] or [RFC8941],
38 // depending on which revision they refer to.
39 //
40 // Currently supported data types are:
41 //  Item:
42 //   integer: 123
43 //   string: "abc"
44 //   token: abc
45 //   byte sequence: *YWJj*
46 //  Parameterised list: abc_123;a=1;b=2; cdef_456, ghi;q="9";r="w"
47 //  List-of-lists: "foo";"bar", "baz", "bat"; "one"
48 //  List: "foo", "bar", "It was the best of times."
49 //        ("foo" "bar"), ("baz"), ("bat" "one"), ()
50 //        abc;a=1;b=2; cde_456, (ghi jkl);q="9";r=w
51 //  Dictionary: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid=?0
52 //
53 // Functions are provided to parse each of these, which are intended to be
54 // called with the complete value of an HTTP header (that is, any
55 // sub-structure will be handled internally by the parser; the exported
56 // functions are not intended to be called on partial header strings.) Input
57 // values should be ASCII byte strings (non-ASCII characters should not be
58 // present in Structured Header values, and will cause the entire header to fail
59 // to parse.)
60 
61 class QUICHE_EXPORT Item {
62  public:
63   enum ItemType {
64     kNullType,
65     kIntegerType,
66     kDecimalType,
67     kStringType,
68     kTokenType,
69     kByteSequenceType,
70     kBooleanType
71   };
72   Item();
73   explicit Item(int64_t value);
74   explicit Item(double value);
75   explicit Item(bool value);
76 
77   // Constructors for string-like items: Strings, Tokens and Byte Sequences.
78   Item(const char* value, Item::ItemType type = kStringType);
79   Item(std::string value, Item::ItemType type = kStringType);
80 
81   QUICHE_EXPORT friend bool operator==(const Item& lhs, const Item& rhs);
82   inline friend bool operator!=(const Item& lhs, const Item& rhs) {
83     return !(lhs == rhs);
84   }
85 
is_null()86   bool is_null() const { return Type() == kNullType; }
is_integer()87   bool is_integer() const { return Type() == kIntegerType; }
is_decimal()88   bool is_decimal() const { return Type() == kDecimalType; }
is_string()89   bool is_string() const { return Type() == kStringType; }
is_token()90   bool is_token() const { return Type() == kTokenType; }
is_byte_sequence()91   bool is_byte_sequence() const { return Type() == kByteSequenceType; }
is_boolean()92   bool is_boolean() const { return Type() == kBooleanType; }
93 
GetInteger()94   int64_t GetInteger() const {
95     const auto* value = absl::get_if<int64_t>(&value_);
96     QUICHE_CHECK(value);
97     return *value;
98   }
GetDecimal()99   double GetDecimal() const {
100     const auto* value = absl::get_if<double>(&value_);
101     QUICHE_CHECK(value);
102     return *value;
103   }
GetBoolean()104   bool GetBoolean() const {
105     const auto* value = absl::get_if<bool>(&value_);
106     QUICHE_CHECK(value);
107     return *value;
108   }
109   // TODO(iclelland): Split up accessors for String, Token and Byte Sequence.
GetString()110   const std::string& GetString() const {
111     struct Visitor {
112       const std::string* operator()(const absl::monostate&) { return nullptr; }
113       const std::string* operator()(const int64_t&) { return nullptr; }
114       const std::string* operator()(const double&) { return nullptr; }
115       const std::string* operator()(const std::string& value) { return &value; }
116       const std::string* operator()(const bool&) { return nullptr; }
117     };
118     const std::string* value = absl::visit(Visitor(), value_);
119     QUICHE_CHECK(value);
120     return *value;
121   }
122 
123   // Transfers ownership of the underlying String, Token, or Byte Sequence.
TakeString()124   std::string TakeString() && {
125     struct Visitor {
126       std::string* operator()(absl::monostate&) { return nullptr; }
127       std::string* operator()(int64_t&) { return nullptr; }
128       std::string* operator()(double&) { return nullptr; }
129       std::string* operator()(std::string& value) { return &value; }
130       std::string* operator()(bool&) { return nullptr; }
131     };
132     std::string* value = absl::visit(Visitor(), value_);
133     QUICHE_CHECK(value);
134     return std::move(*value);
135   }
136 
Type()137   ItemType Type() const { return static_cast<ItemType>(value_.index()); }
138 
139  private:
140   absl::variant<absl::monostate, int64_t, double, std::string, std::string,
141                 std::string, bool>
142       value_;
143 };
144 
145 // Returns a human-readable representation of an ItemType.
146 QUICHE_EXPORT absl::string_view ItemTypeToString(Item::ItemType type);
147 
148 // Returns `true` if the string is a valid Token value.
149 QUICHE_EXPORT bool IsValidToken(absl::string_view str);
150 
151 // Holds a ParameterizedIdentifier (draft 9 only). The contained Item must be a
152 // Token, and there may be any number of parameters. Parameter ordering is not
153 // significant.
154 struct QUICHE_EXPORT ParameterisedIdentifier {
155   using Parameters = std::map<std::string, Item>;
156 
157   Item identifier;
158   Parameters params;
159 
160   ParameterisedIdentifier();
161   ParameterisedIdentifier(const ParameterisedIdentifier&);
162   ParameterisedIdentifier& operator=(const ParameterisedIdentifier&);
163   ParameterisedIdentifier(Item, Parameters);
164   ~ParameterisedIdentifier();
165 };
166 
167 inline bool operator==(const ParameterisedIdentifier& lhs,
168                        const ParameterisedIdentifier& rhs) {
169   return std::tie(lhs.identifier, lhs.params) ==
170          std::tie(rhs.identifier, rhs.params);
171 }
172 
173 using Parameters = std::vector<std::pair<std::string, Item>>;
174 
175 struct QUICHE_EXPORT ParameterizedItem {
176   Item item;
177   Parameters params;
178 
179   ParameterizedItem();
180   ParameterizedItem(const ParameterizedItem&);
181   ParameterizedItem& operator=(const ParameterizedItem&);
182   ParameterizedItem(Item, Parameters);
183   ~ParameterizedItem();
184 };
185 
186 inline bool operator==(const ParameterizedItem& lhs,
187                        const ParameterizedItem& rhs) {
188   return std::tie(lhs.item, lhs.params) == std::tie(rhs.item, rhs.params);
189 }
190 
191 inline bool operator!=(const ParameterizedItem& lhs,
192                        const ParameterizedItem& rhs) {
193   return !(lhs == rhs);
194 }
195 
196 // Holds a ParameterizedMember, which may be either an single Item, or an Inner
197 // List of ParameterizedItems, along with any number of parameters. Parameter
198 // ordering is significant.
199 struct QUICHE_EXPORT ParameterizedMember {
200   std::vector<ParameterizedItem> member;
201   // If false, then |member| should only hold one Item.
202   bool member_is_inner_list = false;
203 
204   Parameters params;
205 
206   ParameterizedMember();
207   ParameterizedMember(const ParameterizedMember&);
208   ParameterizedMember& operator=(const ParameterizedMember&);
209   ParameterizedMember(std::vector<ParameterizedItem>, bool member_is_inner_list,
210                       Parameters);
211   // Shorthand constructor for a member which is an inner list.
212   ParameterizedMember(std::vector<ParameterizedItem>, Parameters);
213   // Shorthand constructor for a member which is a single Item.
214   ParameterizedMember(Item, Parameters);
215   ~ParameterizedMember();
216 };
217 
218 inline bool operator==(const ParameterizedMember& lhs,
219                        const ParameterizedMember& rhs) {
220   return std::tie(lhs.member, lhs.member_is_inner_list, lhs.params) ==
221          std::tie(rhs.member, rhs.member_is_inner_list, rhs.params);
222 }
223 
224 using DictionaryMember = std::pair<std::string, ParameterizedMember>;
225 
226 // Structured Headers RFC8941 Dictionary.
227 class QUICHE_EXPORT Dictionary {
228  public:
229   using iterator = std::vector<DictionaryMember>::iterator;
230   using const_iterator = std::vector<DictionaryMember>::const_iterator;
231 
232   Dictionary();
233   Dictionary(const Dictionary&);
234   Dictionary(Dictionary&&);
235   explicit Dictionary(std::vector<DictionaryMember> members);
236   ~Dictionary();
237   Dictionary& operator=(const Dictionary&) = default;
238   Dictionary& operator=(Dictionary&&) = default;
239   iterator begin();
240   const_iterator begin() const;
241   iterator end();
242   const_iterator end() const;
243 
244   // operator[](size_t) and at(size_t) will both abort the program in case of
245   // out of bounds access.
246   ParameterizedMember& operator[](std::size_t idx);
247   const ParameterizedMember& operator[](std::size_t idx) const;
248   ParameterizedMember& at(std::size_t idx);
249   const ParameterizedMember& at(std::size_t idx) const;
250 
251   // Consistent with std::map, if |key| does not exist in the Dictionary, then
252   // operator[](absl::string_view) will create an entry for it, but at() will
253   // abort the entire program.
254   ParameterizedMember& operator[](absl::string_view key);
255   ParameterizedMember& at(absl::string_view key);
256   const ParameterizedMember& at(absl::string_view key) const;
257 
258   const_iterator find(absl::string_view key) const;
259   iterator find(absl::string_view key);
260 
261   void clear();
262 
263   bool empty() const;
264   std::size_t size() const;
265   bool contains(absl::string_view key) const;
266   friend bool operator==(const Dictionary& lhs, const Dictionary& rhs);
267   friend bool operator!=(const Dictionary& lhs, const Dictionary& rhs);
268 
269  private:
270   // Uses a vector to hold pairs of key and dictionary member. This makes
271   // look up by index and serialization much easier.
272   std::vector<DictionaryMember> members_;
273 };
274 
275 inline bool operator==(const Dictionary& lhs, const Dictionary& rhs) {
276   return lhs.members_ == rhs.members_;
277 }
278 
279 inline bool operator!=(const Dictionary& lhs, const Dictionary& rhs) {
280   return !(lhs == rhs);
281 }
282 
283 // Structured Headers Draft 09 Parameterised List.
284 using ParameterisedList = std::vector<ParameterisedIdentifier>;
285 // Structured Headers Draft 09 List of Lists.
286 using ListOfLists = std::vector<std::vector<Item>>;
287 // Structured Headers RFC8941 List.
288 using List = std::vector<ParameterizedMember>;
289 
290 // Returns the result of parsing the header value as an Item, if it can be
291 // parsed as one, or nullopt if it cannot. Note that this uses the Draft 15
292 // parsing rules, and so applies tighter range limits to integers.
293 QUICHE_EXPORT std::optional<ParameterizedItem> ParseItem(absl::string_view str);
294 
295 // Returns the result of parsing the header value as an Item with no parameters,
296 // or nullopt if it cannot. Note that this uses the Draft 15 parsing rules, and
297 // so applies tighter range limits to integers.
298 QUICHE_EXPORT std::optional<Item> ParseBareItem(absl::string_view str);
299 
300 // Returns the result of parsing the header value as a Parameterised List, if it
301 // can be parsed as one, or nullopt if it cannot. Note that parameter keys will
302 // be returned as strings, which are guaranteed to be ASCII-encoded. List items,
303 // as well as parameter values, will be returned as Items. This method uses the
304 // Draft 09 parsing rules for Items, so integers have the 64-bit int range.
305 // Structured-Headers Draft 09 only.
306 QUICHE_EXPORT std::optional<ParameterisedList> ParseParameterisedList(
307     absl::string_view str);
308 
309 // Returns the result of parsing the header value as a List of Lists, if it can
310 // be parsed as one, or nullopt if it cannot. Inner list items will be returned
311 // as Items. This method uses the Draft 09 parsing rules for Items, so integers
312 // have the 64-bit int range.
313 // Structured-Headers Draft 09 only.
314 QUICHE_EXPORT std::optional<ListOfLists> ParseListOfLists(
315     absl::string_view str);
316 
317 // Returns the result of parsing the header value as a general List, if it can
318 // be parsed as one, or nullopt if it cannot.
319 // Structured-Headers Draft 15 only.
320 QUICHE_EXPORT std::optional<List> ParseList(absl::string_view str);
321 
322 // Returns the result of parsing the header value as a general Dictionary, if it
323 // can be parsed as one, or nullopt if it cannot. Structured-Headers Draft 15
324 // only.
325 QUICHE_EXPORT std::optional<Dictionary> ParseDictionary(absl::string_view str);
326 
327 // Serialization is implemented for Structured-Headers Draft 15 only.
328 QUICHE_EXPORT std::optional<std::string> SerializeItem(const Item& value);
329 QUICHE_EXPORT std::optional<std::string> SerializeItem(
330     const ParameterizedItem& value);
331 QUICHE_EXPORT std::optional<std::string> SerializeList(const List& value);
332 QUICHE_EXPORT std::optional<std::string> SerializeDictionary(
333     const Dictionary& value);
334 
335 }  // namespace structured_headers
336 }  // namespace quiche
337 
338 #endif  // QUICHE_COMMON_STRUCTURED_HEADERS_H_
339