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(HpackDecodingError::
169                       kInitialDynamicTableSizeUpdateIsAboveLowWaterMark);
170       return;
171     }
172     require_dynamic_table_size_update_ = false;
173   } else if (size_limit > final_header_table_size_) {
174     // The new size must not be greater than the final max header table size
175     // that the peer acknowledged.
176     ReportError(
177         HpackDecodingError::kDynamicTableSizeUpdateIsAboveAcknowledgedSetting);
178     return;
179   }
180   decoder_tables_.DynamicTableSizeUpdate(size_limit);
181   if (saw_dynamic_table_size_update_) {
182     allow_dynamic_table_size_update_ = false;
183   } else {
184     saw_dynamic_table_size_update_ = true;
185   }
186   // We no longer need to keep an eye out for a lower header table size.
187   lowest_header_table_size_ = final_header_table_size_;
188 }
189 
OnHpackDecodeError(HpackDecodingError error)190 void HpackDecoderState::OnHpackDecodeError(HpackDecodingError error) {
191   HTTP2_DVLOG(2) << "HpackDecoderState::OnHpackDecodeError "
192                  << HpackDecodingErrorToString(error);
193   if (error_ == HpackDecodingError::kOk) {
194     ReportError(error);
195   }
196 }
197 
OnHeaderBlockEnd()198 void HpackDecoderState::OnHeaderBlockEnd() {
199   HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
200   if (error_ != HpackDecodingError::kOk) {
201     return;
202   }
203   if (require_dynamic_table_size_update_) {
204     // Apparently the HPACK block was empty, but we needed it to contain at
205     // least 1 dynamic table size update.
206     ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate);
207   } else {
208     listener_->OnHeaderListEnd();
209   }
210 }
211 
ReportError(HpackDecodingError error)212 void HpackDecoderState::ReportError(HpackDecodingError error) {
213   HTTP2_DVLOG(2) << "HpackDecoderState::ReportError is new="
214                  << (error_ == HpackDecodingError::kOk ? "true" : "false")
215                  << ", error: " << HpackDecodingErrorToString(error);
216   if (error_ == HpackDecodingError::kOk) {
217     listener_->OnHeaderErrorDetected(HpackDecodingErrorToString(error));
218     error_ = error;
219   }
220 }
221 
222 }  // namespace http2
223