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

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

Issue 246073007: SPDY & HPACK: Land recent internal changes (through 65328503) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase on upstream change: Expanded FRAME_TOO_LARGE/FRAME_SIZE_ERROR comment. Created 6 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « net/spdy/hpack_encoder.h ('k') | net/spdy/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
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 "base/strings/string_util.h" 7 #include <algorithm>
8
9 #include "base/logging.h"
10 #include "net/spdy/hpack_header_table.h"
11 #include "net/spdy/hpack_huffman_table.h"
8 #include "net/spdy/hpack_output_stream.h" 12 #include "net/spdy/hpack_output_stream.h"
9 13
10 namespace net { 14 namespace net {
11 15
12 using base::StringPiece; 16 using base::StringPiece;
13 using std::string; 17 using std::string;
14 18
15 HpackEncoder::HpackEncoder() 19 namespace {
16 : max_string_literal_size_(kDefaultMaxStringLiteralSize) {} 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)
32 : output_stream_(),
33 allow_huffman_compression_(true),
34 huffman_table_(table) {}
17 35
18 HpackEncoder::~HpackEncoder() {} 36 HpackEncoder::~HpackEncoder() {}
19 37
20 bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set, 38 bool HpackEncoder::EncodeHeaderSet(const std::map<string, string>& header_set,
21 string* output) { 39 string* output) {
22 // TOOD(jgraettinger): Do more sophisticated encoding. 40 // Walk the set of entries to encode, which are not already implied by the
23 HpackOutputStream output_stream(max_string_literal_size_); 41 // header table's reference set. They must be explicitly emitted.
42 Representations explicit_set(DetermineEncodingDelta(header_set));
43 for (Representations::const_iterator it = explicit_set.begin();
44 it != explicit_set.end(); ++it) {
45 // Try to find an exact match. Note that dynamic entries are preferred
46 // by the header table index.
47 HpackEntry* entry = header_table_.GetByNameAndValue(it->first, it->second);
48 if (entry != NULL && !entry->IsStatic()) {
49 // Already in the dynamic table. Simply toggle on.
50 CHECK_EQ(kNoState, entry->state());
51 EmitDynamicIndex(entry);
52 continue;
53 }
54
55 // Walk the set of entries to be evicted by this insertion.
56 HpackHeaderTable::EntryTable::iterator evict_begin, evict_end, evict_it;
57 header_table_.EvictionSet(it->first, it->second, &evict_begin, &evict_end);
58
59 for (evict_it = evict_begin; evict_it != evict_end; ++evict_it) {
60 HpackEntry* evictee = &(*evict_it);
61
62 if (evict_it->state() == kReferencedImplicitOn) {
63 // Issue twice to explicitly emit.
64 EmitDynamicIndex(evictee);
65 EmitDynamicIndex(evictee);
66 } else if (evictee->state() == kReferencedExplicitOff) {
67 // Eviction saves us from having to explicitly toggle off.
68 evictee->set_state(kNoState);
69 } else if (evictee->state() == kReferencedThisEncoding) {
70 // Already emitted. No action required.
71 evictee->set_state(kNoState);
72 }
73 }
74 if (entry != NULL) {
75 EmitStaticIndex(entry);
76 } else {
77 EmitIndexedLiteral(*it);
78 }
79 }
80 // Walk the reference set, toggling off as needed and clearing encoding state.
81 for (HpackEntry::OrderedSet::const_iterator it =
82 header_table_.reference_set().begin();
83 it != header_table_.reference_set().end();) {
84 HpackEntry* entry = *(it++); // Step to prevent invalidation.
85 CHECK_NE(kNoState, entry->state());
86
87 if (entry->state() == kReferencedExplicitOff) {
88 // Explicitly toggle off.
89 EmitDynamicIndex(entry);
90 }
91 entry->set_state(kNoState);
92 }
93 output_stream_.TakeString(output);
94 return true;
95 }
96
97 bool HpackEncoder::EncodeHeaderSetWithoutCompression(
98 const std::map<string, string>& header_set,
99 string* output) {
100
101 allow_huffman_compression_ = false;
24 for (std::map<string, string>::const_iterator it = header_set.begin(); 102 for (std::map<string, string>::const_iterator it = header_set.begin();
25 it != header_set.end(); ++it) { 103 it != header_set.end(); ++it) {
26 // TODO(jgraettinger): HTTP/2 requires strict lowercasing of headers, 104 // Note that cookies are not crumbled in this case.
27 // and the permissiveness here isn't wanted. Back this out in upstream. 105 EmitNonIndexedLiteral(*it);
28 if (LowerCaseEqualsASCII(it->first, "cookie")) { 106 }
29 std::vector<StringPiece> crumbs; 107 allow_huffman_compression_ = true;
30 CookieToCrumbs(it->second, &crumbs); 108 output_stream_.TakeString(output);
31 for (size_t i = 0; i != crumbs.size(); i++) {
32 if (!output_stream.AppendLiteralHeaderNoIndexingWithName(
33 it->first, crumbs[i])) {
34 return false;
35 }
36 }
37 } else if (!output_stream.AppendLiteralHeaderNoIndexingWithName(
38 it->first, it->second)) {
39 return false;
40 }
41 }
42 output_stream.TakeString(output);
43 return true; 109 return true;
44 } 110 }
45 111
46 void HpackEncoder::CookieToCrumbs(StringPiece cookie, 112 void HpackEncoder::EmitDynamicIndex(HpackEntry* entry) {
47 std::vector<StringPiece>* out) { 113 DCHECK(!entry->IsStatic());
48 out->clear(); 114 output_stream_.AppendPrefix(kIndexedOpcode);
115 output_stream_.AppendUint32(entry->Index());
116
117 entry->set_state(kNoState);
118 if (header_table_.Toggle(entry)) {
119 // Was added to the reference set.
120 entry->set_state(kReferencedThisEncoding);
121 }
122 }
123
124 void HpackEncoder::EmitStaticIndex(HpackEntry* entry) {
125 DCHECK(entry->IsStatic());
126 output_stream_.AppendPrefix(kIndexedOpcode);
127 output_stream_.AppendUint32(entry->Index());
128
129 HpackEntry* new_entry = header_table_.TryAddEntry(entry->name(),
130 entry->value());
131 if (new_entry) {
132 // This is a static entry: no need to pin.
133 header_table_.Toggle(new_entry);
134 new_entry->set_state(kReferencedThisEncoding);
135 }
136 }
137
138 void HpackEncoder::EmitIndexedLiteral(const Representation& representation) {
139 output_stream_.AppendPrefix(kLiteralIncrementalIndexOpcode);
140 EmitLiteral(representation);
141
142 HpackEntry* new_entry = header_table_.TryAddEntry(representation.first,
143 representation.second);
144 if (new_entry) {
145 header_table_.Toggle(new_entry);
146 new_entry->set_state(kReferencedThisEncoding);
147 }
148 }
149
150 void HpackEncoder::EmitNonIndexedLiteral(
151 const Representation& representation) {
152 output_stream_.AppendPrefix(kLiteralNoIndexOpcode);
153 output_stream_.AppendUint32(0);
154 EmitString(representation.first);
155 EmitString(representation.second);
156 }
157
158 void HpackEncoder::EmitLiteral(const Representation& representation) {
159 const HpackEntry* name_entry = header_table_.GetByName(representation.first);
160 if (name_entry != NULL) {
161 output_stream_.AppendUint32(name_entry->Index());
162 } else {
163 output_stream_.AppendUint32(0);
164 EmitString(representation.first);
165 }
166 EmitString(representation.second);
167 }
168
169 void HpackEncoder::EmitString(StringPiece str) {
170 size_t encoded_size = (!allow_huffman_compression_ ? str.size()
171 : huffman_table_.EncodedSize(str));
172 if (encoded_size < str.size()) {
173 output_stream_.AppendPrefix(kStringLiteralHuffmanEncoded);
174 output_stream_.AppendUint32(encoded_size);
175 huffman_table_.EncodeString(str, &output_stream_);
176 } else {
177 output_stream_.AppendPrefix(kStringLiteralIdentityEncoded);
178 output_stream_.AppendUint32(str.size());
179 output_stream_.AppendBytes(str);
180 }
181 }
182
183 // static
184 HpackEncoder::Representations HpackEncoder::DetermineEncodingDelta(
185 const std::map<string, string>& header_set) {
186 // Flatten & crumble headers into an ordered list of representations.
187 Representations full_set;
188 for (std::map<string, string>::const_iterator it = header_set.begin();
189 it != header_set.end(); ++it) {
190 if (it->first == "cookie") {
191 // Crumble cookie, and sort the result crumbs.
192 size_t sort_offset = full_set.size();
193 CookieToCrumbs(*it, &full_set);
194 std::sort(full_set.begin() + sort_offset, full_set.end());
195 } else {
196 // Note std::map guarantees representations are ordered.
197 full_set.push_back(make_pair(
198 StringPiece(it->first), StringPiece(it->second)));
199 }
200 }
201 // Perform a linear merge of ordered representations with the (also ordered)
202 // reference set. Mark each referenced entry with current membership state,
203 // and gather representations which must be explicitly emitted.
204 Representations::const_iterator r_it = full_set.begin();
205 HpackEntry::OrderedSet::const_iterator s_it =
206 header_table_.reference_set().begin();
207
208 Representations explicit_set;
209 while (r_it != full_set.end() &&
210 s_it != header_table_.reference_set().end()) {
211 // Compare on name, then value.
212 int result = r_it->first.compare((*s_it)->name());
213 if (result == 0) {
214 result = r_it->second.compare((*s_it)->value());
215 }
216
217 if (result < 0) {
218 explicit_set.push_back(*r_it);
219 ++r_it;
220 } else if (result > 0) {
221 (*s_it)->set_state(kReferencedExplicitOff);
222 ++s_it;
223 } else {
224 (*s_it)->set_state(kReferencedImplicitOn);
225 ++s_it;
226 ++r_it;
227 }
228 }
229 while (r_it != full_set.end()) {
230 explicit_set.push_back(*r_it);
231 ++r_it;
232 }
233 while (s_it != header_table_.reference_set().end()) {
234 (*s_it)->set_state(kReferencedExplicitOff);
235 ++s_it;
236 }
237 return explicit_set;
238 }
239
240 // static
241 void HpackEncoder::CookieToCrumbs(const Representation& cookie,
242 Representations* out) {
243 // See Section 8.1.3.4 "Compressing the Cookie Header Field" in the HTTP/2
244 // specification at http://tools.ietf.org/html/draft-ietf-httpbis-http2-11
245 // Cookie values are split into individually-encoded HPACK representations.
49 for (size_t pos = 0;;) { 246 for (size_t pos = 0;;) {
50 size_t end = cookie.find(';', pos); 247 size_t end = cookie.second.find(";", pos);
51 248
52 if (end == StringPiece::npos) { 249 if (end == StringPiece::npos) {
53 out->push_back(cookie.substr(pos)); 250 out->push_back(make_pair(
251 cookie.first,
252 cookie.second.substr(pos)));
54 return; 253 return;
55 } 254 }
56 out->push_back(cookie.substr(pos, end - pos)); 255 out->push_back(make_pair(
256 cookie.first,
257 cookie.second.substr(pos, end - pos)));
57 258
58 // Consume next space if present. 259 // Consume next space if present.
59 pos = end + 1; 260 pos = end + 1;
60 if (pos != cookie.size() && cookie[pos] == ' ') { 261 if (pos != cookie.second.size() && cookie.second[pos] == ' ') {
61 pos++; 262 pos++;
62 } 263 }
63 } 264 }
64 } 265 }
65 266
66 } // namespace net 267 } // namespace net
OLDNEW
« no previous file with comments | « net/spdy/hpack_encoder.h ('k') | net/spdy/hpack_encoder_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698