OLD | NEW |
| (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_decoder.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "base/logging.h" | |
10 #include "net/spdy/hpack/hpack_constants.h" | |
11 #include "net/spdy/hpack/hpack_entry.h" | |
12 #include "net/spdy/platform/api/spdy_estimate_memory_usage.h" | |
13 #include "net/spdy/spdy_flags.h" | |
14 | |
15 namespace net { | |
16 | |
17 HpackDecoder::HpackDecoder() | |
18 : handler_(nullptr), | |
19 total_header_bytes_(0), | |
20 total_parsed_bytes_(0), | |
21 header_block_started_(false), | |
22 size_updates_seen_(0), | |
23 size_updates_allowed_(true), | |
24 incremental_decode_(false) {} | |
25 | |
26 HpackDecoder::~HpackDecoder() {} | |
27 | |
28 void HpackDecoder::ApplyHeaderTableSizeSetting(size_t size_setting) { | |
29 header_table_.SetSettingsHeaderTableSize(size_setting); | |
30 } | |
31 | |
32 void HpackDecoder::HandleControlFrameHeadersStart( | |
33 SpdyHeadersHandlerInterface* handler) { | |
34 handler_ = handler; | |
35 total_header_bytes_ = 0; | |
36 } | |
37 | |
38 bool HpackDecoder::HandleControlFrameHeadersData(const char* headers_data, | |
39 size_t headers_data_length) { | |
40 if (!header_block_started_) { | |
41 decoded_block_.clear(); | |
42 header_block_started_ = true; | |
43 size_updates_allowed_ = true; | |
44 size_updates_seen_ = 0; | |
45 if (handler_ != nullptr) { | |
46 handler_->OnHeaderBlockStart(); | |
47 } | |
48 } | |
49 size_t new_size = headers_block_buffer_.size() + headers_data_length; | |
50 if (max_decode_buffer_size_bytes_ > 0 && | |
51 new_size > max_decode_buffer_size_bytes_) { | |
52 DVLOG(1) << "max_decode_buffer_size_bytes_ < new_size: " | |
53 << max_decode_buffer_size_bytes_ << " < " << new_size; | |
54 return false; | |
55 } | |
56 headers_block_buffer_.insert(headers_block_buffer_.end(), headers_data, | |
57 headers_data + headers_data_length); | |
58 | |
59 // Parse as many whole HPACK entries in the buffer as possible, | |
60 // and then remove the parsed data from the buffer. | |
61 HpackInputStream input_stream(headers_block_buffer_); | |
62 while (input_stream.HasMoreData()) { | |
63 if (!DecodeNextOpcodeWrapper(&input_stream)) { | |
64 if (input_stream.NeedMoreData()) { | |
65 break; | |
66 } | |
67 DVLOG(1) << "!DecodeNextOpcodeWrapper"; | |
68 return false; | |
69 } | |
70 } | |
71 uint32_t parsed_bytes = input_stream.ParsedBytes(); | |
72 DCHECK_GE(headers_block_buffer_.size(), parsed_bytes); | |
73 headers_block_buffer_.erase(0, parsed_bytes); | |
74 total_parsed_bytes_ += parsed_bytes; | |
75 | |
76 return true; | |
77 } | |
78 | |
79 bool HpackDecoder::HandleControlFrameHeadersComplete(size_t* compressed_len) { | |
80 if (compressed_len != nullptr) { | |
81 *compressed_len = total_parsed_bytes_; | |
82 } | |
83 | |
84 // Data in headers_block_buffer_ should have been parsed by | |
85 // HandleControlFrameHeadersData and removed. | |
86 if (headers_block_buffer_.size() > 0) { | |
87 DVLOG(1) << "headers_block_buffer_.size() should be zero, but is " | |
88 << headers_block_buffer_.size(); | |
89 return false; | |
90 } | |
91 | |
92 if (handler_ != nullptr) { | |
93 if (FLAGS_chromium_http2_flag_log_compressed_size) { | |
94 handler_->OnHeaderBlockEnd(total_header_bytes_, total_parsed_bytes_); | |
95 } else { | |
96 handler_->OnHeaderBlockEnd(total_header_bytes_); | |
97 } | |
98 } | |
99 headers_block_buffer_.clear(); | |
100 total_parsed_bytes_ = 0; | |
101 header_block_started_ = false; | |
102 handler_ = nullptr; | |
103 return true; | |
104 } | |
105 | |
106 const SpdyHeaderBlock& HpackDecoder::decoded_block() const { | |
107 return decoded_block_; | |
108 } | |
109 | |
110 void HpackDecoder::SetHeaderTableDebugVisitor( | |
111 std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) { | |
112 header_table_.set_debug_visitor(std::move(visitor)); | |
113 } | |
114 | |
115 void HpackDecoder::set_max_decode_buffer_size_bytes( | |
116 size_t max_decode_buffer_size_bytes) { | |
117 max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes; | |
118 } | |
119 | |
120 size_t HpackDecoder::EstimateMemoryUsage() const { | |
121 return SpdyEstimateMemoryUsage(header_table_) + | |
122 SpdyEstimateMemoryUsage(headers_block_buffer_) + | |
123 SpdyEstimateMemoryUsage(decoded_block_) + | |
124 SpdyEstimateMemoryUsage(key_buffer_) + | |
125 SpdyEstimateMemoryUsage(value_buffer_); | |
126 } | |
127 | |
128 bool HpackDecoder::HandleHeaderRepresentation(SpdyStringPiece name, | |
129 SpdyStringPiece value) { | |
130 size_updates_allowed_ = false; | |
131 total_header_bytes_ += name.size() + value.size(); | |
132 | |
133 if (handler_ == nullptr) { | |
134 decoded_block_.AppendValueOrAddHeader(name, value); | |
135 } else { | |
136 DCHECK(decoded_block_.empty()); | |
137 handler_->OnHeader(name, value); | |
138 } | |
139 return true; | |
140 } | |
141 | |
142 bool HpackDecoder::DecodeNextOpcodeWrapper(HpackInputStream* input_stream) { | |
143 if (DecodeNextOpcode(input_stream)) { | |
144 // Decoding next opcode succeeds. Mark total bytes parsed successfully. | |
145 input_stream->MarkCurrentPosition(); | |
146 return true; | |
147 } | |
148 return false; | |
149 } | |
150 | |
151 bool HpackDecoder::DecodeNextOpcode(HpackInputStream* input_stream) { | |
152 // Implements 7.1: Indexed Header Field Representation. | |
153 if (input_stream->MatchPrefixAndConsume(kIndexedOpcode)) { | |
154 return DecodeNextIndexedHeader(input_stream); | |
155 } | |
156 // Implements 7.2.1: Literal Header Field with Incremental Indexing. | |
157 if (input_stream->MatchPrefixAndConsume(kLiteralIncrementalIndexOpcode)) { | |
158 return DecodeNextLiteralHeader(input_stream, true); | |
159 } | |
160 // Implements 7.2.2: Literal Header Field without Indexing. | |
161 if (input_stream->MatchPrefixAndConsume(kLiteralNoIndexOpcode)) { | |
162 return DecodeNextLiteralHeader(input_stream, false); | |
163 } | |
164 // Implements 7.2.3: Literal Header Field never Indexed. | |
165 // TODO(jgraettinger): Preserve the never-indexed bit. | |
166 if (input_stream->MatchPrefixAndConsume(kLiteralNeverIndexOpcode)) { | |
167 return DecodeNextLiteralHeader(input_stream, false); | |
168 } | |
169 // Implements 7.3: Header Table Size Update. | |
170 if (input_stream->MatchPrefixAndConsume(kHeaderTableSizeUpdateOpcode)) { | |
171 // Header table size updates cannot appear mid-block. | |
172 return DecodeNextHeaderTableSizeUpdate(input_stream); | |
173 } | |
174 // Unrecognized opcode. | |
175 return false; | |
176 } | |
177 | |
178 bool HpackDecoder::DecodeNextHeaderTableSizeUpdate( | |
179 HpackInputStream* input_stream) { | |
180 uint32_t size = 0; | |
181 if (!input_stream->DecodeNextUint32(&size)) { | |
182 return false; | |
183 } | |
184 if (!size_updates_allowed_) { | |
185 DVLOG(1) << "Size updates not allowed after header entries."; | |
186 return false; | |
187 } | |
188 ++size_updates_seen_; | |
189 if (size_updates_seen_ > 2) { | |
190 DVLOG(1) << "Too many size updates at the start of the block."; | |
191 return false; | |
192 } | |
193 if (size > header_table_.settings_size_bound()) { | |
194 DVLOG(1) << "Size (" << size << ") exceeds SETTINGS limit (" | |
195 << header_table_.settings_size_bound() << ")"; | |
196 return false; | |
197 } | |
198 header_table_.SetMaxSize(size); | |
199 return true; | |
200 } | |
201 | |
202 bool HpackDecoder::DecodeNextIndexedHeader(HpackInputStream* input_stream) { | |
203 uint32_t index = 0; | |
204 if (!input_stream->DecodeNextUint32(&index)) { | |
205 return false; | |
206 } | |
207 | |
208 const HpackEntry* entry = header_table_.GetByIndex(index); | |
209 if (entry == NULL) { | |
210 DVLOG(1) << "Index " << index << " is not valid."; | |
211 return false; | |
212 } | |
213 | |
214 return HandleHeaderRepresentation(entry->name(), entry->value()); | |
215 } | |
216 | |
217 bool HpackDecoder::DecodeNextLiteralHeader(HpackInputStream* input_stream, | |
218 bool should_index) { | |
219 SpdyStringPiece name; | |
220 if (!DecodeNextName(input_stream, &name)) { | |
221 return false; | |
222 } | |
223 | |
224 SpdyStringPiece value; | |
225 if (!DecodeNextStringLiteral(input_stream, false, &value)) { | |
226 return false; | |
227 } | |
228 | |
229 if (!HandleHeaderRepresentation(name, value)) { | |
230 return false; | |
231 } | |
232 | |
233 if (!should_index) { | |
234 return true; | |
235 } | |
236 | |
237 ignore_result(header_table_.TryAddEntry(name, value)); | |
238 return true; | |
239 } | |
240 | |
241 bool HpackDecoder::DecodeNextName(HpackInputStream* input_stream, | |
242 SpdyStringPiece* next_name) { | |
243 uint32_t index_or_zero = 0; | |
244 if (!input_stream->DecodeNextUint32(&index_or_zero)) { | |
245 DVLOG(1) << "Failed to decode the next uint."; | |
246 return false; | |
247 } | |
248 | |
249 if (index_or_zero == 0) { | |
250 return DecodeNextStringLiteral(input_stream, true, next_name); | |
251 } | |
252 | |
253 const HpackEntry* entry = header_table_.GetByIndex(index_or_zero); | |
254 if (entry == NULL) { | |
255 DVLOG(1) << "index " << index_or_zero << " is not valid."; | |
256 return false; | |
257 } | |
258 if (entry->IsStatic()) { | |
259 *next_name = entry->name(); | |
260 } else { | |
261 // |entry| could be evicted as part of this insertion. Preemptively copy. | |
262 key_buffer_.assign(entry->name().data(), entry->name().size()); | |
263 *next_name = key_buffer_; | |
264 } | |
265 return true; | |
266 } | |
267 | |
268 bool HpackDecoder::DecodeNextStringLiteral(HpackInputStream* input_stream, | |
269 bool is_key, | |
270 SpdyStringPiece* output) { | |
271 if (input_stream->MatchPrefixAndConsume(kStringLiteralHuffmanEncoded)) { | |
272 SpdyString* buffer = is_key ? &key_buffer_ : &value_buffer_; | |
273 bool result = input_stream->DecodeNextHuffmanString(buffer); | |
274 *output = SpdyStringPiece(*buffer); | |
275 return result; | |
276 } else if (input_stream->MatchPrefixAndConsume( | |
277 kStringLiteralIdentityEncoded)) { | |
278 return input_stream->DecodeNextIdentityString(output); | |
279 } else { | |
280 DVLOG(1) << "String literal is neither Huffman nor identity encoded!"; | |
281 return false; | |
282 } | |
283 } | |
284 | |
285 } // namespace net | |
OLD | NEW |