/proc/self/cwd/source/extensions/filters/network/thrift_proxy/compact_protocol_impl.cc
Line | Count | Source (jump to first uncovered line) |
1 | | #include "source/extensions/filters/network/thrift_proxy/compact_protocol_impl.h" |
2 | | |
3 | | #include <limits> |
4 | | |
5 | | #include "envoy/common/exception.h" |
6 | | |
7 | | #include "source/common/common/assert.h" |
8 | | #include "source/common/common/fmt.h" |
9 | | #include "source/common/common/macros.h" |
10 | | #include "source/common/runtime/runtime_features.h" |
11 | | #include "source/extensions/filters/network/thrift_proxy/buffer_helper.h" |
12 | | |
13 | | namespace Envoy { |
14 | | namespace Extensions { |
15 | | namespace NetworkFilters { |
16 | | namespace ThriftProxy { |
17 | | |
18 | | const uint16_t CompactProtocolImpl::Magic = 0x8201; |
19 | | const uint16_t CompactProtocolImpl::MagicMask = 0xFF1F; |
20 | | |
21 | 0 | bool CompactProtocolImpl::readMessageBegin(Buffer::Instance& buffer, MessageMetadata& metadata) { |
22 | | // Minimum message length: |
23 | | // protocol, message type, and version: 2 bytes + |
24 | | // seq id (var int): 1 byte + |
25 | | // name length (var int): 1 byte + |
26 | | // name: 0 bytes |
27 | 0 | if (buffer.length() < 4) { |
28 | 0 | return false; |
29 | 0 | } |
30 | | |
31 | 0 | uint16_t version = buffer.peekBEInt<uint16_t>(); |
32 | 0 | if ((version & MagicMask) != Magic) { |
33 | 0 | throw EnvoyException(fmt::format("invalid compact protocol version 0x{:04x} != 0x{:04x}", |
34 | 0 | version & MagicMask, Magic)); |
35 | 0 | } |
36 | | |
37 | 0 | MessageType type = static_cast<MessageType>((version & ~MagicMask) >> 5); |
38 | 0 | if (type < MessageType::Call || type > MessageType::LastMessageType) { |
39 | 0 | throw EnvoyException( |
40 | 0 | fmt::format("invalid compact protocol message type {}", static_cast<int8_t>(type))); |
41 | 0 | } |
42 | | |
43 | 0 | int id_size; |
44 | 0 | int32_t id = BufferHelper::peekVarIntI32(buffer, 2, id_size); |
45 | 0 | if (id_size < 0) { |
46 | 0 | return false; |
47 | 0 | } |
48 | | |
49 | 0 | int name_len_size; |
50 | 0 | int32_t name_len = BufferHelper::peekVarIntI32(buffer, id_size + 2, name_len_size); |
51 | 0 | if (name_len_size < 0) { |
52 | 0 | return false; |
53 | 0 | } |
54 | | |
55 | 0 | if (name_len < 0) { |
56 | 0 | throw EnvoyException(absl::StrCat("negative compact protocol message name length ", name_len)); |
57 | 0 | } |
58 | | |
59 | 0 | if (buffer.length() < static_cast<uint64_t>(id_size + name_len_size + name_len + 2)) { |
60 | 0 | return false; |
61 | 0 | } |
62 | | |
63 | 0 | buffer.drain(id_size + name_len_size + 2); |
64 | |
|
65 | 0 | if (name_len > 0) { |
66 | 0 | metadata.setMethodName( |
67 | 0 | std::string(static_cast<const char*>(buffer.linearize(name_len)), name_len)); |
68 | 0 | buffer.drain(name_len); |
69 | 0 | } else { |
70 | 0 | metadata.setMethodName(""); |
71 | 0 | } |
72 | 0 | metadata.setMessageType(type); |
73 | 0 | metadata.setSequenceId(id); |
74 | |
|
75 | 0 | return true; |
76 | 0 | } |
77 | | |
78 | 0 | bool CompactProtocolImpl::readMessageEnd(Buffer::Instance& buffer) { |
79 | 0 | UNREFERENCED_PARAMETER(buffer); |
80 | 0 | return true; |
81 | 0 | } |
82 | | |
83 | 0 | bool CompactProtocolImpl::peekReplyPayload(Buffer::Instance& buffer, ReplyType& reply_type) { |
84 | | // compact protocol does not transmit struct names so go straight to peek for field begin |
85 | | // Minimum size: FieldType::Stop is encoded as 1 byte. |
86 | 0 | if (buffer.length() < 1) { |
87 | 0 | return false; |
88 | 0 | } |
89 | | |
90 | 0 | uint8_t delta_and_type = buffer.peekInt<int8_t>(); |
91 | 0 | if ((delta_and_type & 0x0f) == 0) { |
92 | | // Type is stop, no need to do further decoding |
93 | | // If the first field is stop then response is void success |
94 | 0 | reply_type = ReplyType::Success; |
95 | 0 | return true; |
96 | 0 | } |
97 | | |
98 | 0 | if ((delta_and_type >> 4) != 0) { |
99 | | // field id delta is non zero and so is an IDL exception (success field id is 0) |
100 | 0 | reply_type = ReplyType::Error; |
101 | 0 | return true; |
102 | 0 | } |
103 | | |
104 | 0 | int id_size = 0; |
105 | | // Field ID delta is zero: this is a long-form field header, followed by zig-zag field id. |
106 | 0 | if (buffer.length() < 2) { |
107 | 0 | return false; |
108 | 0 | } |
109 | | |
110 | 0 | int32_t id = BufferHelper::peekZigZagI32(buffer, 1, id_size); |
111 | 0 | if (id_size < 0) { |
112 | 0 | return false; |
113 | 0 | } |
114 | | |
115 | 0 | validateFieldId(id); |
116 | | // successful response struct in field id 0, error (IDL exception) in field id greater than 0 |
117 | 0 | reply_type = id == 0 ? ReplyType::Success : ReplyType::Error; |
118 | 0 | return true; |
119 | 0 | } |
120 | | |
121 | 0 | void CompactProtocolImpl::validateFieldId(int32_t id) { |
122 | 0 | if (id >= 0 && id <= std::numeric_limits<int16_t>::max()) { |
123 | 0 | return; |
124 | 0 | } |
125 | | |
126 | 0 | if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.thrift_allow_negative_field_ids") && |
127 | 0 | id < 0 && id >= std::numeric_limits<int16_t>::min()) { |
128 | 0 | return; |
129 | 0 | } |
130 | | |
131 | 0 | throw EnvoyException(absl::StrCat("invalid compact protocol field id ", id)); |
132 | 0 | } |
133 | | |
134 | 0 | bool CompactProtocolImpl::readStructBegin(Buffer::Instance& buffer, std::string& name) { |
135 | 0 | UNREFERENCED_PARAMETER(buffer); |
136 | 0 | name.clear(); // compact protocol does not transmit struct names |
137 | | |
138 | | // Field ids are encoded as deltas specific to the field's containing struct. Field ids are |
139 | | // tracked in a stack to handle nested structs. |
140 | 0 | last_field_id_stack_.push(last_field_id_); |
141 | 0 | last_field_id_ = 0; |
142 | |
|
143 | 0 | return true; |
144 | 0 | } |
145 | | |
146 | 0 | bool CompactProtocolImpl::readStructEnd(Buffer::Instance& buffer) { |
147 | 0 | UNREFERENCED_PARAMETER(buffer); |
148 | |
|
149 | 0 | if (last_field_id_stack_.empty()) { |
150 | 0 | throw EnvoyException("invalid check for compact protocol struct end"); |
151 | 0 | } |
152 | | |
153 | 0 | last_field_id_ = last_field_id_stack_.top(); |
154 | 0 | last_field_id_stack_.pop(); |
155 | |
|
156 | 0 | return true; |
157 | 0 | } |
158 | | |
159 | | bool CompactProtocolImpl::readFieldBegin(Buffer::Instance& buffer, std::string& name, |
160 | 0 | FieldType& field_type, int16_t& field_id) { |
161 | | // Minimum size: FieldType::Stop is encoded as 1 byte. |
162 | 0 | if (buffer.length() < 1) { |
163 | 0 | return false; |
164 | 0 | } |
165 | | |
166 | 0 | uint8_t delta_and_type = buffer.peekInt<int8_t>(); |
167 | 0 | if ((delta_and_type & 0x0f) == 0) { |
168 | | // Type is stop, no need to do further decoding. |
169 | 0 | name.clear(); |
170 | 0 | field_id = 0; |
171 | 0 | field_type = FieldType::Stop; |
172 | 0 | buffer.drain(1); |
173 | |
|
174 | 0 | return true; |
175 | 0 | } |
176 | | |
177 | 0 | int16_t compact_field_id; |
178 | 0 | CompactFieldType compact_field_type; |
179 | 0 | int id_size = 0; |
180 | 0 | if ((delta_and_type >> 4) == 0) { |
181 | | // Field ID delta is zero: this is a long-form field header, followed by zig-zag field id. |
182 | 0 | if (buffer.length() < 2) { |
183 | 0 | return false; |
184 | 0 | } |
185 | | |
186 | 0 | int32_t id = BufferHelper::peekZigZagI32(buffer, 1, id_size); |
187 | 0 | if (id_size < 0) { |
188 | 0 | return false; |
189 | 0 | } |
190 | | |
191 | 0 | validateFieldId(id); |
192 | 0 | compact_field_type = static_cast<CompactFieldType>(delta_and_type); |
193 | 0 | compact_field_id = static_cast<int16_t>(id); |
194 | 0 | } else { |
195 | | // Short form field header: 4 bits of field id delta, 4 bits of field type. |
196 | 0 | compact_field_type = static_cast<CompactFieldType>(delta_and_type & 0x0F); |
197 | 0 | compact_field_id = last_field_id_ + static_cast<int16_t>(delta_and_type >> 4); |
198 | 0 | } |
199 | | |
200 | 0 | field_type = convertCompactFieldType(compact_field_type); |
201 | | // For simple fields, boolean values are transmitted as a type with no further data. |
202 | 0 | if (field_type == FieldType::Bool) { |
203 | 0 | bool_value_ = compact_field_type == CompactFieldType::BoolTrue; |
204 | 0 | } |
205 | |
|
206 | 0 | name.clear(); // compact protocol does not transmit field names |
207 | 0 | field_id = compact_field_id; |
208 | 0 | last_field_id_ = compact_field_id; |
209 | |
|
210 | 0 | buffer.drain(id_size + 1); |
211 | |
|
212 | 0 | return true; |
213 | 0 | } |
214 | | |
215 | 0 | bool CompactProtocolImpl::readFieldEnd(Buffer::Instance& buffer) { |
216 | 0 | UNREFERENCED_PARAMETER(buffer); |
217 | 0 | bool_value_.reset(); |
218 | 0 | return true; |
219 | 0 | } |
220 | | |
221 | | bool CompactProtocolImpl::readMapBegin(Buffer::Instance& buffer, FieldType& key_type, |
222 | 0 | FieldType& value_type, uint32_t& size) { |
223 | 0 | int s_size; |
224 | 0 | int32_t s = BufferHelper::peekVarIntI32(buffer, 0, s_size); |
225 | 0 | if (s_size < 0) { |
226 | 0 | return false; |
227 | 0 | } |
228 | | |
229 | 0 | if (s < 0) { |
230 | 0 | throw EnvoyException(absl::StrCat("negative compact protocol map size ", s)); |
231 | 0 | } |
232 | | |
233 | 0 | if (s == 0) { |
234 | | // Empty map. Compact protocol provides no type information in this case. |
235 | 0 | key_type = value_type = FieldType::Stop; |
236 | 0 | size = 0; |
237 | 0 | buffer.drain(s_size); |
238 | 0 | return true; |
239 | 0 | } |
240 | | |
241 | 0 | if (buffer.length() < static_cast<uint64_t>(s_size + 1)) { |
242 | 0 | return false; |
243 | 0 | } |
244 | | |
245 | 0 | uint8_t types = buffer.peekInt<int8_t>(s_size); |
246 | 0 | FieldType ktype = convertCompactFieldType(static_cast<CompactFieldType>(types >> 4)); |
247 | 0 | FieldType vtype = convertCompactFieldType(static_cast<CompactFieldType>(types & 0xF)); |
248 | | |
249 | | // Drain the size and the types byte. |
250 | 0 | buffer.drain(s_size + 1); |
251 | |
|
252 | 0 | key_type = ktype; |
253 | 0 | value_type = vtype; |
254 | 0 | size = static_cast<uint32_t>(s); |
255 | |
|
256 | 0 | return true; |
257 | 0 | } |
258 | | |
259 | 0 | bool CompactProtocolImpl::readMapEnd(Buffer::Instance& buffer) { |
260 | 0 | UNREFERENCED_PARAMETER(buffer); |
261 | 0 | return true; |
262 | 0 | } |
263 | | |
264 | | bool CompactProtocolImpl::readListBegin(Buffer::Instance& buffer, FieldType& elem_type, |
265 | 0 | uint32_t& size) { |
266 | | // Minimum length: |
267 | | // size and type: 1 byte |
268 | 0 | if (buffer.length() < 1) { |
269 | 0 | return false; |
270 | 0 | } |
271 | | |
272 | 0 | uint32_t sz = 0; |
273 | 0 | int s_size = 0; |
274 | 0 | uint8_t size_and_type = buffer.peekInt<int8_t>(); |
275 | 0 | if ((size_and_type & 0xF0) != 0xF0) { |
276 | | // Short form list header: size and type byte. |
277 | 0 | sz = static_cast<uint32_t>(size_and_type >> 4); |
278 | 0 | } else { |
279 | | // Long form list header: type byte followed by var int size. |
280 | 0 | int32_t s = BufferHelper::peekVarIntI32(buffer, 1, s_size); |
281 | 0 | if (s_size < 0) { |
282 | 0 | return false; |
283 | 0 | } |
284 | | |
285 | 0 | if (s < 0) { |
286 | 0 | throw EnvoyException(fmt::format("negative compact protocol list/set size {}", s)); |
287 | 0 | } |
288 | | |
289 | 0 | sz = static_cast<uint32_t>(s); |
290 | 0 | } |
291 | | |
292 | 0 | elem_type = convertCompactFieldType(static_cast<CompactFieldType>(size_and_type & 0x0F)); |
293 | 0 | size = sz; |
294 | |
|
295 | 0 | buffer.drain(s_size + 1); |
296 | 0 | return true; |
297 | 0 | } |
298 | | |
299 | 0 | bool CompactProtocolImpl::readListEnd(Buffer::Instance& buffer) { |
300 | 0 | UNREFERENCED_PARAMETER(buffer); |
301 | 0 | return true; |
302 | 0 | } |
303 | | |
304 | | bool CompactProtocolImpl::readSetBegin(Buffer::Instance& buffer, FieldType& elem_type, |
305 | 0 | uint32_t& size) { |
306 | 0 | return readListBegin(buffer, elem_type, size); |
307 | 0 | } |
308 | | |
309 | 0 | bool CompactProtocolImpl::readSetEnd(Buffer::Instance& buffer) { return readListEnd(buffer); } |
310 | | |
311 | 0 | bool CompactProtocolImpl::readBool(Buffer::Instance& buffer, bool& value) { |
312 | | // Boolean struct fields have their value encoded in the field type. |
313 | 0 | if (bool_value_.has_value()) { |
314 | 0 | value = bool_value_.value(); |
315 | 0 | return true; |
316 | 0 | } |
317 | | |
318 | | // All other boolean values (list, set, or map elements) are encoded as single bytes. |
319 | 0 | if (buffer.length() < 1) { |
320 | 0 | return false; |
321 | 0 | } |
322 | | |
323 | 0 | value = buffer.drainInt<int8_t>() != 0; |
324 | 0 | return true; |
325 | 0 | } |
326 | | |
327 | 0 | bool CompactProtocolImpl::readByte(Buffer::Instance& buffer, uint8_t& value) { |
328 | 0 | if (buffer.length() < 1) { |
329 | 0 | return false; |
330 | 0 | } |
331 | 0 | value = buffer.drainInt<int8_t>(); |
332 | 0 | return true; |
333 | 0 | } |
334 | | |
335 | 0 | bool CompactProtocolImpl::readInt16(Buffer::Instance& buffer, int16_t& value) { |
336 | 0 | if (buffer.length() < 1) { |
337 | 0 | return false; |
338 | 0 | } |
339 | | |
340 | 0 | int size; |
341 | 0 | int32_t i = BufferHelper::peekZigZagI32(buffer, 0, size); |
342 | 0 | if (size < 0) { |
343 | 0 | return false; |
344 | 0 | } |
345 | | |
346 | 0 | if (i < std::numeric_limits<int16_t>::min() || i > std::numeric_limits<int16_t>::max()) { |
347 | 0 | throw EnvoyException(fmt::format("compact protocol i16 exceeds allowable range {}", i)); |
348 | 0 | } |
349 | | |
350 | 0 | buffer.drain(size); |
351 | 0 | value = static_cast<int16_t>(i); |
352 | 0 | return true; |
353 | 0 | } |
354 | | |
355 | 0 | bool CompactProtocolImpl::readInt32(Buffer::Instance& buffer, int32_t& value) { |
356 | 0 | if (buffer.length() < 1) { |
357 | 0 | return false; |
358 | 0 | } |
359 | | |
360 | 0 | int size; |
361 | 0 | int32_t i = BufferHelper::peekZigZagI32(buffer, 0, size); |
362 | 0 | if (size < 0) { |
363 | 0 | return false; |
364 | 0 | } |
365 | | |
366 | 0 | buffer.drain(size); |
367 | 0 | value = i; |
368 | 0 | return true; |
369 | 0 | } |
370 | | |
371 | 0 | bool CompactProtocolImpl::readInt64(Buffer::Instance& buffer, int64_t& value) { |
372 | 0 | if (buffer.length() < 1) { |
373 | 0 | return false; |
374 | 0 | } |
375 | | |
376 | 0 | int size; |
377 | 0 | int64_t i = BufferHelper::peekZigZagI64(buffer, 0, size); |
378 | 0 | if (size < 0) { |
379 | 0 | return false; |
380 | 0 | } |
381 | | |
382 | 0 | buffer.drain(size); |
383 | 0 | value = i; |
384 | 0 | return true; |
385 | 0 | } |
386 | | |
387 | 0 | bool CompactProtocolImpl::readDouble(Buffer::Instance& buffer, double& value) { |
388 | 0 | static_assert(sizeof(double) == sizeof(uint64_t), "sizeof(double) != size(uint64_t)"); |
389 | |
|
390 | 0 | if (buffer.length() < 8) { |
391 | 0 | return false; |
392 | 0 | } |
393 | | |
394 | 0 | value = BufferHelper::drainBEDouble(buffer); |
395 | 0 | return true; |
396 | 0 | } |
397 | | |
398 | 0 | bool CompactProtocolImpl::readString(Buffer::Instance& buffer, std::string& value) { |
399 | 0 | if (buffer.length() < 1) { |
400 | 0 | return false; |
401 | 0 | } |
402 | | |
403 | 0 | int len_size; |
404 | 0 | int32_t str_len = BufferHelper::peekVarIntI32(buffer, 0, len_size); |
405 | 0 | if (len_size < 0) { |
406 | 0 | return false; |
407 | 0 | } |
408 | | |
409 | 0 | if (str_len < 0) { |
410 | 0 | throw EnvoyException(fmt::format("negative compact protocol string/binary length {}", str_len)); |
411 | 0 | } |
412 | | |
413 | 0 | if (str_len == 0) { |
414 | 0 | buffer.drain(len_size); |
415 | 0 | value.clear(); |
416 | 0 | return true; |
417 | 0 | } |
418 | | |
419 | 0 | if (buffer.length() < static_cast<uint64_t>(str_len + len_size)) { |
420 | 0 | return false; |
421 | 0 | } |
422 | | |
423 | 0 | buffer.drain(len_size); |
424 | 0 | value.assign(static_cast<const char*>(buffer.linearize(str_len)), str_len); |
425 | 0 | buffer.drain(str_len); |
426 | 0 | return true; |
427 | 0 | } |
428 | | |
429 | 0 | bool CompactProtocolImpl::readBinary(Buffer::Instance& buffer, std::string& value) { |
430 | 0 | return readString(buffer, value); |
431 | 0 | } |
432 | | |
433 | | void CompactProtocolImpl::writeMessageBegin(Buffer::Instance& buffer, |
434 | 0 | const MessageMetadata& metadata) { |
435 | 0 | MessageType msg_type = metadata.messageType(); |
436 | |
|
437 | 0 | uint16_t ptv = (Magic & MagicMask) | (static_cast<uint16_t>(msg_type) << 5); |
438 | 0 | ASSERT((ptv & MagicMask) == Magic); |
439 | 0 | ASSERT((ptv & ~MagicMask) >> 5 == static_cast<uint16_t>(msg_type)); |
440 | | |
441 | 0 | buffer.writeBEInt<uint16_t>(ptv); |
442 | 0 | BufferHelper::writeVarIntI32(buffer, metadata.sequenceId()); |
443 | 0 | writeString(buffer, metadata.methodName()); |
444 | 0 | } |
445 | | |
446 | 0 | void CompactProtocolImpl::writeMessageEnd(Buffer::Instance& buffer) { |
447 | 0 | UNREFERENCED_PARAMETER(buffer); |
448 | 0 | } |
449 | | |
450 | 0 | void CompactProtocolImpl::writeStructBegin(Buffer::Instance& buffer, const std::string& name) { |
451 | 0 | UNREFERENCED_PARAMETER(buffer); |
452 | 0 | UNREFERENCED_PARAMETER(name); |
453 | | |
454 | | // Field ids are encoded as deltas specific to the field's containing struct. Field ids are |
455 | | // tracked in a stack to handle nested structs. |
456 | 0 | last_field_id_stack_.push(last_field_id_); |
457 | 0 | last_field_id_ = 0; |
458 | 0 | } |
459 | | |
460 | 0 | void CompactProtocolImpl::writeStructEnd(Buffer::Instance& buffer) { |
461 | 0 | UNREFERENCED_PARAMETER(buffer); |
462 | |
|
463 | 0 | if (last_field_id_stack_.empty()) { |
464 | 0 | throw EnvoyException("invalid write of compact protocol struct end"); |
465 | 0 | } |
466 | | |
467 | 0 | last_field_id_ = last_field_id_stack_.top(); |
468 | 0 | last_field_id_stack_.pop(); |
469 | 0 | } |
470 | | |
471 | | void CompactProtocolImpl::writeFieldBegin(Buffer::Instance& buffer, const std::string& name, |
472 | 0 | FieldType field_type, int16_t field_id) { |
473 | 0 | UNREFERENCED_PARAMETER(name); |
474 | |
|
475 | 0 | if (field_type == FieldType::Stop) { |
476 | 0 | buffer.writeByte(0); |
477 | 0 | return; |
478 | 0 | } |
479 | | |
480 | 0 | if (field_type == FieldType::Bool) { |
481 | 0 | bool_field_id_ = field_id; |
482 | 0 | return; |
483 | 0 | } |
484 | | |
485 | 0 | writeFieldBeginInternal(buffer, field_type, field_id, {}); |
486 | 0 | } |
487 | | |
488 | | void CompactProtocolImpl::writeFieldBeginInternal( |
489 | | Buffer::Instance& buffer, FieldType field_type, int16_t field_id, |
490 | 0 | absl::optional<CompactFieldType> field_type_override) { |
491 | 0 | CompactFieldType compact_field_type; |
492 | 0 | if (field_type_override.has_value()) { |
493 | 0 | compact_field_type = field_type_override.value(); |
494 | 0 | } else { |
495 | 0 | compact_field_type = convertFieldType(field_type); |
496 | 0 | } |
497 | |
|
498 | 0 | if (field_id > last_field_id_ && field_id - last_field_id_ <= 15) { |
499 | | // Encode short-form field header. |
500 | 0 | buffer.writeByte((static_cast<int8_t>(field_id - last_field_id_) << 4) | |
501 | 0 | static_cast<int8_t>(compact_field_type)); |
502 | 0 | } else { |
503 | 0 | buffer.writeByte(static_cast<int8_t>(compact_field_type)); |
504 | 0 | BufferHelper::writeZigZagI32(buffer, static_cast<int32_t>(field_id)); |
505 | 0 | } |
506 | |
|
507 | 0 | last_field_id_ = field_id; |
508 | 0 | } |
509 | | |
510 | 0 | void CompactProtocolImpl::writeFieldEnd(Buffer::Instance& buffer) { |
511 | 0 | UNREFERENCED_PARAMETER(buffer); |
512 | |
|
513 | 0 | bool_field_id_.reset(); |
514 | 0 | } |
515 | | |
516 | | void CompactProtocolImpl::writeMapBegin(Buffer::Instance& buffer, FieldType key_type, |
517 | 0 | FieldType value_type, uint32_t size) { |
518 | 0 | if (size > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { |
519 | 0 | throw EnvoyException(absl::StrCat("illegal compact protocol map size ", size)); |
520 | 0 | } |
521 | | |
522 | 0 | BufferHelper::writeVarIntI32(buffer, static_cast<int32_t>(size)); |
523 | 0 | if (size == 0) { |
524 | 0 | return; |
525 | 0 | } |
526 | | |
527 | 0 | CompactFieldType compact_key_type = convertFieldType(key_type); |
528 | 0 | CompactFieldType compact_value_type = convertFieldType(value_type); |
529 | 0 | buffer.writeByte((static_cast<int8_t>(compact_key_type) << 4) | |
530 | 0 | static_cast<int8_t>(compact_value_type)); |
531 | 0 | } |
532 | | |
533 | 0 | void CompactProtocolImpl::writeMapEnd(Buffer::Instance& buffer) { UNREFERENCED_PARAMETER(buffer); } |
534 | | |
535 | | void CompactProtocolImpl::writeListBegin(Buffer::Instance& buffer, FieldType elem_type, |
536 | 0 | uint32_t size) { |
537 | 0 | if (size > static_cast<uint32_t>(std::numeric_limits<int32_t>::max())) { |
538 | 0 | throw EnvoyException(fmt::format("illegal compact protocol list/set size {}", size)); |
539 | 0 | } |
540 | | |
541 | 0 | CompactFieldType compact_elem_type = convertFieldType(elem_type); |
542 | |
|
543 | 0 | if (size < 0xF) { |
544 | | // Short form list/set header |
545 | 0 | int8_t short_size = static_cast<int8_t>(size & 0xF); |
546 | 0 | buffer.writeByte((short_size << 4) | static_cast<int8_t>(compact_elem_type)); |
547 | 0 | } else { |
548 | 0 | buffer.writeByte(0xF0 | static_cast<int8_t>(compact_elem_type)); |
549 | 0 | BufferHelper::writeVarIntI32(buffer, static_cast<int32_t>(size)); |
550 | 0 | } |
551 | 0 | } |
552 | | |
553 | 0 | void CompactProtocolImpl::writeListEnd(Buffer::Instance& buffer) { UNREFERENCED_PARAMETER(buffer); } |
554 | | |
555 | | void CompactProtocolImpl::writeSetBegin(Buffer::Instance& buffer, FieldType elem_type, |
556 | 0 | uint32_t size) { |
557 | 0 | writeListBegin(buffer, elem_type, size); |
558 | 0 | } |
559 | | |
560 | 0 | void CompactProtocolImpl::writeSetEnd(Buffer::Instance& buffer) { UNREFERENCED_PARAMETER(buffer); } |
561 | | |
562 | 0 | void CompactProtocolImpl::writeBool(Buffer::Instance& buffer, bool value) { |
563 | 0 | if (bool_field_id_.has_value()) { |
564 | | // Boolean fields have their value encoded by type. |
565 | 0 | CompactFieldType bool_field_type = |
566 | 0 | value ? CompactFieldType::BoolTrue : CompactFieldType::BoolFalse; |
567 | 0 | writeFieldBeginInternal(buffer, FieldType::Bool, bool_field_id_.value(), {bool_field_type}); |
568 | 0 | return; |
569 | 0 | } |
570 | | |
571 | | // Map/Set/List booleans are encoded as bytes. |
572 | 0 | buffer.writeByte(value ? 1 : 0); |
573 | 0 | } |
574 | | |
575 | 0 | void CompactProtocolImpl::writeByte(Buffer::Instance& buffer, uint8_t value) { |
576 | 0 | buffer.writeByte(value); |
577 | 0 | } |
578 | | |
579 | 0 | void CompactProtocolImpl::writeInt16(Buffer::Instance& buffer, int16_t value) { |
580 | 0 | int32_t extended = static_cast<int32_t>(value); |
581 | 0 | BufferHelper::writeZigZagI32(buffer, extended); |
582 | 0 | } |
583 | | |
584 | 0 | void CompactProtocolImpl::writeInt32(Buffer::Instance& buffer, int32_t value) { |
585 | 0 | BufferHelper::writeZigZagI32(buffer, value); |
586 | 0 | } |
587 | | |
588 | 0 | void CompactProtocolImpl::writeInt64(Buffer::Instance& buffer, int64_t value) { |
589 | 0 | BufferHelper::writeZigZagI64(buffer, value); |
590 | 0 | } |
591 | | |
592 | 0 | void CompactProtocolImpl::writeDouble(Buffer::Instance& buffer, double value) { |
593 | 0 | BufferHelper::writeBEDouble(buffer, value); |
594 | 0 | } |
595 | | |
596 | 0 | void CompactProtocolImpl::writeString(Buffer::Instance& buffer, const std::string& value) { |
597 | 0 | BufferHelper::writeVarIntI32(buffer, value.length()); |
598 | 0 | buffer.add(value); |
599 | 0 | } |
600 | | |
601 | 0 | void CompactProtocolImpl::writeBinary(Buffer::Instance& buffer, const std::string& value) { |
602 | 0 | writeString(buffer, value); |
603 | 0 | } |
604 | | |
605 | 0 | FieldType CompactProtocolImpl::convertCompactFieldType(CompactFieldType compact_field_type) { |
606 | 0 | switch (compact_field_type) { |
607 | 0 | case CompactFieldType::BoolTrue: |
608 | 0 | return FieldType::Bool; |
609 | 0 | case CompactFieldType::BoolFalse: |
610 | 0 | return FieldType::Bool; |
611 | 0 | case CompactFieldType::Byte: |
612 | 0 | return FieldType::Byte; |
613 | 0 | case CompactFieldType::I16: |
614 | 0 | return FieldType::I16; |
615 | 0 | case CompactFieldType::I32: |
616 | 0 | return FieldType::I32; |
617 | 0 | case CompactFieldType::I64: |
618 | 0 | return FieldType::I64; |
619 | 0 | case CompactFieldType::Double: |
620 | 0 | return FieldType::Double; |
621 | 0 | case CompactFieldType::String: |
622 | 0 | return FieldType::String; |
623 | 0 | case CompactFieldType::List: |
624 | 0 | return FieldType::List; |
625 | 0 | case CompactFieldType::Set: |
626 | 0 | return FieldType::Set; |
627 | 0 | case CompactFieldType::Map: |
628 | 0 | return FieldType::Map; |
629 | 0 | case CompactFieldType::Struct: |
630 | 0 | return FieldType::Struct; |
631 | 0 | default: |
632 | 0 | throw EnvoyException(fmt::format("unknown compact protocol field type {}", |
633 | 0 | static_cast<int8_t>(compact_field_type))); |
634 | 0 | } |
635 | 0 | } |
636 | | |
637 | 0 | CompactProtocolImpl::CompactFieldType CompactProtocolImpl::convertFieldType(FieldType field_type) { |
638 | 0 | switch (field_type) { |
639 | 0 | case FieldType::Bool: |
640 | | // c.f. special handling in writeFieldBegin |
641 | 0 | return CompactFieldType::BoolTrue; |
642 | 0 | case FieldType::Byte: |
643 | 0 | return CompactFieldType::Byte; |
644 | 0 | case FieldType::I16: |
645 | 0 | return CompactFieldType::I16; |
646 | 0 | case FieldType::I32: |
647 | 0 | return CompactFieldType::I32; |
648 | 0 | case FieldType::I64: |
649 | 0 | return CompactFieldType::I64; |
650 | 0 | case FieldType::Double: |
651 | 0 | return CompactFieldType::Double; |
652 | 0 | case FieldType::String: |
653 | 0 | return CompactFieldType::String; |
654 | 0 | case FieldType::Struct: |
655 | 0 | return CompactFieldType::Struct; |
656 | 0 | case FieldType::Map: |
657 | 0 | return CompactFieldType::Map; |
658 | 0 | case FieldType::Set: |
659 | 0 | return CompactFieldType::Set; |
660 | 0 | case FieldType::List: |
661 | 0 | return CompactFieldType::List; |
662 | 0 | default: |
663 | 0 | throw EnvoyException( |
664 | 0 | fmt::format("unknown protocol field type {}", static_cast<int8_t>(field_type))); |
665 | 0 | } |
666 | 0 | } |
667 | | |
668 | | class CompactProtocolConfigFactory : public ProtocolFactoryBase<CompactProtocolImpl> { |
669 | | public: |
670 | 4 | CompactProtocolConfigFactory() : ProtocolFactoryBase(ProtocolNames::get().COMPACT) {} |
671 | | }; |
672 | | |
673 | | /** |
674 | | * Static registration for the binary protocol. @see RegisterFactory. |
675 | | */ |
676 | | REGISTER_FACTORY(CompactProtocolConfigFactory, NamedProtocolConfigFactory); |
677 | | |
678 | | } // namespace ThriftProxy |
679 | | } // namespace NetworkFilters |
680 | | } // namespace Extensions |
681 | | } // namespace Envoy |