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