| 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 |