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