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