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 char kCookieKey[] = "cookie"; 20 21 } // namespace 22 23 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table) 24 : max_string_literal_size_(kDefaultMaxStringLiteralSize), 25 regular_header_seen_(false), 26 huffman_table_(table) {} 27 28 HpackDecoder::~HpackDecoder() {} 29 30 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id, 31 const char* headers_data, 32 size_t headers_data_length) { 33 decoded_block_.clear(); 34 35 size_t new_size = headers_block_buffer_.size() + headers_data_length; 36 if (new_size > kMaxDecodeBufferSize) { 37 return false; 38 } 39 headers_block_buffer_.insert(headers_block_buffer_.end(), 40 headers_data, 41 headers_data + headers_data_length); 42 return true; 43 } 44 45 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) { 46 HpackInputStream input_stream(max_string_literal_size_, 47 headers_block_buffer_); 48 regular_header_seen_ = false; 49 while (input_stream.HasMoreData()) { 50 if (!DecodeNextOpcode(&input_stream)) { 51 headers_block_buffer_.clear(); 52 return false; 53 } 54 } 55 headers_block_buffer_.clear(); 56 57 // Emit the Cookie header, if any crumbles were encountered. 58 if (!cookie_value_.empty()) { 59 decoded_block_[kCookieKey] = cookie_value_; 60 cookie_value_.clear(); 61 } 62 return true; 63 } 64 65 bool HpackDecoder::HandleHeaderRepresentation(StringPiece name, 66 StringPiece value) { 67 typedef std::pair<std::map<string, string>::iterator, bool> InsertResult; 68 69 // Fail if pseudo-header follows regular header. 70 if (name.size() > 0) { 71 if (name[0] == kPseudoHeaderPrefix) { 72 if (regular_header_seen_) return false; 73 } else { 74 regular_header_seen_ = true; 75 } 76 } 77 78 if (name == kCookieKey) { 79 if (cookie_value_.empty()) { 80 cookie_value_.assign(value.data(), value.size()); 81 } else { 82 cookie_value_ += "; "; 83 cookie_value_.insert(cookie_value_.end(), value.begin(), value.end()); 84 } 85 } else { 86 InsertResult result = decoded_block_.insert( 87 std::make_pair(name.as_string(), value.as_string())); 88 if (!result.second) { 89 result.first->second.push_back('\0'); 90 result.first->second.insert(result.first->second.end(), 91 value.begin(), 92 value.end()); 93 } 94 } 95 return true; 96 } 97 98 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) { 99 // Implements 7.1: Indexed Header Field Representation. 100 if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) { 101 return DecodeNextIndexedHeader(input_stream); 102 } 103 // Implements 7.2.1: Literal Header Field with Incremental Indexing. 104 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) { 105 return DecodeNextLiteralHeader(input_stream, true); 106 } 107 // Implements 7.2.2: Literal Header Field without Indexing. 108 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) { 109 return DecodeNextLiteralHeader(input_stream, false); 110 } 111 // Implements 7.2.3: Literal Header Field never Indexed. 112 // TODO(jgraettinger): Preserve the never-indexed bit. 113 if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) { 114 return DecodeNextLiteralHeader(input_stream, false); 115 } 116 // Implements 7.3: Header Table Size Update. 117 if (input_stream->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode)) { 118 return DecodeNextHeaderTableSizeUpdate(input_stream); 119 } 120 // Unrecognized opcode. 121 return false; 122 } 123 124 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate( 125 HpackInputStream* input_stream) { 126 uint32 size = 0; 127 if (!input_stream->DecodeNextUint32(&size)) { 128 return false; 129 } 130 if (size > header_table_.settings_size_bound()) { 131 return false; 132 } 133 header_table_.SetMaxSize(size); 134 return true; 135 } 136 137 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) { 138 uint32 index = 0; 139 if (!input_stream->DecodeNextUint32(&index)) 140 return false; 141 142 const HpackEntry* entry = header_table_.GetByIndex(index); 143 if (entry == NULL) 144 return false; 145 146 return HandleHeaderRepresentation(entry->name(), entry->value()); 147 } 148 149 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream, 150 bool should_index) { 151 StringPiece name; 152 if (!DecodeNextName(input_stream, &name)) 153 return false; 154 155 StringPiece value; 156 if (!DecodeNextStringLiteral(input_stream, false, &value)) 157 return false; 158 159 if (!HandleHeaderRepresentation(name, value)) return false; 160 161 if (!should_index) 162 return true; 163 164 ignore_result(header_table_.TryAddEntry(name, value)); 165 return true; 166 } 167 168 bool HpackDecoder::DecodeNextName( 169 HpackInputStream* input_stream, StringPiece* next_name) { 170 uint32 index_or_zero = 0; 171 if (!input_stream->DecodeNextUint32(&index_or_zero)) 172 return false; 173 174 if (index_or_zero == 0) 175 return DecodeNextStringLiteral(input_stream, true, next_name); 176 177 const HpackEntry* entry = header_table_.GetByIndex(index_or_zero); 178 if (entry == NULL) { 179 return false; 180 } else if (entry->IsStatic()) { 181 *next_name = entry->name(); 182 } else { 183 // |entry| could be evicted as part of this insertion. Preemptively copy. 184 key_buffer_.assign(entry->name()); 185 *next_name = key_buffer_; 186 } 187 return true; 188 } 189 190 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, 191 bool is_key, 192 StringPiece* output) { 193 if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { 194 string* buffer = is_key ? &key_buffer_ : &value_buffer_; 195 bool result = input_stream->DecodeNextHuffmanString(huffman_table_, buffer); 196 *output = StringPiece(*buffer); 197 return result; 198 } else if (input_stream->MatchPrefixAndConsume( 199 kStringLiteralIdentityEncoded)) { 200 return input_stream->DecodeNextIdentityString(output); 201 } else { 202 return false; 203 } 204 } 205 206 } // namespace net 207