|  | // 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<std::map<string, string>::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 |