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