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

Unified 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 side-by-side diff with in-line comments
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 »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: net/spdy/hpack/hpack_decoder2.cc
diff --git a/net/spdy/hpack/hpack_decoder2.cc b/net/spdy/hpack/hpack_decoder2.cc
new file mode 100644
index 0000000000000000000000000000000000000000..03b1c2ef229415eb0322a972f1a5cbc40b96d1ae
--- /dev/null
+++ b/net/spdy/hpack/hpack_decoder2.cc
@@ -0,0 +1,330 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/spdy/hpack/hpack_decoder2.h"
+
+#include <list>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/strings/string_piece.h"
+#include "net/http2/decoder/decode_buffer.h"
+#include "net/http2/decoder/decode_status.h"
+#include "net/spdy/hpack/hpack_entry.h"
+
+using base::StringPiece;
+
+namespace net {
+
+HpackDecoder2::HpackDecoder2() : hpack_block_decoder_(this) {
+ Reset();
+}
+
+HpackDecoder2::~HpackDecoder2() {}
+
+void HpackDecoder2::Reset() {
+ DVLOG(2) << "HpackDecoder2::Reset";
+ handler_ = nullptr;
+
+ hpack_block_decoder_.Reset();
+ hpack_block_decoder_.set_listener(this);
+
+ total_hpack_bytes_ = 0;
+ total_header_bytes_ = 0;
+ size_update_count_ = 0;
+ header_seen_ = false;
+ in_progress_ = false;
+ error_detected_ = false;
+ header_block_started_ = false;
+
+ name_.Reset();
+ value_.Reset();
+}
+
+void HpackDecoder2::SetErrorDetected() {
+ if (!error_detected_) {
+ DVLOG(2) << "HpackDecoder2::SetErrorDetected";
+ hpack_block_decoder_.set_listener(&no_op_listener_);
+ error_detected_ = true;
+ }
+}
+
+void HpackDecoder2::ApplyHeaderTableSizeSetting(size_t size_setting) {
+ DVLOG(2) << "HpackDecoder2::ApplyHeaderTableSizeSetting";
+ header_table_.SetSettingsHeaderTableSize(size_setting);
+}
+
+// If a SpdyHeadersHandlerInterface is provided, the decoder will emit
+// headers to it rather than accumulating them in a SpdyHeaderBlock.
+void HpackDecoder2::HandleControlFrameHeadersStart(
+ SpdyHeadersHandlerInterface* handler) {
+ DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersStart";
+ DCHECK(!header_block_started_);
+ handler_ = handler;
+}
+
+// Called as HPACK block fragments arrive. Returns false
+// if an error occurred while decoding the block.
+bool HpackDecoder2::HandleControlFrameHeadersData(const char* headers_data,
+ size_t headers_data_length) {
+ DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersData: len="
+ << headers_data_length;
+ if (!header_block_started_) {
+ DCHECK_EQ(total_hpack_bytes_, 0u);
+ // Clear the SpdyHeaderBlock here rather than in Reset so that it is NOT
+ // cleared in HandleControlFrameHeadersComplete, which would be before it
+ // could be used.
+ decoded_block_.clear();
+ header_block_started_ = true;
+ if (handler_ != nullptr) {
+ handler_->OnHeaderBlockStart();
+ }
+ }
+
+ // Sometimes we get a call with headers_data==nullptr and
+ // headers_data_length==0, in which case we need to avoid creating
+ // a DecodeBuffer, which would otherwise complain.
+ if (headers_data_length > 0) {
+ DCHECK_NE(headers_data, nullptr);
+ total_hpack_bytes_ += headers_data_length;
+ DecodeBuffer db(headers_data, headers_data_length);
+ DecodeStatus status = hpack_block_decoder_.Decode(&db);
+ switch (status) {
+ case DecodeStatus::kDecodeDone:
+ // We've completed the decoding of headers_data, and it ended at the
+ // boundary between two HPACK block entries, so name_ and value_ are
+ // currently reset.
+ DCHECK_EQ(0u, db.Remaining());
+ in_progress_ = false;
+ break;
+
+ case DecodeStatus::kDecodeInProgress:
+ DCHECK_EQ(0u, db.Remaining());
+ in_progress_ = true;
+ if (!error_detected_) {
+ name_.BufferStringIfUnbuffered();
+ value_.BufferStringIfUnbuffered();
+ EnforceMaxDecodeBufferSize();
+ }
+ break;
+
+ case DecodeStatus::kDecodeError:
+ SetErrorDetected();
+ break;
+ }
+ }
+ return !error_detected_;
+}
+
+// Called after a HPACK block has been completely delivered via
+// HandleControlFrameHeadersData(). Returns false if an error occurred.
+// |compressed_len| if non-null will be set to the size of the encoded
+// buffered block that was accumulated in HandleControlFrameHeadersData(),
+// to support subsequent calculation of compression percentage.
+// Discards the handler supplied at the start of decoding the block.
+// TODO(jamessynge): Determine if compressed_len is needed; it is used to
+// produce UUMA stat Net.SpdyHpackDecompressionPercentage, but only for
+// SPDY3, not HTTP2.
+bool HpackDecoder2::HandleControlFrameHeadersComplete(size_t* compressed_len) {
+ DVLOG(2) << "HpackDecoder2::HandleControlFrameHeadersComplete";
+ if (error_detected_ || in_progress_) {
+ DVLOG(2) << "error_detected_=" << error_detected_
+ << ", in_progress_=" << in_progress_;
+ return false;
+ }
+ if (compressed_len != nullptr) {
+ *compressed_len = total_hpack_bytes_;
+ }
+ if (handler_ != nullptr) {
+ handler_->OnHeaderBlockEnd(total_header_bytes_);
+ }
+ Reset();
+ return true;
+}
+
+const SpdyHeaderBlock& HpackDecoder2::decoded_block() const {
+ return decoded_block_;
+}
+
+void HpackDecoder2::SetHeaderTableDebugVisitor(
+ std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
+ DVLOG(2) << "HpackDecoder2::SetHeaderTableDebugVisitor";
+ header_table_.set_debug_visitor(std::move(visitor));
+}
+
+void HpackDecoder2::set_max_decode_buffer_size_bytes(
+ size_t max_decode_buffer_size_bytes) {
+ DVLOG(2) << "HpackDecoder2::set_max_decode_buffer_size_bytes";
+ max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
+}
+
+void HpackDecoder2::OnIndexedHeader(size_t index) {
+ DVLOG(2) << "HpackDecoder2::OnIndexedHeader: index=" << index;
+ DCHECK(!error_detected_);
+ const HpackEntry* entry = header_table_.GetByIndex(index);
+ if (entry == nullptr) {
+ SetErrorDetected();
+ return;
+ }
+ HandleHeaderRepresentation(entry->name(), entry->value());
+}
+
+void HpackDecoder2::OnStartLiteralHeader(HpackEntryType entry_type,
+ size_t maybe_name_index) {
+ DVLOG(2) << "HpackDecoder2::OnStartLiteralHeader: entry_type=" << entry_type
+ << ", maybe_name_index=" << maybe_name_index;
+ DCHECK(!error_detected_);
+ entry_type_ = entry_type;
+ if (maybe_name_index > 0) {
+ const HpackEntry* entry = header_table_.GetByIndex(maybe_name_index);
+ if (entry == nullptr) {
+ SetErrorDetected();
+ return;
+ } else {
+ // Non-static entries could be evicted, leaving us with a dangling
+ // pointer, so we preemptively copy. This could be avoided if
+ // TryAddEntry would copy the strings prior to performing eviction.
+ name_.Set(entry->name(), entry->IsStatic());
+ name_.BufferStringIfUnbuffered();
+ }
+ }
+}
+
+void HpackDecoder2::OnNameStart(bool huffman_encoded, size_t len) {
+ DVLOG(2) << "HpackDecoder2::OnNameStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ if (len > max_decode_buffer_size_bytes_) {
+ DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
+ << max_decode_buffer_size_bytes_ << ")";
+ SetErrorDetected();
+ return;
+ }
+ name_.OnStart(huffman_encoded, len);
+}
+
+void HpackDecoder2::OnNameData(const char* data, size_t len) {
+ DVLOG(2) << "HpackDecoder2::OnNameData: len=" << len
+ << "\n data: " << StringPiece(data, len);
+ if (error_detected_) {
+ return;
+ }
+ if (!name_.OnData(data, len)) {
+ SetErrorDetected();
+ }
+}
+
+void HpackDecoder2::OnNameEnd() {
+ DVLOG(2) << "HpackDecoder2::OnNameEnd";
+ if (error_detected_) {
+ return;
+ }
+ if (!name_.OnEnd()) {
+ SetErrorDetected();
+ }
+}
+
+void HpackDecoder2::OnValueStart(bool huffman_encoded, size_t len) {
+ DVLOG(2) << "HpackDecoder2::OnValueStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ if (len > max_decode_buffer_size_bytes_) {
+ DVLOG(1) << "Value length (" << len << ") is longer than permitted ("
+ << max_decode_buffer_size_bytes_ << ")";
+ SetErrorDetected();
+ return;
+ }
+ value_.OnStart(huffman_encoded, len);
+}
+
+void HpackDecoder2::OnValueData(const char* data, size_t len) {
+ DVLOG(2) << "HpackDecoder2::OnValueData: len=" << len
+ << "\n data: " << StringPiece(data, len);
+ if (error_detected_) {
+ return;
+ }
+ if (!value_.OnData(data, len)) {
+ SetErrorDetected();
+ }
+}
+
+void HpackDecoder2::OnValueEnd() {
+ DVLOG(2) << "HpackDecoder2::OnValueEnd";
+ if (error_detected_) {
+ return;
+ }
+ if (!value_.OnEnd()) {
+ SetErrorDetected();
+ return;
+ }
+ if (EnforceMaxDecodeBufferSize()) {
+ // All is well.
+ HandleHeaderRepresentation(name_.str(), value_.str());
+ if (entry_type_ == HpackEntryType::kIndexedLiteralHeader) {
+ header_table_.TryAddEntry(name_.str(), value_.str());
+ }
+ name_.Reset();
+ value_.Reset();
+ }
+}
+
+void HpackDecoder2::OnDynamicTableSizeUpdate(size_t size) {
+ DVLOG(2) << "HpackDecoder2::OnDynamicTableSizeUpdate: size=" << size;
+ if (error_detected_) {
+ return;
+ }
+ if (size > header_table_.settings_size_bound()) {
+ DVLOG(1) << "Dynamic Table Size Update with too large a size: " << size
+ << " > " << header_table_.settings_size_bound();
+ SetErrorDetected();
+ return;
+ }
+ if (header_seen_) {
+ DVLOG(1) << "Dynamic Table Size Update seen after a Header";
+ SetErrorDetected();
+ return;
+ }
+ ++size_update_count_;
+ if (size_update_count_ > 2) {
+ DVLOG(1) << "Too many (" << size_update_count_
+ << ") Dynamic Table Size Updates";
+ SetErrorDetected();
+ return;
+ }
+ header_table_.SetMaxSize(size);
+ return;
+}
+
+bool HpackDecoder2::EnforceMaxDecodeBufferSize() {
+ if (!error_detected_) {
+ size_t buffered_length = name_.BufferedLength() + value_.BufferedLength();
+ DVLOG(2) << "buffered_length=" << buffered_length
+ << "; max=" << max_decode_buffer_size_bytes_;
+ if (buffered_length > max_decode_buffer_size_bytes_) {
+ DVLOG(1) << "Header length (" << buffered_length
+ << ") is longer than permitted ("
+ << max_decode_buffer_size_bytes_ << ")";
+ SetErrorDetected();
+ }
+ }
+ return !error_detected_;
+}
+
+void HpackDecoder2::HandleHeaderRepresentation(StringPiece name,
+ StringPiece value) {
+ DVLOG(2) << "HpackDecoder2::HandleHeaderRepresentation:\n name: " << name
+ << "\n value: " << value;
+ total_header_bytes_ += name.size() + value.size();
+ header_seen_ = true;
+ if (handler_ == nullptr) {
+ DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
+ << "adding to decoded_block";
+ decoded_block_.AppendValueOrAddHeader(name, value);
+ } else {
+ DVLOG(3) << "HpackDecoder2::HandleHeaderRepresentation "
+ << "passing to handler";
+ DCHECK(decoded_block_.empty());
+ handler_->OnHeader(name, value);
+ }
+}
+
+} // namespace net
« 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