Home | History | Annotate | Download | only in spdy
      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_decoder.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/logging.h"
      9 #include "net/spdy/hpack_constants.h"
     10 #include "net/spdy/hpack_output_stream.h"
     11 
     12 namespace net {
     13 
     14 using base::StringPiece;
     15 using std::string;
     16 
     17 namespace {
     18 
     19 const uint8 kNoState = 0;
     20 // Set on entries added to the reference set during this decoding.
     21 const uint8 kReferencedThisEncoding = 1;
     22 
     23 const char kCookieKey[] = "cookie";
     24 
     25 }  // namespace
     26 
     27 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
     28     : max_string_literal_size_(kDefaultMaxStringLiteralSize),
     29       huffman_table_(table) {}
     30 
     31 HpackDecoder::~HpackDecoder() {}
     32 
     33 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id,
     34                                                  const char* headers_data,
     35                                                  size_t headers_data_length) {
     36   decoded_block_.clear();
     37 
     38   size_t new_size = headers_block_buffer_.size() + headers_data_length;
     39   if (new_size > kMaxDecodeBufferSize) {
     40     return false;
     41   }
     42   headers_block_buffer_.insert(headers_block_buffer_.end(),
     43                                headers_data,
     44                                headers_data + headers_data_length);
     45   return true;
     46 }
     47 
     48 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) {
     49   HpackInputStream input_stream(max_string_literal_size_,
     50                                 headers_block_buffer_);
     51   while (input_stream.HasMoreData()) {
     52     if (!DecodeNextOpcode(&input_stream)) {
     53       headers_block_buffer_.clear();
     54       return false;
     55     }
     56   }
     57   headers_block_buffer_.clear();
     58 
     59   // Emit everything in the reference set that hasn't already been emitted.
     60   // Also clear entry state for the next decoded headers block.
     61   // TODO(jgraettinger): We may need to revisit the order in which headers
     62   // are emitted (b/14051713).
     63   for (HpackHeaderTable::OrderedEntrySet::const_iterator it =
     64           header_table_.reference_set().begin();
     65        it != header_table_.reference_set().end(); ++it) {
     66     HpackEntry* entry = *it;
     67 
     68     if (entry->state() == kNoState) {
     69       HandleHeaderRepresentation(entry->name(), entry->value());
     70     } else {
     71       entry->set_state(kNoState);
     72     }
     73   }
     74   // Emit the Cookie header, if any crumbles were encountered.
     75   if (!cookie_value_.empty()) {
     76     decoded_block_[kCookieKey] = cookie_value_;
     77     cookie_value_.clear();
     78   }
     79   return true;
     80 }
     81 
     82 void HpackDecoder::HandleHeaderRepresentation(StringPiece name,
     83                                               StringPiece value) {
     84   typedef std::pair<std::map<string, string>::iterator, bool> InsertResult;
     85 
     86   if (name == kCookieKey) {
     87     if (cookie_value_.empty()) {
     88       cookie_value_.assign(value.data(), value.size());
     89     } else {
     90       cookie_value_ += "; ";
     91       cookie_value_.insert(cookie_value_.end(), value.begin(), value.end());
     92     }
     93   } else {
     94     InsertResult result = decoded_block_.insert(
     95         std::make_pair(name.as_string(), value.as_string()));
     96     if (!result.second) {
     97       result.first->second.push_back('\0');
     98       result.first->second.insert(result.first->second.end(),
     99                                   value.begin(),
    100                                   value.end());
    101     }
    102   }
    103 }
    104 
    105 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) {
    106   // Implements 4.2: Indexed Header Field Representation.
    107   if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) {
    108     return DecodeNextIndexedHeader(input_stream);
    109   }
    110   // Implements 4.3.1: Literal Header Field without Indexing.
    111   if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
    112     return DecodeNextLiteralHeader(input_stream, false);
    113   }
    114   // Implements 4.3.2: Literal Header Field with Incremental Indexing.
    115   if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) {
    116     return DecodeNextLiteralHeader(input_stream, true);
    117   }
    118   // Implements 4.3.3: Literal Header Field never Indexed.
    119   // TODO(jgraettinger): Preserve the never-indexed bit.
    120   if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) {
    121     return DecodeNextLiteralHeader(input_stream, false);
    122   }
    123   // Implements 4.4: Encoding context update.
    124   if (input_stream->MatchPrefixAndConsume(kEncodingContextOpcode)) {
    125     return DecodeNextContextUpdate(input_stream);
    126   }
    127   // Unrecognized opcode.
    128   return false;
    129 }
    130 
    131 bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream* input_stream) {
    132   if (input_stream->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet)) {
    133     header_table_.ClearReferenceSet();
    134     return true;
    135   }
    136   if (input_stream->MatchPrefixAndConsume(kEncodingContextNewMaximumSize)) {
    137     uint32 size = 0;
    138     if (!input_stream->DecodeNextUint32(&size)) {
    139       return false;
    140     }
    141     if (size > header_table_.settings_size_bound()) {
    142       return false;
    143     }
    144     header_table_.SetMaxSize(size);
    145     return true;
    146   }
    147   // Unrecognized encoding context update.
    148   return false;
    149 }
    150 
    151 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
    152   uint32 index = 0;
    153   if (!input_stream->DecodeNextUint32(&index))
    154     return false;
    155 
    156   HpackEntry* entry = header_table_.GetByIndex(index);
    157   if (entry == NULL)
    158     return false;
    159 
    160   if (entry->IsStatic()) {
    161     HandleHeaderRepresentation(entry->name(), entry->value());
    162 
    163     HpackEntry* new_entry = header_table_.TryAddEntry(
    164         entry->name(), entry->value());
    165     if (new_entry) {
    166       header_table_.Toggle(new_entry);
    167       new_entry->set_state(kReferencedThisEncoding);
    168     }
    169   } else {
    170     entry->set_state(kNoState);
    171     if (header_table_.Toggle(entry)) {
    172       HandleHeaderRepresentation(entry->name(), entry->value());
    173       entry->set_state(kReferencedThisEncoding);
    174     }
    175   }
    176   return true;
    177 }
    178 
    179 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream,
    180                                            bool should_index) {
    181   StringPiece name;
    182   if (!DecodeNextName(input_stream, &name))
    183     return false;
    184 
    185   StringPiece value;
    186   if (!DecodeNextStringLiteral(input_stream, false, &value))
    187     return false;
    188 
    189   HandleHeaderRepresentation(name, value);
    190 
    191   if (!should_index)
    192     return true;
    193 
    194   HpackEntry* new_entry = header_table_.TryAddEntry(name, value);
    195   if (new_entry) {
    196     header_table_.Toggle(new_entry);
    197     new_entry->set_state(kReferencedThisEncoding);
    198   }
    199   return true;
    200 }
    201 
    202 bool HpackDecoder::DecodeNextName(
    203     HpackInputStream* input_stream, StringPiece* next_name) {
    204   uint32 index_or_zero = 0;
    205   if (!input_stream->DecodeNextUint32(&index_or_zero))
    206     return false;
    207 
    208   if (index_or_zero == 0)
    209     return DecodeNextStringLiteral(input_stream, true, next_name);
    210 
    211   const HpackEntry* entry = header_table_.GetByIndex(index_or_zero);
    212   if (entry == NULL) {
    213     return false;
    214   } else if (entry->IsStatic()) {
    215     *next_name = entry->name();
    216   } else {
    217     // |entry| could be evicted as part of this insertion. Preemptively copy.
    218     key_buffer_.assign(entry->name());
    219     *next_name = key_buffer_;
    220   }
    221   return true;
    222 }
    223 
    224 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream,
    225                                            bool is_key,
    226                                            StringPiece* output) {
    227   if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) {
    228     string* buffer = is_key ? &key_buffer_ : &value_buffer_;
    229     bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer);
    230     *output = StringPiece(*buffer);
    231     return result;
    232   } else if (input_stream->MatchPrefixAndConsume(
    233       kStringLiteralIdentityEncoded)) {
    234     return input_stream->DecodeNextIdentityString(output);
    235   } else {
    236     return false;
    237   }
    238 }
    239 
    240 }  // namespace net
    241