// Copyright 2014 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_decoder.h" #include "base/basictypes.h" #include "base/logging.h" #include "net/spdy/hpack_constants.h" #include "net/spdy/hpack_output_stream.h" namespace net { using base::StringPiece; using std::string; namespace { const char kCookieKey[] = "cookie"; } // namespace HpackDecoder::HpackDecoder(const HpackHuffmanTable& table) : max_string_literal_size_(kDefaultMaxStringLiteralSize), regular_header_seen_(false), huffman_table_(table) {} HpackDecoder::~HpackDecoder() {} bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id, const char* headers_data, size_t headers_data_length) { decoded_block_.clear(); size_t new_size = headers_block_buffer_.size() + headers_data_length; if (new_size > kMaxDecodeBufferSize) { return false; } headers_block_buffer_.insert(headers_block_buffer_.end(), headers_data, headers_data + headers_data_length); return true; } bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) { HpackInputStream input_stream(max_string_literal_size_, headers_block_buffer_); regular_header_seen_ = false; while (input_stream.HasMoreData()) { if (!DecodeNextOpcode(&input_stream)) { headers_block_buffer_.clear(); return false; } } headers_block_buffer_.clear(); // Emit the Cookie header, if any crumbles were encountered. if (!cookie_value_.empty()) { decoded_block_[kCookieKey] = cookie_value_; cookie_value_.clear(); } return true; } bool HpackDecoder::HandleHeaderRepresentation(StringPiece name, StringPiece value) { typedef std::pair::iterator, bool> InsertResult; // Fail if pseudo-header follows regular header. if (name.size() > 0) { if (name[0] == kPseudoHeaderPrefix) { if (regular_header_seen_) return false; } else { regular_header_seen_ = true; } } if (name == kCookieKey) { if (cookie_value_.empty()) { cookie_value_.assign(value.data(), value.size()); } else { cookie_value_ += "; "; cookie_value_.insert(cookie_value_.end(), value.begin(), value.end()); } } else { InsertResult result = decoded_block_.insert( std::make_pair(name.as_string(), value.as_string())); if (!result.second) { result.first->second.push_back('\0'); result.first->second.insert(result.first->second.end(), value.begin(), value.end()); } } return true; } bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) { // Implements 7.1: Indexed Header Field Representation. if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) { return DecodeNextIndexedHeader(input_stream); } // Implements 7.2.1: Literal Header Field with Incremental Indexing. if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) { return DecodeNextLiteralHeader(input_stream, true); } // Implements 7.2.2: Literal Header Field without Indexing. if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) { return DecodeNextLiteralHeader(input_stream, false); } // Implements 7.2.3: Literal Header Field never Indexed. // TODO(jgraettinger): Preserve the never-indexed bit. if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) { return DecodeNextLiteralHeader(input_stream, false); } // Implements 7.3: Header Table Size Update. if (input_stream->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode)) { return DecodeNextHeaderTableSizeUpdate(input_stream); } // Unrecognized opcode. return false; } bool HpackDecoder::DecodeNextHeaderTableSizeUpdate( HpackInputStream* input_stream) { uint32 size = 0; if (!input_stream->DecodeNextUint32(&size)) { return false; } if (size > header_table_.settings_size_bound()) { return false; } header_table_.SetMaxSize(size); return true; } bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) { uint32 index = 0; if (!input_stream->DecodeNextUint32(&index)) return false; const HpackEntry* entry = header_table_.GetByIndex(index); if (entry == NULL) return false; return HandleHeaderRepresentation(entry->name(), entry->value()); } bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream, bool should_index) { StringPiece name; if (!DecodeNextName(input_stream, &name)) return false; StringPiece value; if (!DecodeNextStringLiteral(input_stream, false, &value)) return false; if (!HandleHeaderRepresentation(name, value)) return false; if (!should_index) return true; ignore_result(header_table_.TryAddEntry(name, value)); return true; } bool HpackDecoder::DecodeNextName( HpackInputStream* input_stream, StringPiece* next_name) { uint32 index_or_zero = 0; if (!input_stream->DecodeNextUint32(&index_or_zero)) return false; if (index_or_zero == 0) return DecodeNextStringLiteral(input_stream, true, next_name); const HpackEntry* entry = header_table_.GetByIndex(index_or_zero); if (entry == NULL) { return false; } else if (entry->IsStatic()) { *next_name = entry->name(); } else { // |entry| could be evicted as part of this insertion. Preemptively copy. key_buffer_.assign(entry->name()); *next_name = key_buffer_; } return true; } bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, bool is_key, StringPiece* output) { if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { string* buffer = is_key ? &key_buffer_ : &value_buffer_; bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer); *output = StringPiece(*buffer); return result; } else if (input_stream->MatchPrefixAndConsume( kStringLiteralIdentityEncoded)) { return input_stream->DecodeNextIdentityString(output); } else { return false; } } } // namespace net