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.
5 #include "net/spdy/hpack_decoder.h"
7 #include "base/basictypes.h"
8 #include "base/logging.h"
9 #include "base/strings/string_util.h"
10 #include "net/spdy/hpack_constants.h"
11 #include "net/spdy/hpack_output_stream.h"
15 using base::StringPiece;
20 const uint8 kNoState = 0;
21 // Set on entries added to the reference set during this decoding.
22 const uint8 kReferencedThisEncoding = 1;
26 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
27 : max_string_literal_size_(kDefaultMaxStringLiteralSize),
28 huffman_table_(table) {}
30 HpackDecoder::~HpackDecoder() {}
32 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id,
33 const char* headers_data,
34 size_t headers_data_length) {
35 decoded_block_.clear();
37 size_t new_size = headers_block_buffer_.size() + headers_data_length;
38 if (new_size > kMaxDecodeBufferSize) {
41 headers_block_buffer_.insert(headers_block_buffer_.end(),
43 headers_data + headers_data_length);
47 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id) {
48 HpackInputStream input_stream(max_string_literal_size_,
49 headers_block_buffer_);
50 while (input_stream.HasMoreData()) {
51 if (!DecodeNextOpcode(&input_stream))
54 headers_block_buffer_.clear();
56 // Emit everything in the reference set that hasn't already been emitted.
57 // Also clear entry state for the next decoded headers block.
58 // TODO(jgraettinger): We may need to revisit the order in which headers
59 // are emitted (b/14051713).
60 for (HpackEntry::OrderedSet::const_iterator it =
61 header_table_.reference_set().begin();
62 it != header_table_.reference_set().end(); ++it) {
63 HpackEntry* entry = *it;
65 if (entry->state() == kNoState) {
66 HandleHeaderRepresentation(entry->name(), entry->value());
68 entry->set_state(kNoState);
71 // Emit the Cookie header, if any crumbles were encountered.
72 if (!cookie_name_.empty()) {
73 decoded_block_[cookie_name_] = cookie_value_;
75 cookie_value_.clear();
80 void HpackDecoder::HandleHeaderRepresentation(StringPiece name,
82 typedef std::pair<std::map<string, string>::iterator, bool> InsertResult;
84 // TODO(jgraettinger): HTTP/2 requires strict lowercasing of headers,
85 // and the permissiveness here isn't wanted. Back this out in upstream.
86 if (LowerCaseEqualsASCII(name.begin(), name.end(), "cookie")) {
87 if (cookie_name_.empty()) {
88 cookie_name_.assign(name.data(), name.size());
89 cookie_value_.assign(value.data(), value.size());
91 cookie_value_ += "; ";
92 cookie_value_.insert(cookie_value_.end(), value.begin(), value.end());
95 InsertResult result = decoded_block_.insert(
96 std::make_pair(name.as_string(), value.as_string()));
98 result.first->second.push_back('\0');
99 result.first->second.insert(result.first->second.end(),
106 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) {
107 // Implements 4.4: Encoding context update. Context updates are a special-case
108 // of indexed header, and must be tested prior to |kIndexedOpcode| below.
109 if (input_stream->MatchPrefixAndConsume(kEncodingContextOpcode)) {
110 return DecodeNextContextUpdate(input_stream);
112 // Implements 4.2: Indexed Header Field Representation.
113 if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) {
114 return DecodeNextIndexedHeader(input_stream);
116 // Implements 4.3.1: Literal Header Field without Indexing.
117 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
118 return DecodeNextLiteralHeader(input_stream, false);
120 // Implements 4.3.2: Literal Header Field with Incremental Indexing.
121 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) {
122 return DecodeNextLiteralHeader(input_stream, true);
124 // Unrecognized opcode.
128 bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream* input_stream) {
129 if (input_stream->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet)) {
130 header_table_.ClearReferenceSet();
133 if (input_stream->MatchPrefixAndConsume(kEncodingContextNewMaximumSize)) {
135 if (!input_stream->DecodeNextUint32(&size)) {
138 if (size > header_table_.settings_size_bound()) {
141 header_table_.SetMaxSize(size);
144 // Unrecognized encoding context update.
148 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
150 if (!input_stream->DecodeNextUint32(&index))
153 // If index == 0, |kEncodingContextOpcode| would have matched.
156 HpackEntry* entry = header_table_.GetByIndex(index);
160 if (entry->IsStatic()) {
161 HandleHeaderRepresentation(entry->name(), entry->value());
163 HpackEntry* new_entry = header_table_.TryAddEntry(
164 entry->name(), entry->value());
166 header_table_.Toggle(new_entry);
167 new_entry->set_state(kReferencedThisEncoding);
170 entry->set_state(kNoState);
171 if (header_table_.Toggle(entry)) {
172 HandleHeaderRepresentation(entry->name(), entry->value());
173 entry->set_state(kReferencedThisEncoding);
179 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream,
182 if (!DecodeNextName(input_stream, &name))
186 if (!DecodeNextStringLiteral(input_stream, false, &value))
189 HandleHeaderRepresentation(name, value);
194 HpackEntry* new_entry = header_table_.TryAddEntry(name, value);
196 header_table_.Toggle(new_entry);
197 new_entry->set_state(kReferencedThisEncoding);
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))
208 if (index_or_zero == 0)
209 return DecodeNextStringLiteral(input_stream, true, next_name);
211 const HpackEntry* entry = header_table_.GetByIndex(index_or_zero);
214 } else if (entry->IsStatic()) {
215 *next_name = entry->name();
217 // |entry| could be evicted as part of this insertion. Preemptively copy.
218 key_buffer_.assign(entry->name());
219 *next_name = key_buffer_;
224 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream,
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);
232 } else if (input_stream->MatchPrefixAndConsume(
233 kStringLiteralIdentityEncoded)) {
234 return input_stream->DecodeNextIdentityString(output);