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 12namespace net { 13 14using base::StringPiece; 15using std::string; 16 17namespace { 18 19const uint8 kNoState = 0; 20// Set on entries added to the reference set during this decoding. 21const uint8 kReferencedThisEncoding = 1; 22 23const char kCookieKey[] = "cookie"; 24 25} // namespace 26 27HpackDecoder::HpackDecoder(const HpackHuffmanTable& table) 28 : max_string_literal_size_(kDefaultMaxStringLiteralSize), 29 huffman_table_(table) {} 30 31HpackDecoder::~HpackDecoder() {} 32 33bool 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 48bool 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 82void 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 105bool 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 131bool 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 151bool 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 179bool 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 202bool 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 224bool 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