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

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

Issue 448433002: Update HPACK implementation to draft-09 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Generate examples_07.hpack. Created 6 years, 4 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
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/spdy/hpack_encoder.h" 5 #include "net/spdy/hpack_encoder.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "net/spdy/hpack_header_table.h" 10 #include "net/spdy/hpack_header_table.h"
11 #include "net/spdy/hpack_huffman_table.h" 11 #include "net/spdy/hpack_huffman_table.h"
12 #include "net/spdy/hpack_output_stream.h" 12 #include "net/spdy/hpack_output_stream.h"
13 13
14 namespace net { 14 namespace net {
15 15
16 using base::StringPiece; 16 using base::StringPiece;
17 using std::string; 17 using std::string;
18 18
19 namespace {
20
21 const uint8 kNoState = 0;
22 // Set on a referenced HpackEntry which is part of the current header set.
23 const uint8 kReferencedImplicitOn = 1;
24 // Set on a referenced HpackEntry which is not part of the current header set.
25 const uint8 kReferencedExplicitOff = 2;
26 // Set on a entries added to the reference set during this encoding.
27 const uint8 kReferencedThisEncoding = 3;
28
29 } // namespace
30
31 HpackEncoder::HpackEncoder(const HpackHuffmanTable& table) 19 HpackEncoder::HpackEncoder(const HpackHuffmanTable& table)
32 : output_stream_(), 20 : output_stream_(),
33 allow_huffman_compression_(true), 21 allow_huffman_compression_(true),
34 huffman_table_(table), 22 huffman_table_(table),
35 char_counts_(NULL), 23 char_counts_(NULL),
36 total_char_counts_(NULL) {} 24 total_char_counts_(NULL) {}
37 25
38 HpackEncoder::~HpackEncoder() {} 26 HpackEncoder::~HpackEncoder() {}
39 27
40 bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set, 28 bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
41 string* output) { 29 string* output) {
42 // Walk the set of entries to encode, which are not already implied by the 30 // Flatten & crumble headers into an ordered list of representations.
43 // header table's reference set. They must be explicitly emitted. 31 Representations full_set;
44 Representations explicit_set(DetermineEncodingDelta(header_set)); 32 for (std::map<string, string>::const_iterator it = header_set.begin();
45 for (Representations::const_iterator it = explicit_set.begin(); 33 it != header_set.end(); ++it) {
46 it != explicit_set.end(); ++it) { 34 if (it->first == "cookie") {
47 // Try to find an exact match. Note that dynamic entries are preferred 35 // |CookieToCrumbs()| produces ordered crumbs.
48 // by the header table index. 36 CookieToCrumbs(*it, &full_set);
49 HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
50 if (entry != NULL && !entry->IsStatic()) {
51 // Already in the dynamic table. Simply toggle on.
52 CHECK_EQ(kNoState, entry->state());
53 EmitDynamicIndex(entry);
54 continue;
55 }
56
57 // Walk the set of entries to be evicted by this insertion.
58 HpackHeaderTable::EntryTable::iterator evict_begin, evict_end, evict_it;
59 header_table_.EvictionSet(it->first, it->second, &evict_begin, &evict_end);
60
61 for (evict_it = evict_begin; evict_it != evict_end; ++evict_it) {
62 HpackEntry* evictee = &(*evict_it);
63
64 if (evict_it->state() == kReferencedImplicitOn) {
65 // Issue twice to explicitly emit.
66 EmitDynamicIndex(evictee);
67 EmitDynamicIndex(evictee);
68 } else if (evictee->state() == kReferencedExplicitOff) {
69 // Eviction saves us from having to explicitly toggle off.
70 evictee->set_state(kNoState);
71 } else if (evictee->state() == kReferencedThisEncoding) {
72 // Already emitted. No action required.
73 evictee->set_state(kNoState);
74 }
75 }
76 if (entry != NULL) {
77 EmitStaticIndex(entry);
78 } else { 37 } else {
79 EmitIndexedLiteral(*it); 38 // Note std::map guarantees representations are ordered.
39 full_set.push_back(make_pair(
40 StringPiece(it->first), StringPiece(it->second)));
80 } 41 }
Johnny 2014/08/06 15:04:50 :pseudo-headers must appear first. I think this Ju
Bence 2014/08/08 13:57:56 I agree. I'll do this on a separate CL.
81 } 42 }
82 // Walk the reference set, toggling off as needed and clearing encoding state.
83 for (HpackHeaderTable::OrderedEntrySet::const_iterator it =
84 header_table_.reference_set().begin();
85 it != header_table_.reference_set().end();) {
86 HpackEntry* entry = *(it++); // Step to prevent invalidation.
87 CHECK_NE(kNoState, entry->state());
88 43
89 if (entry->state() == kReferencedExplicitOff) { 44 // Walk this ordered list and encode entries.
90 // Explicitly toggle off. 45 for (Representations::const_iterator it = full_set.begin();
91 EmitDynamicIndex(entry); 46 it != full_set.end(); ++it) {
92 } 47 HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
93 entry->set_state(kNoState); 48 if (entry != NULL)
49 EmitIndex(entry);
50 else
51 EmitIndexedLiteral(*it);
94 } 52 }
53
95 output_stream_.TakeString(output); 54 output_stream_.TakeString(output);
96 return true; 55 return true;
97 } 56 }
98 57
99 bool HpackEncoder::EncodeHeaderSetWithoutCompression( 58 bool HpackEncoder::EncodeHeaderSetWithoutCompression(
100 const std::map<string, string>& header_set, 59 const std::map<string, string>& header_set,
101 string* output) { 60 string* output) {
102 61
103 allow_huffman_compression_ = false; 62 allow_huffman_compression_ = false;
104 for (std::map<string, string>::const_iterator it = header_set.begin(); 63 for (std::map<string, string>::const_iterator it = header_set.begin();
105 it != header_set.end(); ++it) { 64 it != header_set.end(); ++it) {
106 // Note that cookies are not crumbled in this case. 65 // Note that cookies are not crumbled in this case.
107 EmitNonIndexedLiteral(*it); 66 EmitNonIndexedLiteral(*it);
108 } 67 }
109 allow_huffman_compression_ = true; 68 allow_huffman_compression_ = true;
110 output_stream_.TakeString(output); 69 output_stream_.TakeString(output);
111 return true; 70 return true;
112 } 71 }
113 72
114 void HpackEncoder::EmitDynamicIndex(HpackEntry* entry) { 73 void HpackEncoder::EmitIndex(HpackEntry* entry) {
115 DCHECK(!entry->IsStatic());
116 output_stream_.AppendPrefix(kIndexedOpcode); 74 output_stream_.AppendPrefix(kIndexedOpcode);
117 output_stream_.AppendUint32(header_table_.IndexOf(entry)); 75 output_stream_.AppendUint32(header_table_.IndexOf(entry));
118
119 entry->set_state(kNoState);
120 if (header_table_.Toggle(entry)) {
121 // Was added to the reference set.
122 entry->set_state(kReferencedThisEncoding);
123 }
124 }
125
126 void HpackEncoder::EmitStaticIndex(HpackEntry* entry) {
127 DCHECK(entry->IsStatic());
128 output_stream_.AppendPrefix(kIndexedOpcode);
129 output_stream_.AppendUint32(header_table_.IndexOf(entry));
130
131 HpackEntry* new_entry = header_table_.TryAddEntry(entry->name(),
132 entry->value());
133 if (new_entry) {
134 // This is a static entry: no need to pin.
135 header_table_.Toggle(new_entry);
136 new_entry->set_state(kReferencedThisEncoding);
137 }
138 } 76 }
139 77
140 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { 78 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
141 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode); 79 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
142 EmitLiteral(representation); 80 EmitLiteral(representation);
143 81 header_table_.TryAddEntry(representation.first, representation.second);
144 HpackEntry* new_entry = header_table_.TryAddEntry(representation.first,
145 representation.second);
146 if (new_entry) {
147 header_table_.Toggle(new_entry);
148 new_entry->set_state(kReferencedThisEncoding);
149 }
150 } 82 }
151 83
152 void HpackEncoder::EmitNonIndexedLiteral( 84 void HpackEncoder::EmitNonIndexedLiteral(
153 const Representation& representation) { 85 const Representation& representation) {
154 output_stream_.AppendPrefix(kLiteralNoIndexOpcode); 86 output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
155 output_stream_.AppendUint32(0); 87 output_stream_.AppendUint32(0);
156 EmitString(representation.first); 88 EmitString(representation.first);
157 EmitString(representation.second); 89 EmitString(representation.second);
158 } 90 }
159 91
(...skipping 16 matching lines...) Expand all
176 output_stream_.AppendUint32(encoded_size); 108 output_stream_.AppendUint32(encoded_size);
177 huffman_table_.EncodeString(str, &output_stream_); 109 huffman_table_.EncodeString(str, &output_stream_);
178 } else { 110 } else {
179 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded); 111 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
180 output_stream_.AppendUint32(str.size()); 112 output_stream_.AppendUint32(str.size());
181 output_stream_.AppendBytes(str); 113 output_stream_.AppendBytes(str);
182 } 114 }
183 UpdateCharacterCounts(str); 115 UpdateCharacterCounts(str);
184 } 116 }
185 117
186 // static
187 HpackEncoder::Representations HpackEncoder::DetermineEncodingDelta(
188 const std::map<string, string>& header_set) {
189 // Flatten & crumble headers into an ordered list of representations.
190 Representations full_set;
191 for (std::map<string, string>::const_iterator it = header_set.begin();
192 it != header_set.end(); ++it) {
193 if (it->first == "cookie") {
194 // |CookieToCrumbs()| produces ordered crumbs.
195 CookieToCrumbs(*it, &full_set);
196 } else {
197 // Note std::map guarantees representations are ordered.
198 full_set.push_back(make_pair(
199 StringPiece(it->first), StringPiece(it->second)));
200 }
201 }
202 // Perform a linear merge of ordered representations with the (also ordered)
203 // reference set. Mark each referenced entry with current membership state,
204 // and gather representations which must be explicitly emitted.
205 Representations::const_iterator r_it = full_set.begin();
206 HpackHeaderTable::OrderedEntrySet::const_iterator s_it =
207 header_table_.reference_set().begin();
208
209 Representations explicit_set;
210 while (r_it != full_set.end() &&
211 s_it != header_table_.reference_set().end()) {
212 // Compare on name, then value.
213 int result = r_it->first.compare((*s_it)->name());
214 if (result == 0) {
215 result = r_it->second.compare((*s_it)->value());
216 }
217
218 if (result < 0) {
219 explicit_set.push_back(*r_it);
220 ++r_it;
221 } else if (result > 0) {
222 (*s_it)->set_state(kReferencedExplicitOff);
223 ++s_it;
224 } else {
225 (*s_it)->set_state(kReferencedImplicitOn);
226 ++s_it;
227 ++r_it;
228 }
229 }
230 while (r_it != full_set.end()) {
231 explicit_set.push_back(*r_it);
232 ++r_it;
233 }
234 while (s_it != header_table_.reference_set().end()) {
235 (*s_it)->set_state(kReferencedExplicitOff);
236 ++s_it;
237 }
238 return explicit_set;
239 }
240
241 void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts, 118 void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts,
242 size_t* total_char_counts) { 119 size_t* total_char_counts) {
243 CHECK_LE(256u, char_counts->size()); 120 CHECK_LE(256u, char_counts->size());
244 char_counts_ = char_counts; 121 char_counts_ = char_counts;
245 total_char_counts_ = total_char_counts; 122 total_char_counts_ = total_char_counts;
246 } 123 }
247 124
248 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) { 125 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) {
249 if (char_counts_ == NULL || total_char_counts_ == NULL) { 126 if (char_counts_ == NULL || total_char_counts_ == NULL) {
250 return; 127 return;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
282 pos++; 159 pos++;
283 } 160 }
284 } 161 }
285 // Sort crumbs and remove duplicates. 162 // Sort crumbs and remove duplicates.
286 std::sort(out->begin() + prior_size, out->end()); 163 std::sort(out->begin() + prior_size, out->end());
287 out->erase(std::unique(out->begin() + prior_size, out->end()), 164 out->erase(std::unique(out->begin() + prior_size, out->end()),
288 out->end()); 165 out->end());
289 } 166 }
290 167
291 } // namespace net 168 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698