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/hpack_decoder.h"
9 #include "base/basictypes.h"
10 #include "base/logging.h"
11 #include "net/spdy/hpack/hpack_constants.h"
12 #include "net/spdy/hpack/hpack_output_stream.h"
16 using base::StringPiece
;
21 const char kCookieKey
[] = "cookie";
25 HpackDecoder::HpackDecoder(const HpackHuffmanTable
& table
)
26 : max_string_literal_size_(kDefaultMaxStringLiteralSize
),
27 regular_header_seen_(false),
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(), headers_data
,
42 headers_data
+ headers_data_length
);
46 bool HpackDecoder::HandleControlFrameHeadersComplete(SpdyStreamId id
,
47 size_t* compressed_len
) {
48 HpackInputStream
input_stream(max_string_literal_size_
,
49 headers_block_buffer_
);
50 regular_header_seen_
= false;
52 *compressed_len
= headers_block_buffer_
.size();
54 while (input_stream
.HasMoreData()) {
55 if (!DecodeNextOpcode(&input_stream
)) {
56 headers_block_buffer_
.clear();
60 headers_block_buffer_
.clear();
65 bool HpackDecoder::HandleHeaderRepresentation(StringPiece name
,
67 // Fail if pseudo-header follows regular header.
68 if (name
.size() > 0) {
69 if (name
[0] == kPseudoHeaderPrefix
) {
70 if (regular_header_seen_
) {
74 regular_header_seen_
= true;
78 if (name
== kCookieKey
) {
79 // Create new cookie header, or append to existing.
80 auto it
= decoded_block_
.find(kCookieKey
);
81 if (it
== decoded_block_
.end()) {
82 decoded_block_
[kCookieKey
].assign(value
.data(), value
.size());
84 decoded_block_
[kCookieKey
].append("; ");
85 value
.AppendToString(&decoded_block_
[kCookieKey
]);
88 auto result
= decoded_block_
.insert(
89 std::make_pair(name
.as_string(), value
.as_string()));
91 // Key |name| already exists, append new value.
92 result
.first
->second
.push_back('\0');
93 result
.first
->second
.insert(result
.first
->second
.end(), value
.begin(),
100 bool HpackDecoder::DecodeNextOpcode(HpackInputStream
* input_stream
) {
101 // Implements 7.1: Indexed Header Field Representation.
102 if (input_stream
->MatchPrefixAndConsume(kIndexedOpcode
)) {
103 return DecodeNextIndexedHeader(input_stream
);
105 // Implements 7.2.1: Literal Header Field with Incremental Indexing.
106 if (input_stream
->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode
)) {
107 return DecodeNextLiteralHeader(input_stream
, true);
109 // Implements 7.2.2: Literal Header Field without Indexing.
110 if (input_stream
->MatchPrefixAndConsume(kLiteralNoIndexOpcode
)) {
111 return DecodeNextLiteralHeader(input_stream
, false);
113 // Implements 7.2.3: Literal Header Field never Indexed.
114 // TODO(jgraettinger): Preserve the never-indexed bit.
115 if (input_stream
->MatchPrefixAndConsume(kLiteralNeverIndexOpcode
)) {
116 return DecodeNextLiteralHeader(input_stream
, false);
118 // Implements 7.3: Header Table Size Update.
119 if (input_stream
->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode
)) {
120 return DecodeNextHeaderTableSizeUpdate(input_stream
);
122 // Unrecognized opcode.
126 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate(
127 HpackInputStream
* input_stream
) {
129 if (!input_stream
->DecodeNextUint32(&size
)) {
132 if (size
> header_table_
.settings_size_bound()) {
135 header_table_
.SetMaxSize(size
);
139 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream
* input_stream
) {
141 if (!input_stream
->DecodeNextUint32(&index
)) {
145 const HpackEntry
* entry
= header_table_
.GetByIndex(index
);
150 return HandleHeaderRepresentation(entry
->name(), entry
->value());
153 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream
* input_stream
,
156 if (!DecodeNextName(input_stream
, &name
)) {
161 if (!DecodeNextStringLiteral(input_stream
, false, &value
)) {
165 if (!HandleHeaderRepresentation(name
, value
)) {
173 ignore_result(header_table_
.TryAddEntry(name
, value
));
177 bool HpackDecoder::DecodeNextName(HpackInputStream
* input_stream
,
178 StringPiece
* next_name
) {
179 uint32 index_or_zero
= 0;
180 if (!input_stream
->DecodeNextUint32(&index_or_zero
)) {
184 if (index_or_zero
== 0) {
185 return DecodeNextStringLiteral(input_stream
, true, next_name
);
188 const HpackEntry
* entry
= header_table_
.GetByIndex(index_or_zero
);
192 if (entry
->IsStatic()) {
193 *next_name
= entry
->name();
195 // |entry| could be evicted as part of this insertion. Preemptively copy.
196 key_buffer_
.assign(entry
->name());
197 *next_name
= key_buffer_
;
202 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream
* input_stream
,
204 StringPiece
* output
) {
205 if (input_stream
->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded
)) {
206 string
* buffer
= is_key
? &key_buffer_
: &value_buffer_
;
207 bool result
= input_stream
->DecodeNextHuffmanString(huffman_table_
, buffer
);
208 *output
= StringPiece(*buffer
);
211 if (input_stream
->MatchPrefixAndConsume(kStringLiteralIdentityEncoded
)) {
212 return input_stream
->DecodeNextIdentityString(output
);