Using pre-existing constants instead of hard-coding
[chromium-blink-merge.git] / net / spdy / hpack_decoder.cc
blobabd579ad007049be6730eccd728e881109da6fa2
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 "net/spdy/hpack_constants.h"
10 #include "net/spdy/hpack_output_stream.h"
12 namespace net {
14 using base::StringPiece;
15 using std::string;
17 namespace {
19 const uint8 kNoState = 0;
20 // Set on entries added to the reference set during this decoding.
21 const uint8 kReferencedThisEncoding = 1;
23 const char kCookieKey[] = "cookie";
25 } // namespace
27 HpackDecoder::HpackDecoder(const HpackHuffmanTable& table)
28 : max_string_literal_size_(kDefaultMaxStringLiteralSize),
29 huffman_table_(table) {}
31 HpackDecoder::~HpackDecoder() {}
33 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id,
34 const char* headers_data,
35 size_t headers_data_length) {
36 decoded_block_.clear();
38 size_t new_size = headers_block_buffer_.size() + headers_data_length;
39 if (new_size > kMaxDecodeBufferSize) {
40 return false;
42 headers_block_buffer_.insert(headers_block_buffer_.end(),
43 headers_data,
44 headers_data + headers_data_length);
45 return true;
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;
57 headers_block_buffer_.clear();
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;
68 if (entry->state() == kNoState) {
69 HandleHeaderRepresentation(entry->name(), entry->value());
70 } else {
71 entry->set_state(kNoState);
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();
79 return true;
82 void HpackDecoder::HandleHeaderRepresentation(StringPiece name,
83 StringPiece value) {
84 typedef std::pair<std::map<string, string>::iterator, bool> InsertResult;
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());
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());
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);
110 // Implements 4.3.1: Literal Header Field without Indexing.
111 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) {
112 return DecodeNextLiteralHeader(input_stream, false);
114 // Implements 4.3.2: Literal Header Field with Incremental Indexing.
115 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) {
116 return DecodeNextLiteralHeader(input_stream, true);
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);
123 // Implements 4.4: Encoding context update.
124 if (input_stream->MatchPrefixAndConsume(kEncodingContextOpcode)) {
125 return DecodeNextContextUpdate(input_stream);
127 // Unrecognized opcode.
128 return false;
131 bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream* input_stream) {
132 if (input_stream->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet)) {
133 header_table_.ClearReferenceSet();
134 return true;
136 if (input_stream->MatchPrefixAndConsume(kEncodingContextNewMaximumSize)) {
137 uint32 size = 0;
138 if (!input_stream->DecodeNextUint32(&size)) {
139 return false;
141 if (size > header_table_.settings_size_bound()) {
142 return false;
144 header_table_.SetMaxSize(size);
145 return true;
147 // Unrecognized encoding context update.
148 return false;
151 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) {
152 uint32 index = 0;
153 if (!input_stream->DecodeNextUint32(&index))
154 return false;
156 HpackEntry* entry = header_table_.GetByIndex(index);
157 if (entry == NULL)
158 return false;
160 if (entry->IsStatic()) {
161 HandleHeaderRepresentation(entry->name(), entry->value());
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);
169 } else {
170 entry->set_state(kNoState);
171 if (header_table_.Toggle(entry)) {
172 HandleHeaderRepresentation(entry->name(), entry->value());
173 entry->set_state(kReferencedThisEncoding);
176 return true;
179 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream,
180 bool should_index) {
181 StringPiece name;
182 if (!DecodeNextName(input_stream, &name))
183 return false;
185 StringPiece value;
186 if (!DecodeNextStringLiteral(input_stream, false, &value))
187 return false;
189 HandleHeaderRepresentation(name, value);
191 if (!should_index)
192 return true;
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);
199 return true;
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;
208 if (index_or_zero == 0)
209 return DecodeNextStringLiteral(input_stream, true, next_name);
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_;
221 return true;
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;
240 } // namespace net