1 // Copyright 2016 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 #include "quiche/http2/hpack/decoder/hpack_decoder_state.h"
6
7 #include <utility>
8
9 #include "quiche/http2/http2_constants.h"
10 #include "quiche/common/platform/api/quiche_logging.h"
11
12 namespace http2 {
13 namespace {
14
ExtractString(HpackDecoderStringBuffer * string_buffer)15 std::string ExtractString(HpackDecoderStringBuffer* string_buffer) {
16 if (string_buffer->IsBuffered()) {
17 return string_buffer->ReleaseString();
18 } else {
19 auto result = std::string(string_buffer->str());
20 string_buffer->Reset();
21 return result;
22 }
23 }
24
25 } // namespace
26
HpackDecoderState(HpackDecoderListener * listener)27 HpackDecoderState::HpackDecoderState(HpackDecoderListener* listener)
28 : listener_(listener),
29 final_header_table_size_(Http2SettingsInfo::DefaultHeaderTableSize()),
30 lowest_header_table_size_(final_header_table_size_),
31 require_dynamic_table_size_update_(false),
32 allow_dynamic_table_size_update_(true),
33 saw_dynamic_table_size_update_(false),
34 error_(HpackDecodingError::kOk) {
35 QUICHE_CHECK(listener_);
36 }
37
38 HpackDecoderState::~HpackDecoderState() = default;
39
ApplyHeaderTableSizeSetting(uint32_t header_table_size)40 void HpackDecoderState::ApplyHeaderTableSizeSetting(
41 uint32_t header_table_size) {
42 QUICHE_DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting("
43 << header_table_size << ")";
44 QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
45 if (header_table_size < lowest_header_table_size_) {
46 lowest_header_table_size_ = header_table_size;
47 }
48 final_header_table_size_ = header_table_size;
49 QUICHE_DVLOG(2) << "low water mark: " << lowest_header_table_size_;
50 QUICHE_DVLOG(2) << "final limit: " << final_header_table_size_;
51 }
52
53 // Called to notify this object that we're starting to decode an HPACK block
54 // (e.g. a HEADERS or PUSH_PROMISE frame's header has been decoded).
OnHeaderBlockStart()55 void HpackDecoderState::OnHeaderBlockStart() {
56 QUICHE_DVLOG(2) << "HpackDecoderState::OnHeaderBlockStart";
57 // This instance can't be reused after an error has been detected, as we must
58 // assume that the encoder and decoder compression states are no longer
59 // synchronized.
60 QUICHE_DCHECK(error_ == HpackDecodingError::kOk)
61 << HpackDecodingErrorToString(error_);
62 QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
63 allow_dynamic_table_size_update_ = true;
64 saw_dynamic_table_size_update_ = false;
65 // If the peer has acknowledged a HEADER_TABLE_SIZE smaller than that which
66 // its HPACK encoder has been using, then the next HPACK block it sends MUST
67 // start with a Dynamic Table Size Update entry that is at least as low as
68 // lowest_header_table_size_. That may be followed by another as great as
69 // final_header_table_size_, if those are different.
70 require_dynamic_table_size_update_ =
71 (lowest_header_table_size_ <
72 decoder_tables_.current_header_table_size() ||
73 final_header_table_size_ < decoder_tables_.header_table_size_limit());
74 QUICHE_DVLOG(2) << "HpackDecoderState::OnHeaderListStart "
75 << "require_dynamic_table_size_update_="
76 << require_dynamic_table_size_update_;
77 listener_->OnHeaderListStart();
78 }
79
OnIndexedHeader(size_t index)80 void HpackDecoderState::OnIndexedHeader(size_t index) {
81 QUICHE_DVLOG(2) << "HpackDecoderState::OnIndexedHeader: " << index;
82 if (error_ != HpackDecodingError::kOk) {
83 return;
84 }
85 if (require_dynamic_table_size_update_) {
86 ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate);
87 return;
88 }
89 allow_dynamic_table_size_update_ = false;
90 const HpackStringPair* entry = decoder_tables_.Lookup(index);
91 if (entry != nullptr) {
92 listener_->OnHeader(entry->name, entry->value);
93 } else {
94 ReportError(HpackDecodingError::kInvalidIndex);
95 }
96 }
97
OnNameIndexAndLiteralValue(HpackEntryType entry_type,size_t name_index,HpackDecoderStringBuffer * value_buffer)98 void HpackDecoderState::OnNameIndexAndLiteralValue(
99 HpackEntryType entry_type, size_t name_index,
100 HpackDecoderStringBuffer* value_buffer) {
101 QUICHE_DVLOG(2) << "HpackDecoderState::OnNameIndexAndLiteralValue "
102 << entry_type << ", " << name_index << ", "
103 << value_buffer->str();
104 if (error_ != HpackDecodingError::kOk) {
105 return;
106 }
107 if (require_dynamic_table_size_update_) {
108 ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate);
109 return;
110 }
111 allow_dynamic_table_size_update_ = false;
112 const HpackStringPair* entry = decoder_tables_.Lookup(name_index);
113 if (entry != nullptr) {
114 std::string value(ExtractString(value_buffer));
115 listener_->OnHeader(entry->name, value);
116 if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
117 decoder_tables_.Insert(entry->name, std::move(value));
118 }
119 } else {
120 ReportError(HpackDecodingError::kInvalidNameIndex);
121 }
122 }
123
OnLiteralNameAndValue(HpackEntryType entry_type,HpackDecoderStringBuffer * name_buffer,HpackDecoderStringBuffer * value_buffer)124 void HpackDecoderState::OnLiteralNameAndValue(
125 HpackEntryType entry_type, HpackDecoderStringBuffer* name_buffer,
126 HpackDecoderStringBuffer* value_buffer) {
127 QUICHE_DVLOG(2) << "HpackDecoderState::OnLiteralNameAndValue " << entry_type
128 << ", " << name_buffer->str() << ", " << value_buffer->str();
129 if (error_ != HpackDecodingError::kOk) {
130 return;
131 }
132 if (require_dynamic_table_size_update_) {
133 ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate);
134 return;
135 }
136 allow_dynamic_table_size_update_ = false;
137 std::string name(ExtractString(name_buffer));
138 std::string value(ExtractString(value_buffer));
139 listener_->OnHeader(name, value);
140 if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
141 decoder_tables_.Insert(std::move(name), std::move(value));
142 }
143 }
144
OnDynamicTableSizeUpdate(size_t size_limit)145 void HpackDecoderState::OnDynamicTableSizeUpdate(size_t size_limit) {
146 QUICHE_DVLOG(2) << "HpackDecoderState::OnDynamicTableSizeUpdate "
147 << size_limit << ", required="
148 << (require_dynamic_table_size_update_ ? "true" : "false")
149 << ", allowed="
150 << (allow_dynamic_table_size_update_ ? "true" : "false");
151 if (error_ != HpackDecodingError::kOk) {
152 return;
153 }
154 QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
155 if (!allow_dynamic_table_size_update_) {
156 // At most two dynamic table size updates allowed at the start, and not
157 // after a header.
158 ReportError(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed);
159 return;
160 }
161 if (require_dynamic_table_size_update_) {
162 // The new size must not be greater than the low water mark.
163 if (size_limit > lowest_header_table_size_) {
164 ReportError(HpackDecodingError::
165 kInitialDynamicTableSizeUpdateIsAboveLowWaterMark);
166 return;
167 }
168 require_dynamic_table_size_update_ = false;
169 } else if (size_limit > final_header_table_size_) {
170 // The new size must not be greater than the final max header table size
171 // that the peer acknowledged.
172 ReportError(
173 HpackDecodingError::kDynamicTableSizeUpdateIsAboveAcknowledgedSetting);
174 return;
175 }
176 decoder_tables_.DynamicTableSizeUpdate(size_limit);
177 if (saw_dynamic_table_size_update_) {
178 allow_dynamic_table_size_update_ = false;
179 } else {
180 saw_dynamic_table_size_update_ = true;
181 }
182 // We no longer need to keep an eye out for a lower header table size.
183 lowest_header_table_size_ = final_header_table_size_;
184 }
185
OnHpackDecodeError(HpackDecodingError error)186 void HpackDecoderState::OnHpackDecodeError(HpackDecodingError error) {
187 QUICHE_DVLOG(2) << "HpackDecoderState::OnHpackDecodeError "
188 << HpackDecodingErrorToString(error);
189 if (error_ == HpackDecodingError::kOk) {
190 ReportError(error);
191 }
192 }
193
OnHeaderBlockEnd()194 void HpackDecoderState::OnHeaderBlockEnd() {
195 QUICHE_DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
196 if (error_ != HpackDecodingError::kOk) {
197 return;
198 }
199 if (require_dynamic_table_size_update_) {
200 // Apparently the HPACK block was empty, but we needed it to contain at
201 // least 1 dynamic table size update.
202 ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate);
203 } else {
204 listener_->OnHeaderListEnd();
205 }
206 }
207
ReportError(HpackDecodingError error)208 void HpackDecoderState::ReportError(HpackDecodingError error) {
209 QUICHE_DVLOG(2) << "HpackDecoderState::ReportError is new="
210 << (error_ == HpackDecodingError::kOk ? "true" : "false")
211 << ", error: " << HpackDecodingErrorToString(error);
212 if (error_ == HpackDecodingError::kOk) {
213 listener_->OnHeaderErrorDetected(HpackDecodingErrorToString(error));
214 error_ = error;
215 }
216 }
217
218 } // namespace http2
219