xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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