• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   explicit Dictionary(std::vector<DictionaryMember> members);
235   ~Dictionary();
236   Dictionary& operator=(const Dictionary&) = default;
237   iterator begin();
238   const_iterator begin() const;
239   iterator end();
240   const_iterator end() const;
241 
242   // operator[](size_t) and at(size_t) will both abort the program in case of
243   // out of bounds access.
244   ParameterizedMember& operator[](std::size_t idx);
245   const ParameterizedMember& operator[](std::size_t idx) const;
246   ParameterizedMember& at(std::size_t idx);
247   const ParameterizedMember& at(std::size_t idx) const;
248 
249   // Consistent with std::map, if |key| does not exist in the Dictionary, then
250   // operator[](absl::string_view) will create an entry for it, but at() will
251   // abort the entire program.
252   ParameterizedMember& operator[](absl::string_view key);
253   ParameterizedMember& at(absl::string_view key);
254   const ParameterizedMember& at(absl::string_view key) const;
255 
256   bool empty() const;
257   std::size_t size() const;
258   bool contains(absl::string_view key) const;
259   friend bool operator==(const Dictionary& lhs, const Dictionary& rhs);
260   friend bool operator!=(const Dictionary& lhs, const Dictionary& rhs);
261 
262  private:
263   // Uses a vector to hold pairs of key and dictionary member. This makes
264   // look up by index and serialization much easier.
265   std::vector<DictionaryMember> members_;
266 };
267 
268 inline bool operator==(const Dictionary& lhs, const Dictionary& rhs) {
269   return lhs.members_ == rhs.members_;
270 }
271 
272 inline bool operator!=(const Dictionary& lhs, const Dictionary& rhs) {
273   return !(lhs == rhs);
274 }
275 
276 // Structured Headers Draft 09 Parameterised List.
277 using ParameterisedList = std::vector<ParameterisedIdentifier>;
278 // Structured Headers Draft 09 List of Lists.
279 using ListOfLists = std::vector<std::vector<Item>>;
280 // Structured Headers RFC8941 List.
281 using List = std::vector<ParameterizedMember>;
282 
283 // Returns the result of parsing the header value as an Item, if it can be
284 // parsed as one, or nullopt if it cannot. Note that this uses the Draft 15
285 // parsing rules, and so applies tighter range limits to integers.
286 QUICHE_EXPORT std::optional<ParameterizedItem> ParseItem(absl::string_view str);
287 
288 // Returns the result of parsing the header value as an Item with no parameters,
289 // or nullopt if it cannot. Note that this uses the Draft 15 parsing rules, and
290 // so applies tighter range limits to integers.
291 QUICHE_EXPORT std::optional<Item> ParseBareItem(absl::string_view str);
292 
293 // Returns the result of parsing the header value as a Parameterised List, if it
294 // can be parsed as one, or nullopt if it cannot. Note that parameter keys will
295 // be returned as strings, which are guaranteed to be ASCII-encoded. List items,
296 // as well as parameter values, will be returned as Items. This method uses the
297 // Draft 09 parsing rules for Items, so integers have the 64-bit int range.
298 // Structured-Headers Draft 09 only.
299 QUICHE_EXPORT std::optional<ParameterisedList> ParseParameterisedList(
300     absl::string_view str);
301 
302 // Returns the result of parsing the header value as a List of Lists, if it can
303 // be parsed as one, or nullopt if it cannot. Inner list items will be returned
304 // as Items. This method uses the Draft 09 parsing rules for Items, so integers
305 // have the 64-bit int range.
306 // Structured-Headers Draft 09 only.
307 QUICHE_EXPORT std::optional<ListOfLists> ParseListOfLists(
308     absl::string_view str);
309 
310 // Returns the result of parsing the header value as a general List, if it can
311 // be parsed as one, or nullopt if it cannot.
312 // Structured-Headers Draft 15 only.
313 QUICHE_EXPORT std::optional<List> ParseList(absl::string_view str);
314 
315 // Returns the result of parsing the header value as a general Dictionary, if it
316 // can be parsed as one, or nullopt if it cannot. Structured-Headers Draft 15
317 // only.
318 QUICHE_EXPORT std::optional<Dictionary> ParseDictionary(absl::string_view str);
319 
320 // Serialization is implemented for Structured-Headers Draft 15 only.
321 QUICHE_EXPORT std::optional<std::string> SerializeItem(const Item& value);
322 QUICHE_EXPORT std::optional<std::string> SerializeItem(
323     const ParameterizedItem& value);
324 QUICHE_EXPORT std::optional<std::string> SerializeList(const List& value);
325 QUICHE_EXPORT std::optional<std::string> SerializeDictionary(
326     const Dictionary& value);
327 
328 }  // namespace structured_headers
329 }  // namespace quiche
330 
331 #endif  // QUICHE_COMMON_STRUCTURED_HEADERS_H_
332