Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(193)

Side by Side Diff: net/spdy/hpack/hpack_decoder2.cc

Issue 2553683006: Add HpackDecoder2. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/hpack/hpack_decoder2.h ('k') | net/spdy/hpack/hpack_decoder2_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/spdy/hpack/hpack_decoder2.h"
6
7 #include <list>
8 #include <utility>
9
10 #include "base/logging.h"
11 #include "base/strings/string_piece.h"
12 #include "net/http2/decoder/decode_buffer.h"
13 #include "net/http2/decoder/decode_status.h"
14 #include "net/spdy/hpack/hpack_entry.h"
15
16 using base::StringPiece;
17
18 namespace net {
19
20 HpackDecoder2::HpackDecoder2() : hpack_block_decoder_(this) {
21 Reset();
22 }
23
24 HpackDecoder2::~HpackDecoder2() {}
25
26 void HpackDecoder2::Reset() {
27 DVLOG(2) << "HpackDecoder2::Reset";
28 handler_ = nullptr;
29
30 hpack_block_decoder_.Reset();
31 hpack_block_decoder_.set_listener(this);
32
33 total_hpack_bytes_ = 0;
34 total_header_bytes_ = 0;
35 size_update_count_ = 0;
36 header_seen_ = false;
37 in_progress_ = false;
38 error_detected_ = false;
39 header_block_started_ = false;
40
41 name_.Reset();
42 value_.Reset();
43 }
44
45 void HpackDecoder2::SetErrorDetected() {
46 if (!error_detected_) {
47 DVLOG(2) << "HpackDecoder2::SetErrorDetected";
48 hpack_block_decoder_.set_listener(&no_op_listener_);
49 error_detected_ = true;
50 }
51 }
52
53 void HpackDecoder2::ApplyHeaderTableSizeSetting(size_t size_setting) {
54 DVLOG(2) << "HpackDecoder2::ApplyHeaderTableSizeSetting";
55 header_table_.SetSettingsHeaderTableSize(size_setting);
56 }
57
58 // If a SpdyHeadersHandlerInterface is provided, the decoder will emit
59 // headers to it rather than accumulating them in a SpdyHeaderBlock.
60 void HpackDecoder2::HandleControlFrameHeadersStart(
61 SpdyHeadersHandlerInterface* handler) {
62 DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersStart";
63 DCHECK(!header_block_started_);
64 handler_ = handler;
65 }
66
67 // Called as HPACK block fragments arrive. Returns false
68 // if an error occurred while decoding the block.
69 bool HpackDecoder2::HandleControlFrameHeadersData(const char* headers_data,
70 size_t headers_data_length) {
71 DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersData: len="
72 << headers_data_length;
73 if (!header_block_started_) {
74 DCHECK_EQ(total_hpack_bytes_, 0u);
75 // Clear the SpdyHeaderBlock here rather than in Reset so that it is NOT
76 // cleared in HandleControlFrameHeadersComplete, which would be before it
77 // could be used.
78 decoded_block_.clear();
79 header_block_started_ = true;
80 if (handler_ != nullptr) {
81 handler_->OnHeaderBlockStart();
82 }
83 }
84
85 // Sometimes we get a call with headers_data==nullptr and
86 // headers_data_length==0, in which case we need to avoid creating
87 // a DecodeBuffer, which would otherwise complain.
88 if (headers_data_length > 0) {
89 DCHECK_NE(headers_data, nullptr);
90 total_hpack_bytes_ += headers_data_length;
91 DecodeBuffer db(headers_data, headers_data_length);
92 DecodeStatus status = hpack_block_decoder_.Decode(&db);
93 switch (status) {
94 case DecodeStatus::kDecodeDone:
95 // We've completed the decoding of headers_data, and it ended at the
96 // boundary between two HPACK block entries, so name_ and value_ are
97 // currently reset.
98 DCHECK_EQ(0u, db.Remaining());
99 in_progress_ = false;
100 break;
101
102 case DecodeStatus::kDecodeInProgress:
103 DCHECK_EQ(0u, db.Remaining());
104 in_progress_ = true;
105 if (!error_detected_) {
106 name_.BufferStringIfUnbuffered();
107 value_.BufferStringIfUnbuffered();
108 EnforceMaxDecodeBufferSize();
109 }
110 break;
111
112 case DecodeStatus::kDecodeError:
113 SetErrorDetected();
114 break;
115 }
116 }
117 return !error_detected_;
118 }
119
120 // Called after a HPACK block has been completely delivered via
121 // HandleControlFrameHeadersData(). Returns false if an error occurred.
122 // |compressed_len| if non-null will be set to the size of the encoded
123 // buffered block that was accumulated in HandleControlFrameHeadersData(),
124 // to support subsequent calculation of compression percentage.
125 // Discards the handler supplied at the start of decoding the block.
126 // TODO(jamessynge): Determine if compressed_len is needed; it is used to
127 // produce UUMA stat Net.SpdyHpackDecompressionPercentage, but only for
128 // SPDY3, not HTTP2.
129 bool HpackDecoder2::HandleControlFrameHeadersComplete(size_t* compressed_len) {
130 DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersComplete";
131 if (error_detected_ || in_progress_) {
132 DVLOG(2) << "error_detected_=" << error_detected_
133 << ", in_progress_=" << in_progress_;
134 return false;
135 }
136 if (compressed_len != nullptr) {
137 *compressed_len = total_hpack_bytes_;
138 }
139 if (handler_ != nullptr) {
140 handler_->OnHeaderBlockEnd(total_header_bytes_);
141 }
142 Reset();
143 return true;
144 }
145
146 const SpdyHeaderBlock& HpackDecoder2::decoded_block() const {
147 return decoded_block_;
148 }
149
150 void HpackDecoder2::SetHeaderTableDebugVisitor(
151 std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
152 DVLOG(2) << "HpackDecoder2::SetHeaderTableDebugVisitor";
153 header_table_.set_debug_visitor(std::move(visitor));
154 }
155
156 void HpackDecoder2::set_max_decode_buffer_size_bytes(
157 size_t max_decode_buffer_size_bytes) {
158 DVLOG(2) << "HpackDecoder2::set_max_decode_buffer_size_bytes";
159 max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
160 }
161
162 void HpackDecoder2::OnIndexedHeader(size_t index) {
163 DVLOG(2) << "HpackDecoder2::OnIndexedHeader: index=" << index;
164 DCHECK(!error_detected_);
165 const HpackEntry* entry = header_table_.GetByIndex(index);
166 if (entry == nullptr) {
167 SetErrorDetected();
168 return;
169 }
170 HandleHeaderRepresentation(entry->name(), entry->value());
171 }
172
173 void HpackDecoder2::OnStartLiteralHeader(HpackEntryType entry_type,
174 size_t maybe_name_index) {
175 DVLOG(2) << "HpackDecoder2::OnStartLiteralHeader: entry_type=" << entry_type
176 << ", maybe_name_index=" << maybe_name_index;
177 DCHECK(!error_detected_);
178 entry_type_ = entry_type;
179 if (maybe_name_index > 0) {
180 const HpackEntry* entry = header_table_.GetByIndex(maybe_name_index);
181 if (entry == nullptr) {
182 SetErrorDetected();
183 return;
184 } else {
185 // Non-static entries could be evicted, leaving us with a dangling
186 // pointer, so we preemptively copy. This could be avoided if
187 // TryAddEntry would copy the strings prior to performing eviction.
188 name_.Set(entry->name(), entry->IsStatic());
189 name_.BufferStringIfUnbuffered();
190 }
191 }
192 }
193
194 void HpackDecoder2::OnNameStart(bool huffman_encoded, size_t len) {
195 DVLOG(2) << "HpackDecoder2::OnNameStart: huffman_encoded="
196 << (huffman_encoded ? "true" : "false") << ", len=" << len;
197 if (len > max_decode_buffer_size_bytes_) {
198 DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
199 << max_decode_buffer_size_bytes_ << ")";
200 SetErrorDetected();
201 return;
202 }
203 name_.OnStart(huffman_encoded, len);
204 }
205
206 void HpackDecoder2::OnNameData(const char* data, size_t len) {
207 DVLOG(2) << "HpackDecoder2::OnNameData: len=" << len
208 << "\n data: " << StringPiece(data, len);
209 if (error_detected_) {
210 return;
211 }
212 if (!name_.OnData(data, len)) {
213 SetErrorDetected();
214 }
215 }
216
217 void HpackDecoder2::OnNameEnd() {
218 DVLOG(2) << "HpackDecoder2::OnNameEnd";
219 if (error_detected_) {
220 return;
221 }
222 if (!name_.OnEnd()) {
223 SetErrorDetected();
224 }
225 }
226
227 void HpackDecoder2::OnValueStart(bool huffman_encoded, size_t len) {
228 DVLOG(2) << "HpackDecoder2::OnValueStart: huffman_encoded="
229 << (huffman_encoded ? "true" : "false") << ", len=" << len;
230 if (len > max_decode_buffer_size_bytes_) {
231 DVLOG(1) << "Value length (" << len << ") is longer than permitted ("
232 << max_decode_buffer_size_bytes_ << ")";
233 SetErrorDetected();
234 return;
235 }
236 value_.OnStart(huffman_encoded, len);
237 }
238
239 void HpackDecoder2::OnValueData(const char* data, size_t len) {
240 DVLOG(2) << "HpackDecoder2::OnValueData: len=" << len
241 << "\n data: " << StringPiece(data, len);
242 if (error_detected_) {
243 return;
244 }
245 if (!value_.OnData(data, len)) {
246 SetErrorDetected();
247 }
248 }
249
250 void HpackDecoder2::OnValueEnd() {
251 DVLOG(2) << "HpackDecoder2::OnValueEnd";
252 if (error_detected_) {
253 return;
254 }
255 if (!value_.OnEnd()) {
256 SetErrorDetected();
257 return;
258 }
259 if (EnforceMaxDecodeBufferSize()) {
260 // All is well.
261 HandleHeaderRepresentation(name_.str(), value_.str());
262 if (entry_type_ == HpackEntryType::kIndexedLiteralHeader) {
263 header_table_.TryAddEntry(name_.str(), value_.str());
264 }
265 name_.Reset();
266 value_.Reset();
267 }
268 }
269
270 void HpackDecoder2::OnDynamicTableSizeUpdate(size_t size) {
271 DVLOG(2) << "HpackDecoder2::OnDynamicTableSizeUpdate: size=" << size;
272 if (error_detected_) {
273 return;
274 }
275 if (size > header_table_.settings_size_bound()) {
276 DVLOG(1) << "Dynamic Table Size Update with too large a size: " << size
277 << " > " << header_table_.settings_size_bound();
278 SetErrorDetected();
279 return;
280 }
281 if (header_seen_) {
282 DVLOG(1) << "Dynamic Table Size Update seen after a Header";
283 SetErrorDetected();
284 return;
285 }
286 ++size_update_count_;
287 if (size_update_count_ > 2) {
288 DVLOG(1) << "Too many (" << size_update_count_
289 << ") Dynamic Table Size Updates";
290 SetErrorDetected();
291 return;
292 }
293 header_table_.SetMaxSize(size);
294 return;
295 }
296
297 bool HpackDecoder2::EnforceMaxDecodeBufferSize() {
298 if (!error_detected_) {
299 size_t buffered_length = name_.BufferedLength() + value_.BufferedLength();
300 DVLOG(2) << "buffered_length=" << buffered_length
301 << "; max=" << max_decode_buffer_size_bytes_;
302 if (buffered_length > max_decode_buffer_size_bytes_) {
303 DVLOG(1) << "Header length (" << buffered_length
304 << ") is longer than permitted ("
305 << max_decode_buffer_size_bytes_ << ")";
306 SetErrorDetected();
307 }
308 }
309 return !error_detected_;
310 }
311
312 void HpackDecoder2::HandleHeaderRepresentation(StringPiece name,
313 StringPiece value) {
314 DVLOG(2) << "HpackDecoder2::HandleHeaderRepresentation:\n name: " << name
315 << "\n value: " << value;
316 total_header_bytes_ += name.size() + value.size();
317 header_seen_ = true;
318 if (handler_ == nullptr) {
319 DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
320 << "adding to decoded_block";
321 decoded_block_.AppendValueOrAddHeader(name, value);
322 } else {
323 DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
324 << "passing to handler";
325 DCHECK(decoded_block_.empty());
326 handler_->OnHeader(name, value);
327 }
328 }
329
330 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack/hpack_decoder2.h ('k') | net/spdy/hpack/hpack_decoder2_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698