| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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_decoder.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/logging.h" | |
| 10 #include "net/spdy/hpack/hpack_constants.h" | |
| 11 #include "net/spdy/hpack/hpack_entry.h" | |
| 12 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h" | |
| 13 #include "net/spdy/spdy_flags.h" | |
| 14 | |
| 15 namespace net { | |
| 16 | |
| 17 HpackDecoder::HpackDecoder() | |
| 18 : handler_(nullptr), | |
| 19 total_header_bytes_(0), | |
| 20 total_parsed_bytes_(0), | |
| 21 header_block_started_(false), | |
| 22 size_updates_seen_(0), | |
| 23 size_updates_allowed_(true), | |
| 24 incremental_decode_(false) {} | |
| 25 | |
| 26 HpackDecoder::~HpackDecoder() {} | |
| 27 | |
| 28 void HpackDecoder::ApplyHeaderTableSizeSetting(size_t size_setting) { | |
| 29 header_table_.SetSettingsHeaderTableSize(size_setting); | |
| 30 } | |
| 31 | |
| 32 void HpackDecoder::HandleControlFrameHeadersStart( | |
| 33 SpdyHeadersHandlerInterface* handler) { | |
| 34 handler_ = handler; | |
| 35 total_header_bytes_ = 0; | |
| 36 } | |
| 37 | |
| 38 bool HpackDecoder::HandleControlFrameHeadersData(const char* headers_data, | |
| 39 size_t headers_data_length) { | |
| 40 if (!header_block_started_) { | |
| 41 decoded_block_.clear(); | |
| 42 header_block_started_ = true; | |
| 43 size_updates_allowed_ = true; | |
| 44 size_updates_seen_ = 0; | |
| 45 if (handler_ != nullptr) { | |
| 46 handler_->OnHeaderBlockStart(); | |
| 47 } | |
| 48 } | |
| 49 size_t new_size = headers_block_buffer_.size() + headers_data_length; | |
| 50 if (max_decode_buffer_size_bytes_ > 0 && | |
| 51 new_size > max_decode_buffer_size_bytes_) { | |
| 52 DVLOG(1) << "max_decode_buffer_size_bytes_ < new_size: " | |
| 53 << max_decode_buffer_size_bytes_ << " < " << new_size; | |
| 54 return false; | |
| 55 } | |
| 56 headers_block_buffer_.insert(headers_block_buffer_.end(), headers_data, | |
| 57 headers_data + headers_data_length); | |
| 58 | |
| 59 // Parse as many whole HPACK entries in the buffer as possible, | |
| 60 // and then remove the parsed data from the buffer. | |
| 61 HpackInputStream input_stream(headers_block_buffer_); | |
| 62 while (input_stream.HasMoreData()) { | |
| 63 if (!DecodeNextOpcodeWrapper(&input_stream)) { | |
| 64 if (input_stream.NeedMoreData()) { | |
| 65 break; | |
| 66 } | |
| 67 DVLOG(1) << "!DecodeNextOpcodeWrapper"; | |
| 68 return false; | |
| 69 } | |
| 70 } | |
| 71 uint32_t parsed_bytes = input_stream.ParsedBytes(); | |
| 72 DCHECK_GE(headers_block_buffer_.size(), parsed_bytes); | |
| 73 headers_block_buffer_.erase(0, parsed_bytes); | |
| 74 total_parsed_bytes_ += parsed_bytes; | |
| 75 | |
| 76 return true; | |
| 77 } | |
| 78 | |
| 79 bool HpackDecoder::HandleControlFrameHeadersComplete(size_t* compressed_len) { | |
| 80 if (compressed_len != nullptr) { | |
| 81 *compressed_len = total_parsed_bytes_; | |
| 82 } | |
| 83 | |
| 84 // Data in headers_block_buffer_ should have been parsed by | |
| 85 // HandleControlFrameHeadersData and removed. | |
| 86 if (headers_block_buffer_.size() > 0) { | |
| 87 DVLOG(1) << "headers_block_buffer_.size() should be zero, but is " | |
| 88 << headers_block_buffer_.size(); | |
| 89 return false; | |
| 90 } | |
| 91 | |
| 92 if (handler_ != nullptr) { | |
| 93 if (FLAGS_chromium_http2_flag_log_compressed_size) { | |
| 94 handler_->OnHeaderBlockEnd(total_header_bytes_, total_parsed_bytes_); | |
| 95 } else { | |
| 96 handler_->OnHeaderBlockEnd(total_header_bytes_); | |
| 97 } | |
| 98 } | |
| 99 headers_block_buffer_.clear(); | |
| 100 total_parsed_bytes_ = 0; | |
| 101 header_block_started_ = false; | |
| 102 handler_ = nullptr; | |
| 103 return true; | |
| 104 } | |
| 105 | |
| 106 const SpdyHeaderBlock& HpackDecoder::decoded_block() const { | |
| 107 return decoded_block_; | |
| 108 } | |
| 109 | |
| 110 void HpackDecoder::SetHeaderTableDebugVisitor( | |
| 111 std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { | |
| 112 header_table_.set_debug_visitor(std::move(visitor)); | |
| 113 } | |
| 114 | |
| 115 void HpackDecoder::set_max_decode_buffer_size_bytes( | |
| 116 size_t max_decode_buffer_size_bytes) { | |
| 117 max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes; | |
| 118 } | |
| 119 | |
| 120 size_t HpackDecoder::EstimateMemoryUsage() const { | |
| 121 return SpdyEstimateMemoryUsage(header_table_) + | |
| 122 SpdyEstimateMemoryUsage(headers_block_buffer_) + | |
| 123 SpdyEstimateMemoryUsage(decoded_block_) + | |
| 124 SpdyEstimateMemoryUsage(key_buffer_) + | |
| 125 SpdyEstimateMemoryUsage(value_buffer_); | |
| 126 } | |
| 127 | |
| 128 bool HpackDecoder::HandleHeaderRepresentation(SpdyStringPiece name, | |
| 129 SpdyStringPiece value) { | |
| 130 size_updates_allowed_ = false; | |
| 131 total_header_bytes_ += name.size() + value.size(); | |
| 132 | |
| 133 if (handler_ == nullptr) { | |
| 134 decoded_block_.AppendValueOrAddHeader(name, value); | |
| 135 } else { | |
| 136 DCHECK(decoded_block_.empty()); | |
| 137 handler_->OnHeader(name, value); | |
| 138 } | |
| 139 return true; | |
| 140 } | |
| 141 | |
| 142 bool HpackDecoder::DecodeNextOpcodeWrapper(HpackInputStream* input_stream) { | |
| 143 if (DecodeNextOpcode(input_stream)) { | |
| 144 // Decoding next opcode succeeds. Mark total bytes parsed successfully. | |
| 145 input_stream->MarkCurrentPosition(); | |
| 146 return true; | |
| 147 } | |
| 148 return false; | |
| 149 } | |
| 150 | |
| 151 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) { | |
| 152 // Implements 7.1: Indexed Header Field Representation. | |
| 153 if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) { | |
| 154 return DecodeNextIndexedHeader(input_stream); | |
| 155 } | |
| 156 // Implements 7.2.1: Literal Header Field with Incremental Indexing. | |
| 157 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) { | |
| 158 return DecodeNextLiteralHeader(input_stream, true); | |
| 159 } | |
| 160 // Implements 7.2.2: Literal Header Field without Indexing. | |
| 161 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) { | |
| 162 return DecodeNextLiteralHeader(input_stream, false); | |
| 163 } | |
| 164 // Implements 7.2.3: Literal Header Field never Indexed. | |
| 165 // TODO(jgraettinger): Preserve the never-indexed bit. | |
| 166 if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) { | |
| 167 return DecodeNextLiteralHeader(input_stream, false); | |
| 168 } | |
| 169 // Implements 7.3: Header Table Size Update. | |
| 170 if (input_stream->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode)) { | |
| 171 // Header table size updates cannot appear mid-block. | |
| 172 return DecodeNextHeaderTableSizeUpdate(input_stream); | |
| 173 } | |
| 174 // Unrecognized opcode. | |
| 175 return false; | |
| 176 } | |
| 177 | |
| 178 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate( | |
| 179 HpackInputStream* input_stream) { | |
| 180 uint32_t size = 0; | |
| 181 if (!input_stream->DecodeNextUint32(&size)) { | |
| 182 return false; | |
| 183 } | |
| 184 if (!size_updates_allowed_) { | |
| 185 DVLOG(1) << "Size updates not allowed after header entries."; | |
| 186 return false; | |
| 187 } | |
| 188 ++size_updates_seen_; | |
| 189 if (size_updates_seen_ > 2) { | |
| 190 DVLOG(1) << "Too many size updates at the start of the block."; | |
| 191 return false; | |
| 192 } | |
| 193 if (size > header_table_.settings_size_bound()) { | |
| 194 DVLOG(1) << "Size (" << size << ") exceeds SETTINGS limit (" | |
| 195 << header_table_.settings_size_bound() << ")"; | |
| 196 return false; | |
| 197 } | |
| 198 header_table_.SetMaxSize(size); | |
| 199 return true; | |
| 200 } | |
| 201 | |
| 202 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) { | |
| 203 uint32_t index = 0; | |
| 204 if (!input_stream->DecodeNextUint32(&index)) { | |
| 205 return false; | |
| 206 } | |
| 207 | |
| 208 const HpackEntry* entry = header_table_.GetByIndex(index); | |
| 209 if (entry == NULL) { | |
| 210 DVLOG(1) << "Index " << index << " is not valid."; | |
| 211 return false; | |
| 212 } | |
| 213 | |
| 214 return HandleHeaderRepresentation(entry->name(), entry->value()); | |
| 215 } | |
| 216 | |
| 217 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream, | |
| 218 bool should_index) { | |
| 219 SpdyStringPiece name; | |
| 220 if (!DecodeNextName(input_stream, &name)) { | |
| 221 return false; | |
| 222 } | |
| 223 | |
| 224 SpdyStringPiece value; | |
| 225 if (!DecodeNextStringLiteral(input_stream, false, &value)) { | |
| 226 return false; | |
| 227 } | |
| 228 | |
| 229 if (!HandleHeaderRepresentation(name, value)) { | |
| 230 return false; | |
| 231 } | |
| 232 | |
| 233 if (!should_index) { | |
| 234 return true; | |
| 235 } | |
| 236 | |
| 237 ignore_result(header_table_.TryAddEntry(name, value)); | |
| 238 return true; | |
| 239 } | |
| 240 | |
| 241 bool HpackDecoder::DecodeNextName(HpackInputStream* input_stream, | |
| 242 SpdyStringPiece* next_name) { | |
| 243 uint32_t index_or_zero = 0; | |
| 244 if (!input_stream->DecodeNextUint32(&index_or_zero)) { | |
| 245 DVLOG(1) << "Failed to decode the next uint."; | |
| 246 return false; | |
| 247 } | |
| 248 | |
| 249 if (index_or_zero == 0) { | |
| 250 return DecodeNextStringLiteral(input_stream, true, next_name); | |
| 251 } | |
| 252 | |
| 253 const HpackEntry* entry = header_table_.GetByIndex(index_or_zero); | |
| 254 if (entry == NULL) { | |
| 255 DVLOG(1) << "index " << index_or_zero << " is not valid."; | |
| 256 return false; | |
| 257 } | |
| 258 if (entry->IsStatic()) { | |
| 259 *next_name = entry->name(); | |
| 260 } else { | |
| 261 // |entry| could be evicted as part of this insertion. Preemptively copy. | |
| 262 key_buffer_.assign(entry->name().data(), entry->name().size()); | |
| 263 *next_name = key_buffer_; | |
| 264 } | |
| 265 return true; | |
| 266 } | |
| 267 | |
| 268 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, | |
| 269 bool is_key, | |
| 270 SpdyStringPiece* output) { | |
| 271 if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { | |
| 272 SpdyString* buffer = is_key ? &key_buffer_ : &value_buffer_; | |
| 273 bool result = input_stream->DecodeNextHuffmanString(buffer); | |
| 274 *output = SpdyStringPiece(*buffer); | |
| 275 return result; | |
| 276 } else if (input_stream->MatchPrefixAndConsume( | |
| 277 kStringLiteralIdentityEncoded)) { | |
| 278 return input_stream->DecodeNextIdentityString(output); | |
| 279 } else { | |
| 280 DVLOG(1) << "String literal is neither Huffman nor identity encoded!"; | |
| 281 return false; | |
| 282 } | |
| 283 } | |
| 284 | |
| 285 } // namespace net | |
| OLD | NEW |