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"
14 using base::StringPiece
;
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";
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
) {
42 headers_block_buffer_
.insert(headers_block_buffer_
.end(),
44 headers_data
+ headers_data_length
);
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();
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());
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();
82 void HpackDecoder::HandleHeaderRepresentation(StringPiece name
,
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());
90 cookie_value_
+= "; ";
91 cookie_value_
.insert(cookie_value_
.end(), value
.begin(), value
.end());
94 InsertResult result
= decoded_block_
.insert(
95 std::make_pair(name
.as_string(), value
.as_string()));
97 result
.first
->second
.push_back('\0');
98 result
.first
->second
.insert(result
.first
->second
.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.
131 bool HpackDecoder::DecodeNextContextUpdate(HpackInputStream
* input_stream
) {
132 if (input_stream
->MatchPrefixAndConsume(kEncodingContextEmptyReferenceSet
)) {
133 header_table_
.ClearReferenceSet();
136 if (input_stream
->MatchPrefixAndConsume(kEncodingContextNewMaximumSize
)) {
138 if (!input_stream
->DecodeNextUint32(&size
)) {
141 if (size
> header_table_
.settings_size_bound()) {
144 header_table_
.SetMaxSize(size
);
147 // Unrecognized encoding context update.
151 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream
* input_stream
) {
153 if (!input_stream
->DecodeNextUint32(&index
))
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
);