QUIC - wait for disk cache to load server config if the server is among
[chromium-blink-merge.git] / net / spdy / hpack / hpack_encoder.cc
blobcb02285e3bb180cab42a4f68de27b7ec709adedd
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_encoder.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "net/spdy/hpack/hpack_header_table.h"
11 #include "net/spdy/hpack/hpack_huffman_table.h"
12 #include "net/spdy/hpack/hpack_output_stream.h"
14 namespace net {
16 using base::StringPiece;
17 using std::string;
19 HpackEncoder::HpackEncoder(const HpackHuffmanTable& table)
20 : output_stream_(),
21 allow_huffman_compression_(true),
22 huffman_table_(table),
23 char_counts_(NULL),
24 total_char_counts_(NULL) {}
26 HpackEncoder::~HpackEncoder() {}
28 bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set,
29 string* output) {
30 // Separate header set into pseudo-headers and regular headers.
31 Representations pseudo_headers;
32 Representations regular_headers;
33 bool found_cookie = false;
34 for (const auto& header : header_set) {
35 if (!found_cookie && header.first == "cookie") {
36 // Note that there can only be one "cookie" header, because header_set is
37 // a map.
38 found_cookie = true;
39 CookieToCrumbs(header, &regular_headers);
40 } else if (header.first[0] == kPseudoHeaderPrefix) {
41 DecomposeRepresentation(header, &pseudo_headers);
42 } else {
43 DecomposeRepresentation(header, &regular_headers);
47 // Encode pseudo-headers.
48 bool found_authority = false;
49 for (const auto& header : pseudo_headers) {
50 const HpackEntry* entry =
51 header_table_.GetByNameAndValue(header.first, header.second);
52 if (entry != NULL) {
53 EmitIndex(entry);
54 } else {
55 // :authority is always present and rarely changes, and has moderate
56 // length, therefore it makes a lot of sense to index (insert in the
57 // header table).
58 if (!found_authority && header.first == ":authority") {
59 // Note that there can only be one ":authority" header, because
60 // |header_set| is a map.
61 found_authority = true;
62 EmitIndexedLiteral(header);
63 } else {
64 // Most common pseudo-header fields are represented in the static table,
65 // while uncommon ones are small, so do not index them.
66 EmitNonIndexedLiteral(header);
71 // Encode regular headers.
72 for (const auto& header : regular_headers) {
73 const HpackEntry* entry =
74 header_table_.GetByNameAndValue(header.first, header.second);
75 if (entry != NULL) {
76 EmitIndex(entry);
77 } else {
78 EmitIndexedLiteral(header);
82 output_stream_.TakeString(output);
83 return true;
86 bool HpackEncoder::EncodeHeaderSetWithoutCompression(
87 const SpdyHeaderBlock& header_set,
88 string* output) {
89 allow_huffman_compression_ = false;
90 for (const auto& header : header_set) {
91 // Note that cookies are not crumbled in this case.
92 EmitNonIndexedLiteral(header);
94 allow_huffman_compression_ = true;
95 output_stream_.TakeString(output);
96 return true;
99 void HpackEncoder::EmitIndex(const HpackEntry* entry) {
100 output_stream_.AppendPrefix(kIndexedOpcode);
101 output_stream_.AppendUint32(header_table_.IndexOf(entry));
104 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
105 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
106 EmitLiteral(representation);
107 header_table_.TryAddEntry(representation.first, representation.second);
110 void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) {
111 output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
112 output_stream_.AppendUint32(0);
113 EmitString(representation.first);
114 EmitString(representation.second);
117 void HpackEncoder::EmitLiteral(const Representation& representation) {
118 const HpackEntry* name_entry = header_table_.GetByName(representation.first);
119 if (name_entry != NULL) {
120 output_stream_.AppendUint32(header_table_.IndexOf(name_entry));
121 } else {
122 output_stream_.AppendUint32(0);
123 EmitString(representation.first);
125 EmitString(representation.second);
128 void HpackEncoder::EmitString(StringPiece str) {
129 size_t encoded_size =
130 (!allow_huffman_compression_ ? str.size()
131 : huffman_table_.EncodedSize(str));
132 if (encoded_size < str.size()) {
133 output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
134 output_stream_.AppendUint32(encoded_size);
135 huffman_table_.EncodeString(str, &output_stream_);
136 } else {
137 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
138 output_stream_.AppendUint32(str.size());
139 output_stream_.AppendBytes(str);
141 UpdateCharacterCounts(str);
144 void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts,
145 size_t* total_char_counts) {
146 CHECK_LE(256u, char_counts->size());
147 char_counts_ = char_counts;
148 total_char_counts_ = total_char_counts;
151 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) {
152 if (char_counts_ == NULL || total_char_counts_ == NULL) {
153 return;
155 for (StringPiece::const_iterator it = str.begin(); it != str.end(); ++it) {
156 ++(*char_counts_)[static_cast<uint8>(*it)];
158 (*total_char_counts_) += str.size();
161 // static
162 void HpackEncoder::CookieToCrumbs(const Representation& cookie,
163 Representations* out) {
164 // See Section 8.1.2.5. "Compressing the Cookie Header Field" in the HTTP/2
165 // specification at https://tools.ietf.org/html/draft-ietf-httpbis-http2-14.
166 // Cookie values are split into individually-encoded HPACK representations.
167 StringPiece cookie_value = cookie.second;
168 // Consume leading and trailing whitespace if present.
169 StringPiece::size_type first = cookie_value.find_first_not_of(" \t");
170 StringPiece::size_type last = cookie_value.find_last_not_of(" \t");
171 if (first == StringPiece::npos) {
172 cookie_value.clear();
173 } else {
174 cookie_value = cookie_value.substr(first, (last - first) + 1);
176 for (size_t pos = 0;;) {
177 size_t end = cookie_value.find(";", pos);
179 if (end == StringPiece::npos) {
180 out->push_back(std::make_pair(cookie.first, cookie_value.substr(pos)));
181 break;
183 out->push_back(
184 std::make_pair(cookie.first, cookie_value.substr(pos, end - pos)));
186 // Consume next space if present.
187 pos = end + 1;
188 if (pos != cookie_value.size() && cookie_value[pos] == ' ') {
189 pos++;
194 // static
195 void HpackEncoder::DecomposeRepresentation(const Representation& header_field,
196 Representations* out) {
197 size_t pos = 0;
198 size_t end = 0;
199 while (end != StringPiece::npos) {
200 end = header_field.second.find('\0', pos);
201 out->push_back(std::make_pair(header_field.first,
202 header_field.second.substr(pos, end - pos)));
203 pos = end + 1;
207 } // namespace net