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 char kCookieKey
[] = "cookie";
23 HpackDecoder::HpackDecoder(const HpackHuffmanTable
& table
)
24 : max_string_literal_size_(kDefaultMaxStringLiteralSize
),
25 regular_header_seen_(false),
26 huffman_table_(table
) {}
28 HpackDecoder::~HpackDecoder() {}
30 bool HpackDecoder::HandleControlFrameHeadersData(SpdyStreamId id
,
31 const char* headers_data
,
32 size_t headers_data_length
) {
33 decoded_block_
.clear();
35 size_t new_size
= headers_block_buffer_
.size() + headers_data_length
;
36 if (new_size
> kMaxDecodeBufferSize
) {
39 headers_block_buffer_
.insert(headers_block_buffer_
.end(),
41 headers_data
+ headers_data_length
);
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();
55 headers_block_buffer_
.clear();
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();
65 bool HpackDecoder::HandleHeaderRepresentation(StringPiece name
,
67 typedef std::pair
<std::map
<string
, string
>::iterator
, bool> InsertResult
;
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;
74 regular_header_seen_
= true;
78 if (name
== kCookieKey
) {
79 if (cookie_value_
.empty()) {
80 cookie_value_
.assign(value
.data(), value
.size());
82 cookie_value_
+= "; ";
83 cookie_value_
.insert(cookie_value_
.end(), value
.begin(), value
.end());
86 InsertResult result
= decoded_block_
.insert(
87 std::make_pair(name
.as_string(), value
.as_string()));
89 result
.first
->second
.push_back('\0');
90 result
.first
->second
.insert(result
.first
->second
.end(),
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
);
103 // Implements 7.2.1: Literal Header Field with Incremental Indexing.
104 if (input_stream
->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode
)) {
105 return DecodeNextLiteralHeader(input_stream
, true);
107 // Implements 7.2.2: Literal Header Field without Indexing.
108 if (input_stream
->MatchPrefixAndConsume(kLiteralNoIndexOpcode
)) {
109 return DecodeNextLiteralHeader(input_stream
, false);
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);
116 // Implements 7.3: Header Table Size Update.
117 if (input_stream
->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode
)) {
118 return DecodeNextHeaderTableSizeUpdate(input_stream
);
120 // Unrecognized opcode.
124 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate(
125 HpackInputStream
* input_stream
) {
127 if (!input_stream
->DecodeNextUint32(&size
)) {
130 if (size
> header_table_
.settings_size_bound()) {
133 header_table_
.SetMaxSize(size
);
137 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream
* input_stream
) {
139 if (!input_stream
->DecodeNextUint32(&index
))
142 const HpackEntry
* entry
= header_table_
.GetByIndex(index
);
146 return HandleHeaderRepresentation(entry
->name(), entry
->value());
149 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream
* input_stream
,
152 if (!DecodeNextName(input_stream
, &name
))
156 if (!DecodeNextStringLiteral(input_stream
, false, &value
))
159 if (!HandleHeaderRepresentation(name
, value
)) return false;
164 ignore_result(header_table_
.TryAddEntry(name
, value
));
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
))
174 if (index_or_zero
== 0)
175 return DecodeNextStringLiteral(input_stream
, true, next_name
);
177 const HpackEntry
* entry
= header_table_
.GetByIndex(index_or_zero
);
180 } else if (entry
->IsStatic()) {
181 *next_name
= entry
->name();
183 // |entry| could be evicted as part of this insertion. Preemptively copy.
184 key_buffer_
.assign(entry
->name());
185 *next_name
= key_buffer_
;
190 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream
* input_stream
,
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
);
198 } else if (input_stream
->MatchPrefixAndConsume(
199 kStringLiteralIdentityEncoded
)) {
200 return input_stream
->DecodeNextIdentityString(output
);