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