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_encoder.h"
9 #include "base/logging.h"
10 #include "net/spdy/hpack_header_table.h"
11 #include "net/spdy/hpack_huffman_table.h"
12 #include "net/spdy/hpack_output_stream.h"
16 using base::StringPiece
;
19 HpackEncoder::HpackEncoder(const HpackHuffmanTable
& table
)
21 allow_huffman_compression_(true),
22 huffman_table_(table
),
24 total_char_counts_(NULL
) {}
26 HpackEncoder::~HpackEncoder() {}
28 bool HpackEncoder::EncodeHeaderSet(const std::map
<string
, string
>& header_set
,
30 // Flatten & crumble headers into an ordered list of representations.
31 Representations full_set
;
32 for (std::map
<string
, string
>::const_iterator it
= header_set
.begin();
33 it
!= header_set
.end(); ++it
) {
34 if (it
->first
== "cookie") {
35 // |CookieToCrumbs()| produces ordered crumbs.
36 CookieToCrumbs(*it
, &full_set
);
38 // Note std::map guarantees representations are ordered.
39 full_set
.push_back(make_pair(
40 StringPiece(it
->first
), StringPiece(it
->second
)));
44 // Walk this ordered list and encode entries.
45 for (Representations::const_iterator it
= full_set
.begin();
46 it
!= full_set
.end(); ++it
) {
47 HpackEntry
* entry
= header_table_
.GetByNameAndValue(it
->first
, it
->second
);
51 // TODO(bnc): if another entry in the header table is about to be evicted
52 // but it appears in the header list, emit that by index first.
53 EmitIndexedLiteral(*it
);
57 output_stream_
.TakeString(output
);
61 bool HpackEncoder::EncodeHeaderSetWithoutCompression(
62 const std::map
<string
, string
>& header_set
,
65 allow_huffman_compression_
= false;
66 for (std::map
<string
, string
>::const_iterator it
= header_set
.begin();
67 it
!= header_set
.end(); ++it
) {
68 // Note that cookies are not crumbled in this case.
69 EmitNonIndexedLiteral(*it
);
71 allow_huffman_compression_
= true;
72 output_stream_
.TakeString(output
);
76 void HpackEncoder::EmitIndex(HpackEntry
* entry
) {
77 output_stream_
.AppendPrefix(kIndexedOpcode
);
78 output_stream_
.AppendUint32(header_table_
.IndexOf(entry
));
81 void HpackEncoder::EmitIndexedLiteral(const Representation
& representation
) {
82 output_stream_
.AppendPrefix(kLiteralIncrementalIndexOpcode
);
83 EmitLiteral(representation
);
84 header_table_
.TryAddEntry(representation
.first
, representation
.second
);
87 void HpackEncoder::EmitNonIndexedLiteral(
88 const Representation
& representation
) {
89 output_stream_
.AppendPrefix(kLiteralNoIndexOpcode
);
90 output_stream_
.AppendUint32(0);
91 EmitString(representation
.first
);
92 EmitString(representation
.second
);
95 void HpackEncoder::EmitLiteral(const Representation
& representation
) {
96 const HpackEntry
* name_entry
= header_table_
.GetByName(representation
.first
);
97 if (name_entry
!= NULL
) {
98 output_stream_
.AppendUint32(header_table_
.IndexOf(name_entry
));
100 output_stream_
.AppendUint32(0);
101 EmitString(representation
.first
);
103 EmitString(representation
.second
);
106 void HpackEncoder::EmitString(StringPiece str
) {
107 size_t encoded_size
= (!allow_huffman_compression_
? str
.size()
108 : huffman_table_
.EncodedSize(str
));
109 if (encoded_size
< str
.size()) {
110 output_stream_
.AppendPrefix(kStringLiteralHuffmanEncoded
);
111 output_stream_
.AppendUint32(encoded_size
);
112 huffman_table_
.EncodeString(str
, &output_stream_
);
114 output_stream_
.AppendPrefix(kStringLiteralIdentityEncoded
);
115 output_stream_
.AppendUint32(str
.size());
116 output_stream_
.AppendBytes(str
);
118 UpdateCharacterCounts(str
);
121 void HpackEncoder::SetCharCountsStorage(std::vector
<size_t>* char_counts
,
122 size_t* total_char_counts
) {
123 CHECK_LE(256u, char_counts
->size());
124 char_counts_
= char_counts
;
125 total_char_counts_
= total_char_counts
;
128 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str
) {
129 if (char_counts_
== NULL
|| total_char_counts_
== NULL
) {
132 for (StringPiece::const_iterator it
= str
.begin(); it
!= str
.end(); ++it
) {
133 ++(*char_counts_
)[static_cast<uint8
>(*it
)];
135 (*total_char_counts_
) += str
.size();
139 void HpackEncoder::CookieToCrumbs(const Representation
& cookie
,
140 Representations
* out
) {
141 size_t prior_size
= out
->size();
143 // See Section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
144 // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
145 // Cookie values are split into individually-encoded HPACK representations.
146 for (size_t pos
= 0;;) {
147 size_t end
= cookie
.second
.find(";", pos
);
149 if (end
== StringPiece::npos
) {
150 out
->push_back(make_pair(
152 cookie
.second
.substr(pos
)));
155 out
->push_back(make_pair(
157 cookie
.second
.substr(pos
, end
- pos
)));
159 // Consume next space if present.
161 if (pos
!= cookie
.second
.size() && cookie
.second
[pos
] == ' ') {
165 // Sort crumbs and remove duplicates.
166 std::sort(out
->begin() + prior_size
, out
->end());
167 out
->erase(std::unique(out
->begin() + prior_size
, out
->end()),