Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(612)

Side by Side Diff: net/spdy/hpack/hpack_encoder.cc

Issue 2832973003: Split net/spdy into core and chromium subdirectories. (Closed)
Patch Set: Fix some more build rules. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « net/spdy/hpack/hpack_encoder.h ('k') | net/spdy/hpack/hpack_encoder_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "net/spdy/hpack/hpack_encoder.h"
6
7 #include <algorithm>
8 #include <limits>
9
10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h"
12 #include "net/spdy/hpack/hpack_constants.h"
13 #include "net/spdy/hpack/hpack_header_table.h"
14 #include "net/spdy/hpack/hpack_huffman_table.h"
15 #include "net/spdy/hpack/hpack_output_stream.h"
16 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h"
17
18 namespace net {
19
20 class HpackEncoder::RepresentationIterator {
21 public:
22 // |pseudo_headers| and |regular_headers| must outlive the iterator.
23 RepresentationIterator(const Representations& pseudo_headers,
24 const Representations& regular_headers)
25 : pseudo_begin_(pseudo_headers.begin()),
26 pseudo_end_(pseudo_headers.end()),
27 regular_begin_(regular_headers.begin()),
28 regular_end_(regular_headers.end()) {}
29
30 // |headers| must outlive the iterator.
31 explicit RepresentationIterator(const Representations& headers)
32 : pseudo_begin_(headers.begin()),
33 pseudo_end_(headers.end()),
34 regular_begin_(headers.end()),
35 regular_end_(headers.end()) {}
36
37 bool HasNext() {
38 return pseudo_begin_ != pseudo_end_ || regular_begin_ != regular_end_;
39 }
40
41 const Representation Next() {
42 if (pseudo_begin_ != pseudo_end_) {
43 return *pseudo_begin_++;
44 } else {
45 return *regular_begin_++;
46 }
47 }
48
49 private:
50 Representations::const_iterator pseudo_begin_;
51 Representations::const_iterator pseudo_end_;
52 Representations::const_iterator regular_begin_;
53 Representations::const_iterator regular_end_;
54 };
55
56 namespace {
57
58 // The default header listener.
59 void NoOpListener(SpdyStringPiece /*name*/, SpdyStringPiece /*value*/) {}
60
61 // The default HPACK indexing policy.
62 bool DefaultPolicy(SpdyStringPiece name, SpdyStringPiece /* value */) {
63 if (name.empty()) {
64 return false;
65 }
66 // :authority is always present and rarely changes, and has moderate
67 // length, therefore it makes a lot of sense to index (insert in the
68 // dynamic table).
69 if (name[0] == kPseudoHeaderPrefix) {
70 return name == ":authority";
71 }
72 return true;
73 }
74
75 } // namespace
76
77 HpackEncoder::HpackEncoder(const HpackHuffmanTable& table)
78 : output_stream_(),
79 huffman_table_(table),
80 min_table_size_setting_received_(std::numeric_limits<size_t>::max()),
81 listener_(NoOpListener),
82 should_index_(DefaultPolicy),
83 enable_compression_(true),
84 should_emit_table_size_(false) {}
85
86 HpackEncoder::~HpackEncoder() {}
87
88 void HpackEncoder::EncodeHeaderSet(const Representations& representations,
89 SpdyString* output) {
90 RepresentationIterator iter(representations);
91 EncodeRepresentations(&iter, output);
92 }
93
94 bool HpackEncoder::EncodeHeaderSet(const SpdyHeaderBlock& header_set,
95 SpdyString* output) {
96 // Separate header set into pseudo-headers and regular headers.
97 Representations pseudo_headers;
98 Representations regular_headers;
99 bool found_cookie = false;
100 for (const auto& header : header_set) {
101 if (!found_cookie && header.first == "cookie") {
102 // Note that there can only be one "cookie" header, because header_set is
103 // a map.
104 found_cookie = true;
105 CookieToCrumbs(header, &regular_headers);
106 } else if (!header.first.empty() &&
107 header.first[0] == kPseudoHeaderPrefix) {
108 DecomposeRepresentation(header, &pseudo_headers);
109 } else {
110 DecomposeRepresentation(header, &regular_headers);
111 }
112 }
113
114 {
115 RepresentationIterator iter(pseudo_headers, regular_headers);
116 EncodeRepresentations(&iter, output);
117 }
118 return true;
119 }
120
121 void HpackEncoder::ApplyHeaderTableSizeSetting(size_t size_setting) {
122 if (size_setting == header_table_.settings_size_bound()) {
123 return;
124 }
125 if (size_setting < header_table_.settings_size_bound()) {
126 min_table_size_setting_received_ =
127 std::min(size_setting, min_table_size_setting_received_);
128 }
129 header_table_.SetSettingsHeaderTableSize(size_setting);
130 should_emit_table_size_ = true;
131 }
132
133 size_t HpackEncoder::EstimateMemoryUsage() const {
134 // |huffman_table_| is a singleton. It's accounted for in spdy_session_pool.cc
135 return SpdyEstimateMemoryUsage(header_table_) +
136 SpdyEstimateMemoryUsage(output_stream_);
137 }
138
139 void HpackEncoder::EncodeRepresentations(RepresentationIterator* iter,
140 SpdyString* output) {
141 MaybeEmitTableSize();
142 while (iter->HasNext()) {
143 const auto header = iter->Next();
144 listener_(header.first, header.second);
145 if (enable_compression_) {
146 const HpackEntry* entry =
147 header_table_.GetByNameAndValue(header.first, header.second);
148 if (entry != nullptr) {
149 EmitIndex(entry);
150 } else if (should_index_(header.first, header.second)) {
151 EmitIndexedLiteral(header);
152 } else {
153 EmitNonIndexedLiteral(header);
154 }
155 } else {
156 EmitNonIndexedLiteral(header);
157 }
158 }
159
160 output_stream_.TakeString(output);
161 }
162
163 void HpackEncoder::EmitIndex(const HpackEntry* entry) {
164 DVLOG(2) << "Emitting index " << header_table_.IndexOf(entry);
165 output_stream_.AppendPrefix(kIndexedOpcode);
166 output_stream_.AppendUint32(header_table_.IndexOf(entry));
167 }
168
169 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
170 DVLOG(2) << "Emitting indexed literal: (" << representation.first << ", "
171 << representation.second << ")";
172 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
173 EmitLiteral(representation);
174 header_table_.TryAddEntry(representation.first, representation.second);
175 }
176
177 void HpackEncoder::EmitNonIndexedLiteral(const Representation& representation) {
178 DVLOG(2) << "Emitting nonindexed literal: (" << representation.first << ", "
179 << representation.second << ")";
180 output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
181 output_stream_.AppendUint32(0);
182 EmitString(representation.first);
183 EmitString(representation.second);
184 }
185
186 void HpackEncoder::EmitLiteral(const Representation& representation) {
187 const HpackEntry* name_entry = header_table_.GetByName(representation.first);
188 if (name_entry != nullptr) {
189 output_stream_.AppendUint32(header_table_.IndexOf(name_entry));
190 } else {
191 output_stream_.AppendUint32(0);
192 EmitString(representation.first);
193 }
194 EmitString(representation.second);
195 }
196
197 void HpackEncoder::EmitString(SpdyStringPiece str) {
198 size_t encoded_size =
199 enable_compression_ ? huffman_table_.EncodedSize(str) : str.size();
200 if (encoded_size < str.size()) {
201 DVLOG(2) << "Emitted Huffman-encoded string of length " << encoded_size;
202 output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
203 output_stream_.AppendUint32(encoded_size);
204 huffman_table_.EncodeString(str, &output_stream_);
205 } else {
206 DVLOG(2) << "Emitted literal string of length " << str.size();
207 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
208 output_stream_.AppendUint32(str.size());
209 output_stream_.AppendBytes(str);
210 }
211 }
212
213 void HpackEncoder::MaybeEmitTableSize() {
214 if (!should_emit_table_size_) {
215 return;
216 }
217 const size_t current_size = CurrentHeaderTableSizeSetting();
218 DVLOG(1) << "MaybeEmitTableSize current_size=" << current_size;
219 DVLOG(1) << "MaybeEmitTableSize min_table_size_setting_received_="
220 << min_table_size_setting_received_;
221 if (min_table_size_setting_received_ < current_size) {
222 output_stream_.AppendPrefix(kHeaderTableSizeUpdateOpcode);
223 output_stream_.AppendUint32(min_table_size_setting_received_);
224 }
225 output_stream_.AppendPrefix(kHeaderTableSizeUpdateOpcode);
226 output_stream_.AppendUint32(current_size);
227 min_table_size_setting_received_ = std::numeric_limits<size_t>::max();
228 should_emit_table_size_ = false;
229 }
230
231 // static
232 void HpackEncoder::CookieToCrumbs(const Representation& cookie,
233 Representations* out) {
234 // See Section 8.1.2.5. "Compressing the Cookie Header Field" in the HTTP/2
235 // specification at https://tools.ietf.org/html/draft-ietf-httpbis-http2-14.
236 // Cookie values are split into individually-encoded HPACK representations.
237 SpdyStringPiece cookie_value = cookie.second;
238 // Consume leading and trailing whitespace if present.
239 SpdyStringPiece::size_type first = cookie_value.find_first_not_of(" \t");
240 SpdyStringPiece::size_type last = cookie_value.find_last_not_of(" \t");
241 if (first == SpdyStringPiece::npos) {
242 cookie_value = SpdyStringPiece();
243 } else {
244 cookie_value = cookie_value.substr(first, (last - first) + 1);
245 }
246 for (size_t pos = 0;;) {
247 size_t end = cookie_value.find(";", pos);
248
249 if (end == SpdyStringPiece::npos) {
250 out->push_back(std::make_pair(cookie.first, cookie_value.substr(pos)));
251 break;
252 }
253 out->push_back(
254 std::make_pair(cookie.first, cookie_value.substr(pos, end - pos)));
255
256 // Consume next space if present.
257 pos = end + 1;
258 if (pos != cookie_value.size() && cookie_value[pos] == ' ') {
259 pos++;
260 }
261 }
262 }
263
264 // static
265 void HpackEncoder::DecomposeRepresentation(const Representation& header_field,
266 Representations* out) {
267 size_t pos = 0;
268 size_t end = 0;
269 while (end != SpdyStringPiece::npos) {
270 end = header_field.second.find('\0', pos);
271 out->push_back(std::make_pair(
272 header_field.first,
273 header_field.second.substr(
274 pos, end == SpdyStringPiece::npos ? end : end - pos)));
275 pos = end + 1;
276 }
277 }
278
279 // static
280 void HpackEncoder::GatherRepresentation(const Representation& header_field,
281 Representations* out) {
282 out->push_back(std::make_pair(header_field.first, header_field.second));
283 }
284
285 // Iteratively encodes a SpdyHeaderBlock.
286 class HpackEncoder::Encoderator : public ProgressiveEncoder {
287 public:
288 Encoderator(const SpdyHeaderBlock& header_set, HpackEncoder* encoder);
289
290 // Encoderator is neither copyable nor movable.
291 Encoderator(const Encoderator&) = delete;
292 Encoderator& operator=(const Encoderator&) = delete;
293
294 // Returns true iff more remains to encode.
295 bool HasNext() const override { return has_next_; }
296
297 // Encodes up to max_encoded_bytes of the current header block into the
298 // given output string.
299 void Next(size_t max_encoded_bytes, SpdyString* output) override;
300
301 private:
302 HpackEncoder* encoder_;
303 std::unique_ptr<RepresentationIterator> header_it_;
304 Representations pseudo_headers_;
305 Representations regular_headers_;
306 bool has_next_;
307 };
308
309 HpackEncoder::Encoderator::Encoderator(const SpdyHeaderBlock& header_set,
310 HpackEncoder* encoder)
311 : encoder_(encoder), has_next_(true) {
312 // Separate header set into pseudo-headers and regular headers.
313 const bool use_compression = encoder_->enable_compression_;
314 bool found_cookie = false;
315 for (const auto& header : header_set) {
316 if (!found_cookie && header.first == "cookie") {
317 // Note that there can only be one "cookie" header, because header_set
318 // is a map.
319 found_cookie = true;
320 CookieToCrumbs(header, &regular_headers_);
321 } else if (!header.first.empty() &&
322 header.first[0] == kPseudoHeaderPrefix) {
323 use_compression ? DecomposeRepresentation(header, &pseudo_headers_)
324 : GatherRepresentation(header, &pseudo_headers_);
325 } else {
326 use_compression ? DecomposeRepresentation(header, &regular_headers_)
327 : GatherRepresentation(header, &regular_headers_);
328 }
329 }
330 header_it_ = base::MakeUnique<RepresentationIterator>(pseudo_headers_,
331 regular_headers_);
332
333 encoder_->MaybeEmitTableSize();
334 }
335
336 void HpackEncoder::Encoderator::Next(size_t max_encoded_bytes,
337 SpdyString* output) {
338 SPDY_BUG_IF(!has_next_)
339 << "Encoderator::Next called with nothing left to encode.";
340 const bool use_compression = encoder_->enable_compression_;
341
342 // Encode up to max_encoded_bytes of headers.
343 while (header_it_->HasNext() &&
344 encoder_->output_stream_.size() <= max_encoded_bytes) {
345 const Representation header = header_it_->Next();
346 encoder_->listener_(header.first, header.second);
347 if (use_compression) {
348 const HpackEntry* entry = encoder_->header_table_.GetByNameAndValue(
349 header.first, header.second);
350 if (entry != nullptr) {
351 encoder_->EmitIndex(entry);
352 } else if (encoder_->should_index_(header.first, header.second)) {
353 encoder_->EmitIndexedLiteral(header);
354 } else {
355 encoder_->EmitNonIndexedLiteral(header);
356 }
357 } else {
358 encoder_->EmitNonIndexedLiteral(header);
359 }
360 }
361
362 has_next_ = encoder_->output_stream_.size() > max_encoded_bytes;
363 encoder_->output_stream_.BoundedTakeString(max_encoded_bytes, output);
364 }
365
366 std::unique_ptr<HpackEncoder::ProgressiveEncoder> HpackEncoder::EncodeHeaderSet(
367 const SpdyHeaderBlock& header_set) {
368 return base::MakeUnique<Encoderator>(header_set, this);
369 }
370
371 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack/hpack_encoder.h ('k') | net/spdy/hpack/hpack_encoder_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698