/src/brpc/src/brpc/details/hpack.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | |
18 | | |
19 | | #include "brpc/details/hpack.h" |
20 | | |
21 | | #include <limits> // std::numeric_limits |
22 | | #include <vector> |
23 | | #include "butil/containers/bounded_queue.h" // butil::BoundedQueue |
24 | | #include "butil/containers/flat_map.h" // butil::FlatMap |
25 | | #include "butil/containers/case_ignored_flat_map.h" // butil::FlatMap |
26 | | #include "brpc/details/hpack-static-table.h" // s_static_headers |
27 | | |
28 | | |
29 | | namespace brpc { |
30 | | |
31 | | // Options to initialize a IndexTable |
32 | | struct IndexTableOptions { |
33 | | size_t max_size; |
34 | | uint64_t start_index; |
35 | | const HeaderCstr* static_table; |
36 | | size_t static_table_size; |
37 | | bool need_indexes; |
38 | | IndexTableOptions() |
39 | 1.37k | : max_size(0) |
40 | 1.37k | , start_index(0) |
41 | | , static_table(NULL) |
42 | 1.37k | , static_table_size(0) |
43 | 1.37k | , need_indexes(false) |
44 | 1.37k | {} |
45 | | }; |
46 | | |
47 | | struct HeaderAndHashCode { |
48 | | size_t hash_code; |
49 | | const HPacker::Header* header; |
50 | | }; |
51 | | |
52 | | struct HeaderHasher { |
53 | 14 | size_t operator()(const HPacker::Header& h) const { |
54 | 14 | return butil::CaseIgnoredHasher()(h.name) |
55 | 14 | * 101 + butil::DefaultHasher<std::string>()(h.value); |
56 | 14 | } |
57 | 0 | size_t operator()(const HeaderAndHashCode& h) const { |
58 | 0 | return h.hash_code; |
59 | 0 | } |
60 | | }; |
61 | | |
62 | | struct HeaderEqualTo { |
63 | 1 | bool operator()(const HPacker::Header& h1, const HPacker::Header& h2) const { |
64 | 1 | return butil::CaseIgnoredEqual()(h1.name, h2.name) |
65 | 1 | && butil::DefaultEqualTo<std::string>()(h1.value, h2.value); |
66 | 1 | } |
67 | 0 | bool operator()(const HPacker::Header& h1, const HeaderAndHashCode& h2) const { |
68 | 0 | return operator()(h1, *h2.header); |
69 | 0 | } |
70 | | }; |
71 | | |
72 | | class BAIDU_CACHELINE_ALIGNMENT IndexTable { |
73 | | DISALLOW_COPY_AND_ASSIGN(IndexTable); |
74 | | typedef HPacker::Header Header; |
75 | | public: |
76 | | IndexTable() |
77 | 1.37k | : _start_index(0) |
78 | 1.37k | , _add_times(0) |
79 | 1.37k | , _size(0) |
80 | 1.37k | {} |
81 | 1.37k | ~IndexTable() {} |
82 | | int Init(const IndexTableOptions& options); |
83 | | |
84 | 273 | const Header* HeaderAt(int index) const { |
85 | 273 | if (BAIDU_UNLIKELY(index < _start_index)) { |
86 | 2 | return NULL; |
87 | 2 | } |
88 | 271 | return _header_queue.bottom(index - _start_index); |
89 | 273 | }; |
90 | | |
91 | 0 | int GetIndexOfHeader(const HeaderAndHashCode& h) { |
92 | 0 | DCHECK(_need_indexes); |
93 | 0 | const uint64_t* v = _header_index.seek(h); |
94 | 0 | if (!v) { |
95 | 0 | return 0; |
96 | 0 | } |
97 | 0 | DCHECK_LE(_add_times - *v, _header_queue.size()); |
98 | | // The latest added entry has the smallest index |
99 | 0 | return _start_index + (_add_times - *v) - 1; |
100 | 0 | } |
101 | | |
102 | 0 | int GetIndexOfName(const std::string& name) { |
103 | 0 | DCHECK(_need_indexes); |
104 | 0 | const uint64_t* v = _name_index.seek(name); |
105 | 0 | if (!v) { |
106 | 0 | return 0; |
107 | 0 | } |
108 | 0 | DCHECK_LE(_add_times - *v, _header_queue.size()); |
109 | | // The latest added entry has the smallest index |
110 | 0 | return _start_index + (_add_times - *v) - 1; |
111 | 0 | } |
112 | | |
113 | 406 | bool empty() const { return _size == 0; } |
114 | 1.64k | int start_index() const { return _start_index; } |
115 | 1.37k | int end_index() const { return start_index() + _header_queue.size(); } |
116 | | |
117 | 282 | static inline size_t HeaderSize(const Header& h) { |
118 | | // https://tools.ietf.org/html/rfc7541#section-4.1 |
119 | 282 | return h.name.size() + h.value.size() + 32; |
120 | 282 | } |
121 | | |
122 | 0 | void PopHeader() { |
123 | 0 | DCHECK(!empty()); |
124 | 0 | const Header* h = _header_queue.top(); |
125 | 0 | const size_t entry_size = HeaderSize(*h); |
126 | 0 | DCHECK_LE(entry_size, _size); |
127 | 0 | const uint64_t id = _add_times - _header_queue.size(); |
128 | 0 | if (_need_indexes) { |
129 | 0 | RemoveHeaderFromIndexes(*h, id); |
130 | 0 | } |
131 | 0 | _size -= entry_size; |
132 | 0 | _header_queue.pop(); |
133 | 0 | } |
134 | | |
135 | 0 | void RemoveHeaderFromIndexes(const Header& h, uint64_t expected_id) { |
136 | 0 | if (!h.value.empty()) { |
137 | 0 | const uint64_t* v = _header_index.seek(h); |
138 | 0 | DCHECK(v); |
139 | 0 | if (*v == expected_id) { |
140 | 0 | _header_index.erase(h); |
141 | 0 | } |
142 | 0 | } |
143 | 0 | const uint64_t* v = _name_index.seek(h.name); |
144 | 0 | DCHECK(v); |
145 | 0 | if (*v == expected_id) { |
146 | 0 | _name_index.erase(h.name); |
147 | 0 | } |
148 | 0 | } |
149 | | |
150 | 282 | void AddHeader(const Header& h) { |
151 | 282 | CHECK(!h.name.empty()); |
152 | 282 | const size_t entry_size = HeaderSize(h); |
153 | | |
154 | 282 | while (!empty() && (_size + entry_size) > _max_size) { |
155 | 0 | PopHeader(); |
156 | 0 | } |
157 | | |
158 | 282 | if (entry_size > _max_size) { |
159 | | // https://tools.ietf.org/html/rfc7541#section-4.1 |
160 | | // If this header is larger than the max size, clear the table only. |
161 | 124 | DCHECK(empty()); |
162 | 124 | return; |
163 | 124 | } |
164 | | |
165 | 158 | _size += entry_size; |
166 | 158 | CHECK(!_header_queue.full()); |
167 | 158 | _header_queue.push(h); |
168 | | |
169 | 158 | const int id = _add_times++; |
170 | 158 | if (_need_indexes) { |
171 | | // Overwrite existance value. |
172 | 61 | if (!h.value.empty()) { |
173 | 14 | _header_index[h] = id; |
174 | 14 | } |
175 | 61 | _name_index[h.name] = id; |
176 | 61 | } |
177 | 158 | } |
178 | | |
179 | 16.8k | void ResetMaxSize(size_t new_max_size) { |
180 | 16.8k | LOG(INFO) << this << ".size=" << _size << " new_max_size=" << new_max_size |
181 | 16.8k | << " max_size=" << _max_size; |
182 | 16.8k | if (new_max_size > _max_size) { |
183 | | //LOG(ERROR) << "Invalid new_max_size=" << new_max_size; |
184 | | //return -1; |
185 | 4.17k | _max_size = new_max_size; |
186 | 4.17k | return; |
187 | 4.17k | } |
188 | 12.6k | if (new_max_size < _max_size) { |
189 | 4.18k | _max_size = new_max_size; |
190 | 4.18k | while (_size > _max_size) { |
191 | 0 | PopHeader(); |
192 | 0 | } |
193 | 4.18k | } |
194 | 12.6k | return; |
195 | 16.8k | } |
196 | | |
197 | | void Print(std::ostream& os) const; |
198 | | |
199 | | private: |
200 | | |
201 | | int _start_index; |
202 | | bool _need_indexes; |
203 | | uint64_t _add_times; // Increase when adding a new entry. |
204 | | size_t _max_size; |
205 | | size_t _size; |
206 | | butil::BoundedQueue<Header> _header_queue; |
207 | | |
208 | | // ----------------------- Encoder only ---------------------------- |
209 | | // Indexes that map entry to the latest time it was added. |
210 | | // Note that duplicated entries are allowed in index table, which indicates |
211 | | // that the same header is possibly added/removed for multiple times, |
212 | | // requiring a costly multimap to index all the header entries. |
213 | | // Since the encoder just cares whether this header is in the index table |
214 | | // rather than which the index number is, only the latest entry of the same |
215 | | // header is indexed here, which is definitely the last one to be removed. |
216 | | butil::FlatMap<Header, uint64_t, HeaderHasher, HeaderEqualTo> _header_index; |
217 | | butil::CaseIgnoredFlatMap<uint64_t> _name_index; |
218 | | }; |
219 | | |
220 | 1.37k | int IndexTable::Init(const IndexTableOptions& options) { |
221 | 1.37k | size_t num_headers = 0; |
222 | 1.37k | if (options.static_table_size > 0) { |
223 | 1 | num_headers = options.static_table_size; |
224 | 1 | _max_size = UINT_MAX; |
225 | 1.37k | } else { |
226 | 1.37k | num_headers = options.max_size / (32 + 2); |
227 | | // ^ |
228 | | // name and value both have at least one byte in. |
229 | 1.37k | _max_size = options.max_size; |
230 | 1.37k | } |
231 | 1.37k | void *header_queue_storage = malloc(num_headers * sizeof(Header)); |
232 | 1.37k | if (!header_queue_storage) { |
233 | 0 | LOG(ERROR) << "Fail to malloc space for " << num_headers << " headers"; |
234 | 0 | return -1; |
235 | 0 | } |
236 | 1.37k | butil::BoundedQueue<Header> tmp( |
237 | 1.37k | header_queue_storage, num_headers * sizeof(Header), |
238 | 1.37k | butil::OWNS_STORAGE); |
239 | 1.37k | _header_queue.swap(tmp); |
240 | 1.37k | _start_index = options.start_index; |
241 | 1.37k | _need_indexes = options.need_indexes; |
242 | 1.37k | if (_need_indexes) { |
243 | 686 | if (_name_index.init(num_headers * 2) != 0) { |
244 | 0 | LOG(WARNING) << "Fail to init _name_index"; |
245 | 0 | } |
246 | 686 | if (_header_index.init(num_headers * 2) != 0) { |
247 | 0 | LOG(WARNING) << "Fail to init _name_index"; |
248 | 0 | } |
249 | 686 | } |
250 | 1.37k | if (options.static_table_size > 0) { |
251 | | // Add header in the reverse order |
252 | 62 | for (int i = options.static_table_size - 1; i >= 0; --i) { |
253 | 61 | Header h; |
254 | 61 | h.name = options.static_table[i].name; |
255 | 61 | h.value = options.static_table[i].value; |
256 | 61 | AddHeader(h); |
257 | 61 | } |
258 | 1 | } |
259 | 1.37k | return 0; |
260 | 1.37k | } |
261 | | |
262 | 0 | void IndexTable::Print(std::ostream& os) const { |
263 | 0 | os << "{start_index=" << _start_index |
264 | 0 | << " need_indexes=" << _need_indexes |
265 | 0 | << " add_times=" << _add_times |
266 | 0 | << " max_size=" << _max_size |
267 | 0 | << " size=" << _size |
268 | 0 | << " header_queue.size=" << _header_queue.size() |
269 | 0 | << " header_index.size=" << _header_index.size() |
270 | 0 | << " name_index.size=" << _name_index.size() |
271 | 0 | << '}'; |
272 | 0 | } |
273 | | |
274 | | struct HuffmanNode { |
275 | | uint16_t left_child; |
276 | | uint16_t right_child; |
277 | | int32_t value; |
278 | | }; |
279 | | |
280 | | class BAIDU_CACHELINE_ALIGNMENT HuffmanTree { |
281 | | DISALLOW_COPY_AND_ASSIGN(HuffmanTree); |
282 | | public: |
283 | | typedef uint16_t NodeId; |
284 | | enum ConstValue { |
285 | | NULL_NODE = 0, |
286 | | ROOT_NODE = 1, |
287 | | INVALID_VALUE = INT_MAX |
288 | | }; |
289 | | |
290 | 1 | HuffmanTree() { |
291 | | // Allocate memory for root |
292 | 1 | HuffmanNode root = { NULL_NODE, NULL_NODE, INVALID_VALUE }; |
293 | 1 | _node_memory.push_back(root); |
294 | 1 | } |
295 | | |
296 | 257 | void AddLeafNode(int32_t value, const HuffmanCode& code) { |
297 | 257 | NodeId cur = ROOT_NODE; |
298 | 4.94k | for (int i = code.bit_len; i > 0; i--) { |
299 | 4.68k | CHECK_EQ(node(cur).value, INVALID_VALUE) << "value=" << value << "cur=" << cur; |
300 | 4.68k | if (code.code & (1u << (i - 1))) { |
301 | 3.99k | if (node(cur).right_child == NULL_NODE) { |
302 | 256 | NodeId new_id = AllocNode(); |
303 | 256 | node(cur).right_child = new_id; |
304 | 256 | } |
305 | 3.99k | cur = node(cur).right_child; |
306 | 3.99k | } else { |
307 | 692 | if (node(cur).left_child == NULL_NODE) { |
308 | 256 | NodeId new_id = AllocNode(); |
309 | 256 | node(cur).left_child = new_id; |
310 | 256 | } |
311 | 692 | cur = node(cur).left_child; |
312 | 692 | } |
313 | 4.68k | } |
314 | 257 | CHECK_EQ(INVALID_VALUE, node(cur).value) << "value=" << value << " cur=" << cur; |
315 | 257 | CHECK_EQ(NULL_NODE, node(cur).left_child); |
316 | 257 | CHECK_EQ(NULL_NODE, node(cur).right_child); |
317 | 257 | node(cur).value = value; |
318 | 257 | } |
319 | | |
320 | 151k | const HuffmanNode* node(NodeId id) const { |
321 | 151k | if (id == 0u) { |
322 | 0 | return NULL; |
323 | 0 | } |
324 | 151k | if (id > _node_memory.size()) { |
325 | 0 | return NULL; |
326 | 0 | } |
327 | 151k | return &_node_memory[id - 1]; |
328 | 151k | } |
329 | | |
330 | | private: |
331 | | |
332 | 15.6k | HuffmanNode& node(NodeId id) { |
333 | 15.6k | return _node_memory[id - 1]; |
334 | 15.6k | } |
335 | | |
336 | 512 | NodeId AllocNode() { |
337 | 512 | const NodeId id = _node_memory.size() + 1; |
338 | 512 | HuffmanNode node = { NULL_NODE, NULL_NODE, INVALID_VALUE }; |
339 | 512 | _node_memory.push_back(node); |
340 | 512 | return id; |
341 | 512 | } |
342 | | std::vector<HuffmanNode> _node_memory; |
343 | | |
344 | | }; |
345 | | |
346 | | class HuffmanEncoder { |
347 | | DISALLOW_COPY_AND_ASSIGN(HuffmanEncoder); |
348 | | public: |
349 | | HuffmanEncoder(butil::IOBufAppender* out, const HuffmanCode* table) |
350 | 0 | : _out(out) |
351 | 0 | , _table(table) |
352 | 0 | , _partial_byte(0) |
353 | 0 | , _remain_bit(8) |
354 | 0 | , _out_bytes(0) |
355 | 0 | {} |
356 | | |
357 | 0 | void Encode(unsigned char byte) { |
358 | 0 | const HuffmanCode code = _table[byte]; |
359 | 0 | uint16_t bits_left = code.bit_len; |
360 | 0 | while (bits_left) { |
361 | 0 | const uint16_t adding_bits_len = std::min(_remain_bit, bits_left); |
362 | 0 | const uint8_t adding_bits = static_cast<uint8_t>( |
363 | 0 | (code.code & ((1u << bits_left) - 1)) // clear leading bits |
364 | 0 | >> (bits_left - adding_bits_len)); // align to LSB |
365 | 0 | _partial_byte |= adding_bits << (_remain_bit - adding_bits_len); |
366 | 0 | _remain_bit -= adding_bits_len; |
367 | 0 | bits_left -= adding_bits_len; |
368 | 0 | if (!_remain_bit) { |
369 | 0 | ++_out_bytes; |
370 | 0 | _out->push_back(_partial_byte); |
371 | 0 | _remain_bit = 8; |
372 | 0 | _partial_byte = 0; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | } |
376 | | |
377 | 0 | void EndStream() { |
378 | 0 | if (_remain_bit == 8u) { |
379 | 0 | return; |
380 | 0 | } |
381 | 0 | DCHECK_LT(_remain_bit, 8u); |
382 | | // Add padding `1's to lsb to make _out aligned |
383 | 0 | _partial_byte |= (1 << _remain_bit) - 1; |
384 | | // TODO: push_back is probably costly since it acquires tls everytime it |
385 | | // is invoked. |
386 | 0 | _out->push_back(_partial_byte); |
387 | 0 | _partial_byte = 0; |
388 | 0 | _remain_bit = 0; |
389 | 0 | _out = NULL; |
390 | 0 | ++_out_bytes; |
391 | 0 | } |
392 | | |
393 | 0 | uint32_t out_bytes() const { return _out_bytes; } |
394 | | |
395 | | private: |
396 | | butil::IOBufAppender* _out; |
397 | | const HuffmanCode* _table; |
398 | | uint8_t _partial_byte; |
399 | | uint16_t _remain_bit; |
400 | | uint32_t _out_bytes; |
401 | | }; |
402 | | |
403 | | class HuffmanDecoder { |
404 | | DISALLOW_COPY_AND_ASSIGN(HuffmanDecoder); |
405 | | public: |
406 | | HuffmanDecoder(std::string* out, const HuffmanTree* tree) |
407 | | // FIXME: resizing of out is costly |
408 | 249 | : _out(out) |
409 | 249 | , _tree(tree) |
410 | 249 | , _cur_node(tree->node(HuffmanTree::ROOT_NODE)) |
411 | 249 | , _cur_depth(0) // Depth of root node is 0 |
412 | 249 | , _padding(true) |
413 | 249 | {} |
414 | 15.9k | int Decode(uint8_t byte) { |
415 | 143k | for (int i = 7; i >= 0; --i) { |
416 | 127k | if (byte & (1u << i)) { |
417 | 24.4k | _cur_node = _tree->node(_cur_node->right_child); |
418 | 24.4k | if (BAIDU_UNLIKELY(!_cur_node)) { |
419 | 0 | LOG(ERROR) << "Decoder stream reaches NULL_NODE"; |
420 | 0 | return -1; |
421 | 0 | } |
422 | 24.4k | if (_cur_node->value != HuffmanTree::INVALID_VALUE) { |
423 | 3.71k | if (BAIDU_UNLIKELY(_cur_node->value == HPACK_HUFFMAN_EOS)) { |
424 | 7 | LOG(ERROR) << "Decoder stream reaches EOS"; |
425 | 7 | return -1; |
426 | 7 | } |
427 | 3.70k | _out->push_back(static_cast<uint8_t>(_cur_node->value)); |
428 | 3.70k | _cur_node = _tree->node(HuffmanTree::ROOT_NODE); |
429 | 3.70k | _cur_depth = 0; |
430 | 3.70k | _padding = true; |
431 | 3.70k | continue; |
432 | 3.71k | } |
433 | 20.7k | _padding &= 1; |
434 | 102k | } else { |
435 | 102k | _cur_node = _tree->node(_cur_node->left_child); |
436 | 102k | if (BAIDU_UNLIKELY(!_cur_node)) { |
437 | 0 | LOG(ERROR) << "Decoder stream reaches NULL_NODE"; |
438 | 0 | return -1; |
439 | 0 | } |
440 | 102k | if (_cur_node->value != HuffmanTree::INVALID_VALUE) { |
441 | 19.8k | if (BAIDU_UNLIKELY(_cur_node->value == HPACK_HUFFMAN_EOS)) { |
442 | 0 | LOG(ERROR) << "Decoder stream reaches EOS"; |
443 | 0 | return -1; |
444 | 0 | } |
445 | 19.8k | _out->push_back(static_cast<uint8_t>(_cur_node->value)); |
446 | 19.8k | _cur_node = _tree->node(HuffmanTree::ROOT_NODE); |
447 | 19.8k | _cur_depth = 0; |
448 | 19.8k | _padding = true; |
449 | 19.8k | continue; |
450 | 19.8k | } |
451 | 83.1k | _padding &= 0; |
452 | 83.1k | } |
453 | 103k | ++_cur_depth; |
454 | 103k | } |
455 | 15.9k | return 0; |
456 | 15.9k | } |
457 | 242 | int EndStream() { |
458 | 242 | if (_cur_depth == 0) { |
459 | 148 | return 0; |
460 | 148 | } |
461 | 94 | if (_cur_depth <= 7 && _padding) { |
462 | 46 | return 0; |
463 | 46 | } |
464 | | // Invalid stream, the padding is not corresponding to MSB of EOS |
465 | | // https://tools.ietf.org/html/rfc7541#section-5.2 |
466 | 48 | return -1; |
467 | 94 | } |
468 | | private: |
469 | | std::string* _out; |
470 | | const HuffmanTree* _tree; |
471 | | const HuffmanNode* _cur_node; |
472 | | uint16_t _cur_depth; |
473 | | bool _padding; |
474 | | }; |
475 | | |
476 | | // Primitive Type Representations |
477 | | |
478 | | // Encode variant intger and return the size |
479 | | inline void EncodeInteger(butil::IOBufAppender* out, uint8_t msb, |
480 | 0 | uint8_t prefix_size, uint32_t value) { |
481 | 0 | uint8_t max_prefix_value = (1 << prefix_size) - 1; |
482 | 0 | if (value < max_prefix_value) { |
483 | 0 | msb |= value; |
484 | 0 | out->push_back(msb); |
485 | 0 | return; |
486 | 0 | } |
487 | 0 | value -= max_prefix_value; |
488 | 0 | msb |= max_prefix_value; |
489 | 0 | out->push_back(msb); |
490 | 0 | for (; value >= 128; ) { |
491 | 0 | const uint8_t c = (value & 0x7f) | 0x80; |
492 | 0 | value >>= 7; |
493 | 0 | out->push_back(c); |
494 | 0 | } |
495 | 0 | out->push_back(static_cast<uint8_t>(value)); |
496 | 0 | } |
497 | | |
498 | | // Static variables |
499 | | static HuffmanTree* s_huffman_tree = NULL; |
500 | | static IndexTable* s_static_table = NULL; |
501 | | static pthread_once_t s_create_once = PTHREAD_ONCE_INIT; |
502 | | |
503 | 1 | static void CreateStaticTableOrDie() { |
504 | 1 | s_huffman_tree = new HuffmanTree; |
505 | 258 | for (size_t i = 0; i < ARRAY_SIZE(s_huffman_table); ++i) { |
506 | 257 | s_huffman_tree->AddLeafNode(i, s_huffman_table[i]); |
507 | 257 | } |
508 | 1 | IndexTableOptions options; |
509 | 1 | options.max_size = UINT_MAX; |
510 | 1 | options.static_table = s_static_headers; |
511 | 1 | options.static_table_size = ARRAY_SIZE(s_static_headers); |
512 | 1 | options.start_index = 1; |
513 | 1 | options.need_indexes = true; |
514 | 1 | s_static_table = new IndexTable; |
515 | 1 | if (s_static_table->Init(options) != 0) { |
516 | 0 | LOG(ERROR) << "Fail to init static table"; |
517 | 0 | exit(1); |
518 | 0 | } |
519 | 1 | } |
520 | | |
521 | 685 | static void CreateStaticTableOnceOrDie() { |
522 | 685 | if (pthread_once(&s_create_once, CreateStaticTableOrDie) != 0) { |
523 | 0 | PLOG(ERROR) << "Fail to pthread_once"; |
524 | 0 | exit(1); |
525 | 0 | } |
526 | 685 | } |
527 | | |
528 | | // Assume that no header would be larger than 10MB |
529 | | static const size_t MAX_HPACK_INTEGER = 10 * 1024 * 1024ul; |
530 | | |
531 | | inline ssize_t DecodeInteger(butil::IOBufBytesIterator& iter, |
532 | 18.1k | uint8_t prefix_size, uint32_t* value) { |
533 | 18.1k | if (iter == NULL) { |
534 | 0 | return 0; // No enough data |
535 | 0 | } |
536 | 18.1k | uint8_t first_byte = *iter; |
537 | 18.1k | uint64_t tmp = first_byte & ((1 << prefix_size) - 1); |
538 | 18.1k | ++iter; |
539 | 18.1k | if (tmp < ((1u << prefix_size) - 1)) { |
540 | 17.4k | *value = static_cast<uint32_t>(tmp); |
541 | 17.4k | return 1; |
542 | 17.4k | } |
543 | 635 | uint8_t cur_byte = 0; |
544 | 635 | int m = 0; |
545 | 635 | ssize_t in_bytes = 1; |
546 | 10.3k | do { |
547 | 10.3k | if (!iter) { |
548 | 18 | return 0; |
549 | 18 | } |
550 | 10.3k | cur_byte = *iter; |
551 | 10.3k | in_bytes++; |
552 | 10.3k | tmp += static_cast<uint64_t>(cur_byte & 0x7f) << m; |
553 | 10.3k | m += 7; |
554 | 10.3k | ++iter; |
555 | 10.3k | } while ((cur_byte & 0x80) && (tmp < MAX_HPACK_INTEGER)); |
556 | | |
557 | 617 | if (tmp >= MAX_HPACK_INTEGER) { |
558 | 27 | LOG(ERROR) << "Source stream is likely malformed"; |
559 | 27 | return -1; |
560 | 27 | } |
561 | | |
562 | 590 | *value = static_cast<uint32_t>(tmp); |
563 | | |
564 | 590 | return in_bytes; |
565 | 617 | } |
566 | | |
567 | | template <bool LOWERCASE> // use template to remove dead branches. |
568 | | inline void EncodeString(butil::IOBufAppender* out, const std::string& s, |
569 | 0 | bool huffman_encoding) { |
570 | 0 | if (!huffman_encoding) { |
571 | 0 | EncodeInteger(out, 0x00, 7, s.size()); |
572 | 0 | if (LOWERCASE) { |
573 | 0 | for (size_t i = 0; i < s.size(); ++i) { |
574 | 0 | out->push_back(butil::ascii_tolower(s[i])); |
575 | 0 | } |
576 | 0 | } else { |
577 | 0 | out->append(s); |
578 | 0 | } |
579 | 0 | return; |
580 | 0 | } |
581 | | // Calculate length of encoded string |
582 | 0 | uint32_t bit_len = 0; |
583 | 0 | if (LOWERCASE) { |
584 | 0 | for (size_t i = 0; i < s.size(); ++i) { |
585 | 0 | bit_len += s_huffman_table[(uint8_t)butil::ascii_tolower(s[i])].bit_len; |
586 | 0 | } |
587 | 0 | } else { |
588 | 0 | for (size_t i = 0; i < s.size(); ++i) { |
589 | 0 | bit_len += s_huffman_table[(uint8_t)s[i]].bit_len; |
590 | 0 | } |
591 | 0 | } |
592 | 0 | EncodeInteger(out, 0x80, 7, (bit_len >> 3) + !!(bit_len & 7)); |
593 | 0 | HuffmanEncoder e(out, s_huffman_table); |
594 | 0 | if (LOWERCASE) { |
595 | 0 | for (size_t i = 0; i < s.size(); ++i) { |
596 | 0 | e.Encode(butil::ascii_tolower(s[i])); |
597 | 0 | } |
598 | 0 | } else { |
599 | 0 | for (size_t i = 0; i < s.size(); ++i) { |
600 | 0 | e.Encode(s[i]); |
601 | 0 | } |
602 | 0 | } |
603 | 0 | e.EndStream(); |
604 | 0 | } Unexecuted instantiation: void brpc::EncodeString<true>(butil::IOBufAppender*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool) Unexecuted instantiation: void brpc::EncodeString<false>(butil::IOBufAppender*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, bool) |
605 | | |
606 | 697 | inline ssize_t DecodeString(butil::IOBufBytesIterator& iter, std::string* out) { |
607 | 697 | if (iter == NULL) { |
608 | 85 | return 0; |
609 | 85 | } |
610 | 612 | const bool huffman = *iter & 0x80; |
611 | 612 | uint32_t length = 0; |
612 | 612 | ssize_t in_bytes = DecodeInteger(iter, 7, &length); |
613 | 612 | if (in_bytes <= 0) { |
614 | 5 | return -1; |
615 | 5 | } |
616 | 607 | if (length > iter.bytes_left()) { |
617 | 47 | return 0; |
618 | 47 | } |
619 | 560 | in_bytes += length; |
620 | 560 | out->clear(); |
621 | 560 | if (!huffman) { |
622 | 311 | iter.copy_and_forward(out, length); |
623 | 311 | return in_bytes; |
624 | 311 | } |
625 | 249 | HuffmanDecoder d(out, s_huffman_tree); |
626 | 16.1k | for (; iter != NULL && length; ++iter, --length) { |
627 | 15.9k | if (d.Decode(*iter) != 0) { |
628 | 7 | return -1; |
629 | 7 | } |
630 | 15.9k | } |
631 | 242 | if (d.EndStream() != 0) { |
632 | 48 | return -1; |
633 | 48 | } |
634 | 194 | return in_bytes; |
635 | 242 | } |
636 | | |
637 | | HPacker::HPacker() |
638 | | : _encode_table(NULL) |
639 | 685 | , _decode_table(NULL) { |
640 | 685 | CreateStaticTableOnceOrDie(); |
641 | 685 | } |
642 | | |
643 | 685 | HPacker::~HPacker() { |
644 | 685 | if (_encode_table) { |
645 | 685 | delete _encode_table; |
646 | 685 | _encode_table = NULL; |
647 | 685 | } |
648 | 685 | if (_decode_table) { |
649 | 685 | delete _decode_table; |
650 | 685 | _decode_table = NULL; |
651 | 685 | } |
652 | 685 | } |
653 | | |
654 | 685 | int HPacker::Init(size_t max_table_size) { |
655 | 685 | CHECK(!_encode_table); |
656 | 685 | CHECK(!_decode_table); |
657 | 685 | IndexTableOptions encode_table_options; |
658 | 685 | encode_table_options.max_size = max_table_size; |
659 | 685 | encode_table_options.start_index = s_static_table->end_index(); |
660 | 685 | encode_table_options.need_indexes = true; |
661 | 685 | _encode_table = new IndexTable; |
662 | 685 | if (_encode_table->Init(encode_table_options) != 0) { |
663 | 0 | LOG(ERROR) << "Fail to init encode table"; |
664 | 0 | return -1; |
665 | 0 | } |
666 | 685 | IndexTableOptions decode_table_options; |
667 | 685 | decode_table_options.max_size = max_table_size; |
668 | 685 | decode_table_options.start_index = s_static_table->end_index(); |
669 | 685 | decode_table_options.need_indexes = false; |
670 | 685 | _decode_table = new IndexTable; |
671 | 685 | if (_decode_table->Init(decode_table_options) != 0) { |
672 | 0 | LOG(ERROR) << "Fail to init decode table"; |
673 | 0 | return -1; |
674 | 0 | } |
675 | 685 | return 0; |
676 | 685 | } |
677 | | |
678 | 0 | inline int HPacker::FindHeaderFromIndexTable(const Header& h) const { |
679 | | // saves a hash (which is a hotspot) for ones missing s_static_table |
680 | 0 | const HeaderAndHashCode hhc = { HeaderHasher()(h), &h }; |
681 | 0 | int index = s_static_table->GetIndexOfHeader(hhc); |
682 | 0 | if (index > 0) { |
683 | 0 | return index; |
684 | 0 | } |
685 | 0 | return _encode_table->GetIndexOfHeader(hhc); |
686 | 0 | } |
687 | | |
688 | 0 | inline int HPacker::FindNameFromIndexTable(const std::string& name) const { |
689 | 0 | int index = s_static_table->GetIndexOfName(name); |
690 | 0 | if (index > 0) { |
691 | 0 | return index; |
692 | 0 | } |
693 | 0 | return _encode_table->GetIndexOfName(name); |
694 | 0 | } |
695 | | |
696 | | void HPacker::Encode(butil::IOBufAppender* out, const Header& header, |
697 | 0 | const HPackOptions& options) { |
698 | 0 | if (options.index_policy != HPACK_NEVER_INDEX_HEADER) { |
699 | 0 | const int index = FindHeaderFromIndexTable(header); |
700 | 0 | if (index > 0) { |
701 | | // This header is already in the index table |
702 | 0 | return EncodeInteger(out, 0x80, 7, index); |
703 | 0 | } |
704 | 0 | } // The header can't be indexed or the header wasn't in the index table |
705 | | |
706 | 0 | const int name_index = FindNameFromIndexTable(header.name); |
707 | 0 | if (options.index_policy == HPACK_INDEX_HEADER) { |
708 | | // TODO: Add Options that indexes name independently |
709 | 0 | _encode_table->AddHeader(header); |
710 | 0 | } |
711 | 0 | switch (options.index_policy) { |
712 | 0 | case HPACK_INDEX_HEADER: |
713 | 0 | EncodeInteger(out, 0x40, 6, name_index); |
714 | 0 | break; |
715 | 0 | case HPACK_NOT_INDEX_HEADER: |
716 | 0 | EncodeInteger(out, 0x00, 4, name_index); |
717 | 0 | break; |
718 | 0 | case HPACK_NEVER_INDEX_HEADER: |
719 | 0 | EncodeInteger(out, 0x10, 4, name_index); |
720 | 0 | break; |
721 | 0 | } |
722 | 0 | if (name_index == 0) { |
723 | 0 | EncodeString<true>(out, header.name, options.encode_name); |
724 | 0 | } |
725 | 0 | EncodeString<false>(out, header.value, options.encode_value); |
726 | 0 | } |
727 | | |
728 | 273 | inline const HPacker::Header* HPacker::HeaderAt(int index) const { |
729 | 273 | return (index >= _decode_table->start_index()) |
730 | 273 | ? _decode_table->HeaderAt(index) : s_static_table->HeaderAt(index); |
731 | 273 | } |
732 | | |
733 | | inline ssize_t HPacker::DecodeWithKnownPrefix( |
734 | 516 | butil::IOBufBytesIterator& iter, Header* h, uint8_t prefix_size) const { |
735 | 516 | int index = 0; |
736 | 516 | ssize_t index_bytes = DecodeInteger(iter, prefix_size, (uint32_t*)&index); |
737 | 516 | ssize_t name_bytes = 0; |
738 | 516 | if (index_bytes <= 0) { |
739 | 12 | LOG(ERROR) << "Fail to decode index"; |
740 | 12 | return -1; |
741 | 12 | } |
742 | 504 | if (index != 0) { |
743 | 200 | const Header* indexed_header = HeaderAt(index); |
744 | 200 | if (indexed_header == NULL) { |
745 | 56 | LOG(ERROR) << "No header at index=" << index; |
746 | 56 | return -1; |
747 | 56 | } |
748 | 144 | h->name = indexed_header->name; |
749 | 304 | } else { |
750 | 304 | name_bytes = DecodeString(iter, &h->name); |
751 | 304 | if (name_bytes <= 0) { |
752 | 55 | LOG(ERROR) << "Fail to decode name"; |
753 | 55 | return -1; |
754 | 55 | } |
755 | 249 | tolower(&h->name); |
756 | 249 | } |
757 | 393 | ssize_t value_bytes = DecodeString(iter, &h->value); |
758 | 393 | if (value_bytes <= 0) { |
759 | 137 | LOG(ERROR) << "Fail to decode value"; |
760 | 137 | return -1; |
761 | 137 | } |
762 | 256 | return index_bytes + name_bytes + value_bytes; |
763 | 393 | } |
764 | | |
765 | 17.5k | ssize_t HPacker::Decode(butil::IOBufBytesIterator& iter, Header* h) { |
766 | 17.5k | if (iter == NULL) { |
767 | 51 | return 0; |
768 | 51 | } |
769 | 17.4k | const uint8_t first_byte = *iter; |
770 | | // Check the leading 4 bits to determin the entry type |
771 | 17.4k | switch (first_byte >> 4) { |
772 | 56 | case 15: |
773 | 57 | case 14: |
774 | 60 | case 13: |
775 | 62 | case 12: |
776 | 68 | case 11: |
777 | 71 | case 10: |
778 | 77 | case 9: |
779 | 87 | case 8: |
780 | | // (1xxx) Indexed Header Field Representation |
781 | | // https://tools.ietf.org/html/rfc7541#section-6.1 |
782 | 87 | { |
783 | 87 | int index = 0; |
784 | 87 | ssize_t index_bytes = DecodeInteger(iter, 7, (uint32_t*)&index); |
785 | 87 | if (index_bytes <= 0) { |
786 | 14 | return index_bytes; |
787 | 14 | } |
788 | 73 | const Header* indexed_header = HeaderAt(index); |
789 | 73 | if (indexed_header == NULL) { |
790 | 51 | LOG(ERROR) << "No header at index=" << index; |
791 | 51 | return -1; |
792 | 51 | } |
793 | 22 | *h = *indexed_header; |
794 | 22 | return index_bytes; |
795 | 73 | } |
796 | 0 | break; |
797 | 59 | case 7: |
798 | 95 | case 5: |
799 | 109 | case 6: |
800 | 357 | case 4: |
801 | | // (01xx) Literal Header Field with Incremental Indexing |
802 | | // https://tools.ietf.org/html/rfc7541#section-6.2.1 |
803 | 357 | { |
804 | 357 | const ssize_t bytes_consumed = DecodeWithKnownPrefix(iter, h, 6); |
805 | 357 | if (bytes_consumed <= 0) { |
806 | 136 | return -1; |
807 | 136 | } |
808 | 221 | _decode_table->AddHeader(*h); |
809 | 221 | return bytes_consumed; |
810 | 357 | } |
811 | 0 | break; |
812 | 11.0k | case 3: |
813 | 16.8k | case 2: |
814 | | // (001x) Dynamic Table Size Update |
815 | | // https://tools.ietf.org/html/rfc7541#section-6.3 |
816 | 16.8k | { |
817 | 16.8k | uint32_t max_size = 0; |
818 | 16.8k | ssize_t read_bytes = DecodeInteger(iter, 5, &max_size); |
819 | 16.8k | if (read_bytes <= 0) { |
820 | 14 | return read_bytes; |
821 | 14 | } |
822 | 16.8k | if (max_size > H2Settings::DEFAULT_HEADER_TABLE_SIZE) { |
823 | 17 | LOG(ERROR) << "Invalid max_size=" << max_size; |
824 | 17 | return -1; |
825 | 17 | } |
826 | 16.8k | _decode_table->ResetMaxSize(max_size); |
827 | 16.8k | return Decode(iter, h); |
828 | 16.8k | } |
829 | 61 | case 1: |
830 | | // (0001) Literal Header Field Never Indexed |
831 | | // https://tools.ietf.org/html/rfc7541#section-6.2.3 |
832 | 61 | return DecodeWithKnownPrefix(iter, h, 4); |
833 | | // TODO: Expose NeverIndex to the caller. |
834 | 98 | case 0: |
835 | | // (0000) Literal Header Field without Indexing |
836 | | // https://tools.ietf.org/html/rfc7541#section-6.2.1 |
837 | 98 | return DecodeWithKnownPrefix(iter, h, 4); |
838 | | // TODO: Expose NeverIndex to the caller. |
839 | 0 | default: |
840 | 0 | CHECK(false) << "Can't reach here"; |
841 | 0 | return -1; |
842 | 17.4k | } |
843 | 17.4k | } |
844 | | |
845 | 0 | void HPacker::Describe(std::ostream& os, const DescribeOptions& opt) const { |
846 | 0 | if (opt.verbose) { |
847 | 0 | os << '\n'; |
848 | 0 | } |
849 | 0 | const char sep = (opt.verbose ? '\n' : ' '); |
850 | 0 | os << "encode_table="; |
851 | 0 | if (_encode_table) { |
852 | 0 | _encode_table->Print(os); |
853 | 0 | } else { |
854 | 0 | os << "null"; |
855 | 0 | } |
856 | 0 | os << sep << "decode_table="; |
857 | 0 | if (_decode_table) { |
858 | 0 | _decode_table->Print(os); |
859 | 0 | } else { |
860 | 0 | os << "null"; |
861 | 0 | } |
862 | 0 | if (opt.verbose) { |
863 | 0 | os << '\n'; |
864 | 0 | } |
865 | 0 | } |
866 | | |
867 | 249 | void tolower(std::string* s) { |
868 | 249 | const char* d = s->c_str(); |
869 | 21.5k | for (size_t i = 0; i < s->size(); ++i) { |
870 | 21.3k | const char c = d[i]; |
871 | 21.3k | const char c2 = butil::ascii_tolower(c); |
872 | 21.3k | if (c2 != c) { |
873 | 1.21k | (*s)[i] = c2; |
874 | 1.21k | } |
875 | 21.3k | } |
876 | 249 | } |
877 | | |
878 | | } // namespace brpc |