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