/proc/self/cwd/source/common/json/json_streamer.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <memory> |
4 | | #include <stack> |
5 | | #include <string> |
6 | | #include <type_traits> |
7 | | #include <variant> |
8 | | |
9 | | #include "envoy/buffer/buffer.h" |
10 | | |
11 | | #include "source/common/buffer/buffer_util.h" |
12 | | #include "source/common/json/constants.h" |
13 | | #include "source/common/json/json_sanitizer.h" |
14 | | |
15 | | #include "absl/strings/string_view.h" |
16 | | #include "absl/types/variant.h" |
17 | | |
18 | | namespace Envoy { |
19 | | namespace Json { |
20 | | |
21 | | // To ensure the streamer is being used correctly, we use assertions to enforce |
22 | | // that only the topmost map/array in the stack is being written to. To make |
23 | | // this easier to do from the Level classes, we provider Streamer::topLevel() as |
24 | | // a member function, but this is only needed when compiled for debug. |
25 | | // |
26 | | // We only compile Streamer::topLevel in debug to avoid having it be a coverage |
27 | | // gap. However, assertions fail to compile in release mode if they reference |
28 | | // non-existent functions or member variables, so we only compile the assertions |
29 | | // in debug mode. |
30 | | #ifdef NDEBUG |
31 | | #define ASSERT_THIS_IS_TOP_LEVEL \ |
32 | | do { \ |
33 | | } while (0) |
34 | | #define ASSERT_LEVELS_EMPTY \ |
35 | | do { \ |
36 | | } while (0) |
37 | | #else |
38 | 0 | #define ASSERT_THIS_IS_TOP_LEVEL ASSERT(this->streamer_.topLevel() == this) |
39 | 0 | #define ASSERT_LEVELS_EMPTY ASSERT(this->levels_.empty()) |
40 | | #endif |
41 | | |
42 | | // Buffer wrapper that implements the necessary abstraction for the template |
43 | | // StreamerBase. |
44 | | // This could be used to stream JSON output of StreamerBase to a Buffer. |
45 | | class BufferOutput { |
46 | | public: |
47 | 0 | void add(absl::string_view a) { buffer_.addFragments({a}); } |
48 | 0 | void add(absl::string_view a, absl::string_view b, absl::string_view c) { |
49 | 0 | buffer_.addFragments({a, b, c}); |
50 | 0 | } |
51 | | |
52 | 0 | explicit BufferOutput(Buffer::Instance& output) : buffer_(output) {} |
53 | | Buffer::Instance& buffer_; |
54 | | }; |
55 | | |
56 | | // String wrapper that implements the necessary abstraction for the template |
57 | | // StreamerBase. |
58 | | // This could be used to stream JSON output of StreamerBase to a single string. |
59 | | class StringOutput { |
60 | | public: |
61 | 0 | void add(absl::string_view a) { buffer_.append(a); } |
62 | 0 | void add(absl::string_view a, absl::string_view b, absl::string_view c) { |
63 | 0 | absl::StrAppend(&buffer_, a, b, c); |
64 | 0 | } |
65 | 0 | explicit StringOutput(std::string& output) : buffer_(output) {} |
66 | | |
67 | | std::string& buffer_; |
68 | | }; |
69 | | |
70 | | /** |
71 | | * Provides an API for streaming JSON output, as an alternative to populating a |
72 | | * JSON structure with an image of what you want to serialize, or using a |
73 | | * protobuf with reflection. The advantage of this approach is that it does not |
74 | | * require building an intermediate data structure with redundant copies of all |
75 | | * strings, maps, and arrays. |
76 | | * |
77 | | * NOTE: This template take a type that can be used to stream output. This is either |
78 | | * BufferOutput, StringOutput or any other types that have implemented |
79 | | * add(absl::string_view) and |
80 | | * add(absl::string_view, absl::string_view, absl::string_view) methods. |
81 | | */ |
82 | | template <class OutputBufferType> class StreamerBase { |
83 | | public: |
84 | | using Value = absl::variant<absl::string_view, double, uint64_t, int64_t, bool, absl::monostate>; |
85 | | |
86 | | /** |
87 | | * @param response The buffer in which to stream output. |
88 | | * NOTE: The response must could be used to construct instance of OutputBufferType. |
89 | | */ |
90 | 0 | template <class T> explicit StreamerBase(T& response) : response_(response) {} |
91 | | |
92 | | class Array; |
93 | | using ArrayPtr = std::unique_ptr<Array>; |
94 | | class Map; |
95 | | using MapPtr = std::unique_ptr<Map>; |
96 | | |
97 | | /** |
98 | | * Represents the current map or array. We keep track of what character is |
99 | | * needed to close it, and whether or not the first entry has been added. |
100 | | */ |
101 | | class Level { |
102 | | public: |
103 | | Level(StreamerBase& streamer, absl::string_view opener, absl::string_view closer) |
104 | 0 | : streamer_(streamer), closer_(closer) { |
105 | 0 | streamer_.addWithoutSanitizing(opener); |
106 | 0 | #ifndef NDEBUG |
107 | 0 | streamer_.push(this); |
108 | 0 | #endif |
109 | 0 | } |
110 | 0 | virtual ~Level() { |
111 | 0 | streamer_.addWithoutSanitizing(closer_); |
112 | 0 | #ifndef NDEBUG |
113 | 0 | streamer_.pop(this); |
114 | 0 | #endif |
115 | 0 | } |
116 | | |
117 | | /** |
118 | | * This must be called on the top level map or array. It's a programming |
119 | | * error to call this method on a map that's not the top level. |
120 | | * It's also a programming error to call this on map that isn't expecting |
121 | | * a value. You must call Map::addKey prior to calling this. |
122 | | * |
123 | | * @return a newly created subordinate map, which becomes the new top level until destroyed. |
124 | | */ |
125 | 0 | MapPtr addMap() { |
126 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
127 | 0 | nextField(); |
128 | 0 | return std::make_unique<Map>(streamer_); |
129 | 0 | } |
130 | | |
131 | | /** |
132 | | * This must be called on the top level map or array. It's a programming |
133 | | * error to call this method on a map or array that's not the top level. |
134 | | * It's also a programming error to call this on map that isn't expecting |
135 | | * a value. You must call Map::addKey prior to calling this. |
136 | | * |
137 | | * @return a newly created subordinate array, which becomes the new top level until destroyed. |
138 | | */ |
139 | 0 | ArrayPtr addArray() { |
140 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
141 | 0 | nextField(); |
142 | 0 | return std::make_unique<Array>(streamer_); |
143 | 0 | } |
144 | | |
145 | | /** |
146 | | * Adds a numeric value to the current array or map. It's a programming |
147 | | * error to call this method on a map or array that's not the top level. |
148 | | * It's also a programming error to call this on map that isn't expecting |
149 | | * a value. You must call Map::addKey prior to calling this. |
150 | | */ |
151 | 0 | void addNumber(double number) { |
152 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
153 | 0 | nextField(); |
154 | 0 | streamer_.addNumber(number); |
155 | 0 | } |
156 | 0 | void addNumber(uint64_t number) { |
157 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
158 | 0 | nextField(); |
159 | 0 | streamer_.addNumber(number); |
160 | 0 | } |
161 | 0 | void addNumber(int64_t number) { |
162 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
163 | 0 | nextField(); |
164 | 0 | streamer_.addNumber(number); |
165 | 0 | } |
166 | | |
167 | | /** |
168 | | * Adds a string constant value to the current array or map. The string |
169 | | * will be sanitized per JSON rules. |
170 | | * |
171 | | * It's a programming error to call this method on a map or array that's not |
172 | | * the top level. It's also a programming error to call this on map that |
173 | | * isn't expecting a value. You must call Map::addKey prior to calling this. |
174 | | */ |
175 | 0 | void addString(absl::string_view str) { |
176 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
177 | 0 | nextField(); |
178 | 0 | streamer_.addString(str); |
179 | 0 | } |
180 | | |
181 | | /** |
182 | | * Adds a bool constant value to the current array or map. It's a programming |
183 | | * error to call this method on a map or array that's not the top level. |
184 | | * It's also a programming error to call this on map that isn't expecting |
185 | | * a value. You must call Map::addKey prior to calling this. |
186 | | */ |
187 | 0 | void addBool(bool b) { |
188 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
189 | 0 | nextField(); |
190 | 0 | streamer_.addBool(b); |
191 | 0 | } |
192 | | |
193 | | /** |
194 | | * Adds a null constant value to the current array or map. It's a programming |
195 | | * error to call this method on a map or array that's not the top level. |
196 | | * It's also a programming error to call this on map that isn't expecting |
197 | | * a value. You must call Map::addKey prior to calling this. |
198 | | */ |
199 | 0 | void addNull() { |
200 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
201 | 0 | nextField(); |
202 | 0 | streamer_.addNull(); |
203 | 0 | } |
204 | | |
205 | | protected: |
206 | | /** |
207 | | * Initiates a new field, serializing a comma separator if this is not the |
208 | | * first one. |
209 | | */ |
210 | 0 | virtual void nextField() { |
211 | 0 | if (is_first_) { |
212 | 0 | is_first_ = false; |
213 | 0 | } else { |
214 | 0 | streamer_.addWithoutSanitizing(","); |
215 | 0 | } |
216 | 0 | } |
217 | | |
218 | | /** |
219 | | * Renders a string or a number in json format. Doubles that are NaN are |
220 | | * rendered as 'null'. Strings are json-sanitized if needed, and surrounded |
221 | | * by quotes. |
222 | | * |
223 | | * @param Value the value to render. |
224 | | */ |
225 | 0 | void addValue(const Value& value) { |
226 | 0 | static_assert(absl::variant_size_v<Value> == 6, "Value must be a variant with 6 types"); |
227 | |
|
228 | 0 | switch (value.index()) { |
229 | 0 | case 0: |
230 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<0, Value>, absl::string_view>, |
231 | 0 | "value at index 0 must be an absl::string_vlew"); |
232 | 0 | addString(absl::get<absl::string_view>(value)); |
233 | 0 | break; |
234 | 0 | case 1: |
235 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<1, Value>, double>, |
236 | 0 | "value at index 1 must be a double"); |
237 | 0 | addNumber(absl::get<double>(value)); |
238 | 0 | break; |
239 | 0 | case 2: |
240 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<2, Value>, uint64_t>, |
241 | 0 | "value at index 2 must be a uint64_t"); |
242 | 0 | addNumber(absl::get<uint64_t>(value)); |
243 | 0 | break; |
244 | 0 | case 3: |
245 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<3, Value>, int64_t>, |
246 | 0 | "value at index 3 must be an int64_t"); |
247 | 0 | addNumber(absl::get<int64_t>(value)); |
248 | 0 | break; |
249 | 0 | case 4: |
250 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<4, Value>, bool>, |
251 | 0 | "value at index 4 must be a bool"); |
252 | 0 | addBool(absl::get<bool>(value)); |
253 | 0 | break; |
254 | 0 | case 5: |
255 | 0 | static_assert(std::is_same_v<absl::variant_alternative_t<5, Value>, absl::monostate>, |
256 | 0 | "value at index 5 must be an absl::monostate"); |
257 | 0 | addNull(); |
258 | 0 | break; |
259 | 0 | } |
260 | 0 | } |
261 | | |
262 | | protected: |
263 | | bool is_first_{true}; // Used to control whether a comma-separator is added for a new entry. |
264 | | StreamerBase& streamer_; |
265 | | absl::string_view closer_; |
266 | | }; |
267 | | using LevelPtr = std::unique_ptr<Level>; |
268 | | |
269 | | /** |
270 | | * Represents a JSON map while it is being serialized. No data is buffered |
271 | | * in the structure; just enough state to be able emit delimiters properly. |
272 | | */ |
273 | | class Map : public Level { |
274 | | public: |
275 | | using NameValue = std::pair<const absl::string_view, Value>; |
276 | | using Entries = absl::Span<const NameValue>; |
277 | | |
278 | 0 | Map(StreamerBase& streamer) : Level(streamer, "{", "}") {} |
279 | | |
280 | | /** |
281 | | * Initiates a new map key. This must be followed by rendering a value, |
282 | | * sub-array, or sub-map. It is a programming error to delete a map that has |
283 | | * rendered a key without a matching value. It's also a programming error to |
284 | | * call this method on a map that's not the current top level. |
285 | | * |
286 | | * See also addEntries, which directly populates a list of name/value |
287 | | * pairs in a single call. |
288 | | */ |
289 | 0 | void addKey(absl::string_view key) { |
290 | 0 | ASSERT_THIS_IS_TOP_LEVEL; |
291 | 0 | ASSERT(!expecting_value_); |
292 | 0 | nextField(); |
293 | 0 | this->streamer_.addSanitized("\"", key, "\":"); |
294 | 0 | expecting_value_ = true; |
295 | 0 | } |
296 | | |
297 | | /** |
298 | | * Populates a list of name/value pairs in a single call. This function |
299 | | * makes it easy to populate structures with scalar values. It's a |
300 | | * programming error to call this method on a map that's not the current top |
301 | | * level. |
302 | | */ |
303 | 0 | void addEntries(const Entries& entries) { |
304 | 0 | for (const NameValue& entry : entries) { |
305 | 0 | addKey(entry.first); |
306 | 0 | this->addValue(entry.second); |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | protected: |
311 | 0 | void nextField() override { |
312 | 0 | if (expecting_value_) { |
313 | 0 | expecting_value_ = false; |
314 | 0 | } else { |
315 | 0 | Level::nextField(); |
316 | 0 | } |
317 | 0 | } |
318 | | |
319 | | private: |
320 | | bool expecting_value_{false}; |
321 | | }; |
322 | | |
323 | | /** |
324 | | * Represents a JSON array while it is being serialized. No data is buffered |
325 | | * in the structure; just enough state to be able emit delimiters properly. |
326 | | */ |
327 | | class Array : public Level { |
328 | | public: |
329 | 0 | Array(StreamerBase& streamer) : Level(streamer, "[", "]") {} |
330 | | using Entries = absl::Span<const Value>; |
331 | | |
332 | | /** |
333 | | * Adds values to an array. The values may be numeric or strings; strings |
334 | | * will be escaped if needed. It's a programming error to call this method |
335 | | * on an array that's not the current top level. |
336 | | * |
337 | | * @param entries the array of numeric or string values. |
338 | | */ |
339 | 0 | void addEntries(const Entries& entries) { |
340 | 0 | for (const Value& value : entries) { |
341 | 0 | this->addValue(value); |
342 | 0 | } |
343 | 0 | } |
344 | | }; |
345 | | |
346 | | /** |
347 | | * Makes a root map for the streamer. |
348 | | * |
349 | | * You must create a root map or array before any of the JSON population |
350 | | * functions can be called, as those are only available on Map and Array |
351 | | * objects. |
352 | | */ |
353 | 0 | MapPtr makeRootMap() { |
354 | 0 | ASSERT_LEVELS_EMPTY; |
355 | 0 | return std::make_unique<Map>(*this); |
356 | 0 | } |
357 | | |
358 | | /** |
359 | | * Makes a root array for the streamer. |
360 | | * |
361 | | * You must create a root map or array before any of the JSON population |
362 | | * functions can be called, as those are only available on Map and Array |
363 | | * objects. |
364 | | */ |
365 | 0 | ArrayPtr makeRootArray() { |
366 | 0 | ASSERT_LEVELS_EMPTY; |
367 | 0 | return std::make_unique<Array>(*this); |
368 | 0 | } |
369 | | |
370 | | /** |
371 | | * Takes a raw string, sanitizes it using JSON syntax, surrounds it |
372 | | * with a prefix and suffix, and streams it out. |
373 | | */ |
374 | 0 | void addSanitized(absl::string_view prefix, absl::string_view token, absl::string_view suffix) { |
375 | 0 | absl::string_view sanitized = Json::sanitize(sanitize_buffer_, token); |
376 | 0 | response_.add(prefix, sanitized, suffix); |
377 | 0 | } |
378 | | |
379 | | /** |
380 | | * Serializes a string to the output stream. The input string value will be sanitized and |
381 | | * surrounded by quotes. |
382 | | * @param str the string to be serialized. |
383 | | */ |
384 | 0 | void addString(absl::string_view str) { addSanitized("\"", str, "\""); } |
385 | | |
386 | | /** |
387 | | * Serializes a number. |
388 | | */ |
389 | 0 | void addNumber(double d) { |
390 | 0 | if (std::isnan(d)) { |
391 | 0 | response_.add(Constants::Null); |
392 | 0 | } else { |
393 | 0 | Buffer::Util::serializeDouble(d, response_); |
394 | 0 | } |
395 | 0 | } |
396 | 0 | void addNumber(uint64_t u) { response_.add(absl::StrCat(u)); } |
397 | 0 | void addNumber(int64_t i) { response_.add(absl::StrCat(i)); } |
398 | | |
399 | | /** |
400 | | * Serializes a bool to the output stream. |
401 | | */ |
402 | 0 | void addBool(bool b) { response_.add(b ? Constants::True : Constants::False); } |
403 | | |
404 | | /** |
405 | | * Serializes a null to the output stream. |
406 | | */ |
407 | 0 | void addNull() { response_.add(Constants::Null); } |
408 | | |
409 | | private: |
410 | | /** |
411 | | * Adds a string to the output stream without sanitizing it. This is only used to push |
412 | | * the delimiters to output buffer. |
413 | | */ |
414 | 0 | void addWithoutSanitizing(absl::string_view str) { response_.add(str); } |
415 | | |
416 | | #ifndef NDEBUG |
417 | | /** |
418 | | * @return the top Level*. This is used for asserts. |
419 | | */ |
420 | 0 | Level* topLevel() const { return levels_.top(); } |
421 | | |
422 | | /** |
423 | | * Pushes a new level onto the stack. |
424 | | */ |
425 | 0 | void push(Level* level) { levels_.push(level); } |
426 | | |
427 | | /** |
428 | | * Pops a level off of a stack, asserting that it matches. |
429 | | */ |
430 | 0 | void pop(Level* level) { |
431 | 0 | ASSERT(levels_.top() == level); |
432 | 0 | levels_.pop(); |
433 | 0 | } |
434 | | |
435 | | #endif |
436 | | |
437 | | OutputBufferType response_; |
438 | | std::string sanitize_buffer_; |
439 | | |
440 | | #ifndef NDEBUG |
441 | | // Keeps a stack of Maps or Arrays (subclasses of Level) to facilitate |
442 | | // assertions that only the top-level map/array can be written. |
443 | | std::stack<Level*> levels_; |
444 | | #endif |
445 | | }; |
446 | | |
447 | | /** |
448 | | * A Streamer that streams to a Buffer::Instance. |
449 | | */ |
450 | | using Streamer = StreamerBase<BufferOutput>; |
451 | | |
452 | | } // namespace Json |
453 | | } // namespace Envoy |