1 // Copyright 2014 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_SPDY_CORE_HPACK_HPACK_ENCODER_H_ 6 #define QUICHE_SPDY_CORE_HPACK_HPACK_ENCODER_H_ 7 8 #include <stddef.h> 9 10 #include <memory> 11 #include <string> 12 #include <utility> 13 #include <vector> 14 15 #include "absl/strings/string_view.h" 16 #include "quiche/common/platform/api/quiche_export.h" 17 #include "quiche/common/quiche_callbacks.h" 18 #include "quiche/spdy/core/hpack/hpack_header_table.h" 19 #include "quiche/spdy/core/hpack/hpack_output_stream.h" 20 #include "quiche/spdy/core/http2_header_block.h" 21 22 // An HpackEncoder encodes header sets as outlined in 23 // http://tools.ietf.org/html/rfc7541. 24 25 namespace spdy { 26 27 namespace test { 28 class HpackEncoderPeer; 29 } // namespace test 30 31 class QUICHE_EXPORT HpackEncoder { 32 public: 33 using Representation = std::pair<absl::string_view, absl::string_view>; 34 using Representations = std::vector<Representation>; 35 36 // Callers may provide a HeaderListener to be informed of header name-value 37 // pairs processed by this encoder. 38 using HeaderListener = 39 quiche::MultiUseCallback<void(absl::string_view, absl::string_view)>; 40 41 // An indexing policy should return true if the provided header name-value 42 // pair should be inserted into the HPACK dynamic table. 43 using IndexingPolicy = 44 quiche::MultiUseCallback<bool(absl::string_view, absl::string_view)>; 45 46 HpackEncoder(); 47 HpackEncoder(const HpackEncoder&) = delete; 48 HpackEncoder& operator=(const HpackEncoder&) = delete; 49 ~HpackEncoder(); 50 51 // Encodes and returns the given header set as a string. 52 std::string EncodeHeaderBlock(const Http2HeaderBlock& header_set); 53 54 class QUICHE_EXPORT ProgressiveEncoder { 55 public: ~ProgressiveEncoder()56 virtual ~ProgressiveEncoder() {} 57 58 // Returns true iff more remains to encode. 59 virtual bool HasNext() const = 0; 60 61 // Encodes and returns up to max_encoded_bytes of the current header block. 62 virtual std::string Next(size_t max_encoded_bytes) = 0; 63 }; 64 65 // Returns a ProgressiveEncoder which must be outlived by both the given 66 // Http2HeaderBlock and this object. 67 std::unique_ptr<ProgressiveEncoder> EncodeHeaderSet( 68 const Http2HeaderBlock& header_set); 69 // Returns a ProgressiveEncoder which must be outlived by this HpackEncoder. 70 // The encoder will not attempt to split any \0-delimited values in 71 // |representations|. If such splitting is desired, it must be performed by 72 // the caller when constructing the list of representations. 73 std::unique_ptr<ProgressiveEncoder> EncodeRepresentations( 74 const Representations& representations); 75 76 // Called upon a change to SETTINGS_HEADER_TABLE_SIZE. Specifically, this 77 // is to be called after receiving (and sending an acknowledgement for) a 78 // SETTINGS_HEADER_TABLE_SIZE update from the remote decoding endpoint. 79 void ApplyHeaderTableSizeSetting(size_t size_setting); 80 81 // TODO(birenroy): Rename this GetDynamicTableCapacity(). CurrentHeaderTableSizeSetting()82 size_t CurrentHeaderTableSizeSetting() const { 83 return header_table_.settings_size_bound(); 84 } 85 86 // This HpackEncoder will use |policy| to determine whether to insert header 87 // name-value pairs into the dynamic table. SetIndexingPolicy(IndexingPolicy policy)88 void SetIndexingPolicy(IndexingPolicy policy) { 89 should_index_ = std::move(policy); 90 } 91 92 // |listener| will be invoked for each header name-value pair processed by 93 // this encoder. SetHeaderListener(HeaderListener listener)94 void SetHeaderListener(HeaderListener listener) { 95 listener_ = std::move(listener); 96 } 97 DisableCompression()98 void DisableCompression() { enable_compression_ = false; } 99 100 // Disables the deconstruction of Cookie header values into individual 101 // components, as described in 102 // https://httpwg.org/specs/rfc9113.html#CompressCookie. The deconstructed 103 // representation can cause problems for some HTTP/2 endpoints. DisableCookieCrumbling()104 void DisableCookieCrumbling() { crumble_cookies_ = false; } 105 106 // Returns the current dynamic table size, including the 32 bytes per entry 107 // overhead mentioned in RFC 7541 section 4.1. GetDynamicTableSize()108 size_t GetDynamicTableSize() const { return header_table_.size(); } 109 110 private: 111 friend class test::HpackEncoderPeer; 112 113 class RepresentationIterator; 114 class Encoderator; 115 116 // Encodes a sequence of header name-value pairs as a single header block. 117 std::string EncodeRepresentations(RepresentationIterator* iter); 118 119 // Emits a static/dynamic indexed representation (Section 7.1). 120 void EmitIndex(size_t index); 121 122 // Emits a literal representation (Section 7.2). 123 void EmitIndexedLiteral(const Representation& representation); 124 void EmitNonIndexedLiteral(const Representation& representation, 125 bool enable_compression); 126 void EmitLiteral(const Representation& representation); 127 128 // Emits a Huffman or identity string (whichever is smaller). 129 void EmitString(absl::string_view str); 130 131 // Emits the current dynamic table size if the table size was recently 132 // updated and we have not yet emitted it (Section 6.3). 133 void MaybeEmitTableSize(); 134 135 // Crumbles a cookie header into ";" delimited crumbs. 136 static void CookieToCrumbs(const Representation& cookie, 137 Representations* crumbs_out); 138 139 // Crumbles other header field values at \0 delimiters. 140 static void DecomposeRepresentation(const Representation& header_field, 141 Representations* out); 142 143 HpackHeaderTable header_table_; 144 HpackOutputStream output_stream_; 145 146 size_t min_table_size_setting_received_; 147 HeaderListener listener_; 148 IndexingPolicy should_index_; 149 bool enable_compression_; 150 bool should_emit_table_size_; 151 bool crumble_cookies_; 152 }; 153 154 } // namespace spdy 155 156 #endif // QUICHE_SPDY_CORE_HPACK_HPACK_ENCODER_H_ 157