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