Coverage Report

Created: 2024-09-19 09:45

/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