OLD | NEW |
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); |
| 37 } else { |
| 38 // Note std::map guarantees representations are ordered. |
| 39 full_set.push_back(make_pair( |
| 40 StringPiece(it->first), StringPiece(it->second))); |
| 41 } |
| 42 } |
| 43 |
| 44 // Walk this ordered list and encode entries. |
| 45 for (Representations::const_iterator it = full_set.begin(); |
| 46 it != full_set.end(); ++it) { |
49 HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second); | 47 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) { | 48 if (entry != NULL) { |
77 EmitStaticIndex(entry); | 49 EmitIndex(entry); |
78 } else { | 50 } else { |
| 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. |
79 EmitIndexedLiteral(*it); | 53 EmitIndexedLiteral(*it); |
80 } | 54 } |
81 } | 55 } |
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 | 56 |
89 if (entry->state() == kReferencedExplicitOff) { | |
90 // Explicitly toggle off. | |
91 EmitDynamicIndex(entry); | |
92 } | |
93 entry->set_state(kNoState); | |
94 } | |
95 output_stream_.TakeString(output); | 57 output_stream_.TakeString(output); |
96 return true; | 58 return true; |
97 } | 59 } |
98 | 60 |
99 bool HpackEncoder::EncodeHeaderSetWithoutCompression( | 61 bool HpackEncoder::EncodeHeaderSetWithoutCompression( |
100 const std::map<string, string>& header_set, | 62 const std::map<string, string>& header_set, |
101 string* output) { | 63 string* output) { |
102 | 64 |
103 allow_huffman_compression_ = false; | 65 allow_huffman_compression_ = false; |
104 for (std::map<string, string>::const_iterator it = header_set.begin(); | 66 for (std::map<string, string>::const_iterator it = header_set.begin(); |
105 it != header_set.end(); ++it) { | 67 it != header_set.end(); ++it) { |
106 // Note that cookies are not crumbled in this case. | 68 // Note that cookies are not crumbled in this case. |
107 EmitNonIndexedLiteral(*it); | 69 EmitNonIndexedLiteral(*it); |
108 } | 70 } |
109 allow_huffman_compression_ = true; | 71 allow_huffman_compression_ = true; |
110 output_stream_.TakeString(output); | 72 output_stream_.TakeString(output); |
111 return true; | 73 return true; |
112 } | 74 } |
113 | 75 |
114 void HpackEncoder::EmitDynamicIndex(HpackEntry* entry) { | 76 void HpackEncoder::EmitIndex(HpackEntry* entry) { |
115 DCHECK(!entry->IsStatic()); | |
116 output_stream_.AppendPrefix(kIndexedOpcode); | 77 output_stream_.AppendPrefix(kIndexedOpcode); |
117 output_stream_.AppendUint32(header_table_.IndexOf(entry)); | 78 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 } | 79 } |
139 | 80 |
140 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { | 81 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) { |
141 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode); | 82 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode); |
142 EmitLiteral(representation); | 83 EmitLiteral(representation); |
143 | 84 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 } | 85 } |
151 | 86 |
152 void HpackEncoder::EmitNonIndexedLiteral( | 87 void HpackEncoder::EmitNonIndexedLiteral( |
153 const Representation& representation) { | 88 const Representation& representation) { |
154 output_stream_.AppendPrefix(kLiteralNoIndexOpcode); | 89 output_stream_.AppendPrefix(kLiteralNoIndexOpcode); |
155 output_stream_.AppendUint32(0); | 90 output_stream_.AppendUint32(0); |
156 EmitString(representation.first); | 91 EmitString(representation.first); |
157 EmitString(representation.second); | 92 EmitString(representation.second); |
158 } | 93 } |
159 | 94 |
(...skipping 16 matching lines...) Expand all Loading... |
176 output_stream_.AppendUint32(encoded_size); | 111 output_stream_.AppendUint32(encoded_size); |
177 huffman_table_.EncodeString(str, &output_stream_); | 112 huffman_table_.EncodeString(str, &output_stream_); |
178 } else { | 113 } else { |
179 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded); | 114 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded); |
180 output_stream_.AppendUint32(str.size()); | 115 output_stream_.AppendUint32(str.size()); |
181 output_stream_.AppendBytes(str); | 116 output_stream_.AppendBytes(str); |
182 } | 117 } |
183 UpdateCharacterCounts(str); | 118 UpdateCharacterCounts(str); |
184 } | 119 } |
185 | 120 |
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, | 121 void HpackEncoder::SetCharCountsStorage(std::vector<size_t>* char_counts, |
242 size_t* total_char_counts) { | 122 size_t* total_char_counts) { |
243 CHECK_LE(256u, char_counts->size()); | 123 CHECK_LE(256u, char_counts->size()); |
244 char_counts_ = char_counts; | 124 char_counts_ = char_counts; |
245 total_char_counts_ = total_char_counts; | 125 total_char_counts_ = total_char_counts; |
246 } | 126 } |
247 | 127 |
248 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) { | 128 void HpackEncoder::UpdateCharacterCounts(base::StringPiece str) { |
249 if (char_counts_ == NULL || total_char_counts_ == NULL) { | 129 if (char_counts_ == NULL || total_char_counts_ == NULL) { |
250 return; | 130 return; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
282 pos++; | 162 pos++; |
283 } | 163 } |
284 } | 164 } |
285 // Sort crumbs and remove duplicates. | 165 // Sort crumbs and remove duplicates. |
286 std::sort(out->begin() + prior_size, out->end()); | 166 std::sort(out->begin() + prior_size, out->end()); |
287 out->erase(std::unique(out->begin() + prior_size, out->end()), | 167 out->erase(std::unique(out->begin() + prior_size, out->end()), |
288 out->end()); | 168 out->end()); |
289 } | 169 } |
290 | 170 |
291 } // namespace net | 171 } // namespace net |
OLD | NEW |