Chromium Code Reviews| 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); |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |