Line data Source code
1 : #pragma once 2 : 3 : #include <memory> 4 : #include <stack> 5 : #include <string> 6 : 7 : #include "envoy/buffer/buffer.h" 8 : 9 : #include "absl/strings/string_view.h" 10 : #include "absl/types/variant.h" 11 : 12 : namespace Envoy { 13 : namespace Json { 14 : 15 : /** 16 : * Provides an API for streaming JSON output, as an alternative to populating a 17 : * JSON structure with an image of what you want to serialize, or using a 18 : * protobuf with reflection. The advantage of this approach is that it does not 19 : * require building an intermediate data structure with redundant copies of all 20 : * strings, maps, and arrays. 21 : */ 22 : class Streamer { 23 : public: 24 : using Value = absl::variant<absl::string_view, double, uint64_t, int64_t>; 25 : 26 : /** 27 : * @param response The buffer in which to stream output. Note: this buffer can 28 : * be flushed during population; it is not necessary to hold 29 : * the entire json structure in memory before streaming it to 30 : * the network. 31 : */ 32 0 : explicit Streamer(Buffer::Instance& response) : response_(response) {} 33 : 34 : class Array; 35 : using ArrayPtr = std::unique_ptr<Array>; 36 : class Map; 37 : using MapPtr = std::unique_ptr<Map>; 38 : 39 : /** 40 : * Represents the current map or array. We keep track of what character is 41 : * needed to close it, and whether or not the first entry has been added. 42 : */ 43 : class Level { 44 : public: 45 : Level(Streamer& streamer, absl::string_view opener, absl::string_view closer); 46 : virtual ~Level(); 47 : 48 : /** 49 : * This must be called on the top level map or array. It's a programming 50 : * error to call this method on a map that's not the top level. 51 : * It's also a programming error to call this on map that isn't expecting 52 : * a value. You must call Map::addKey prior to calling this. 53 : * 54 : * @return a newly created subordinate map, which becomes the new top level until destroyed. 55 : */ 56 : MapPtr addMap(); 57 : 58 : /** 59 : * This must be called on the top level map or array. It's a programming 60 : * error to call this method on a map or array that's not the top level. 61 : * It's also a programming error to call this on map that isn't expecting 62 : * a value. You must call Map::addKey prior to calling this. 63 : * 64 : * @return a newly created subordinate array, which becomes the new top level until destroyed. 65 : */ 66 : ArrayPtr addArray(); 67 : 68 : /** 69 : * Adds a numeric value to the current array or map. It's a programming 70 : * error to call this method on a map or array that's not the top level. 71 : * It's also a programming error to call this on map that isn't expecting 72 : * a value. You must call Map::addKey prior to calling this. 73 : */ 74 : void addNumber(double d); 75 : void addNumber(uint64_t u); 76 : void addNumber(int64_t i); 77 : 78 : /** 79 : * Adds a string constant value to the current array or map. The string 80 : * will be sanitized per JSON rules. 81 : * 82 : * It's a programming error to call this method on a map or array that's not 83 : * the top level. It's also a programming error to call this on map that 84 : * isn't expecting a value. You must call Map::addKey prior to calling this. 85 : */ 86 : void addString(absl::string_view str); 87 : 88 : protected: 89 : /** 90 : * Initiates a new field, serializing a comma separator if this is not the 91 : * first one. 92 : */ 93 : virtual void nextField(); 94 : 95 : /** 96 : * Renders a string or a number in json format. Doubles that are NaN are 97 : * rendered as 'null'. Strings are json-sanitized if needed, and surrounded 98 : * by quotes. 99 : * 100 : * @param Value the value to render. 101 : */ 102 : void addValue(const Value& value); 103 : 104 : private: 105 : friend Streamer; 106 : 107 : bool is_first_{true}; // Used to control whether a comma-separator is added for a new entry. 108 : Streamer& streamer_; 109 : absl::string_view closer_; 110 : }; 111 : using LevelPtr = std::unique_ptr<Level>; 112 : 113 : /** 114 : * Represents a JSON map while it is being serialized. No data is buffered 115 : * in the structure; just enough state to be able emit delimiters properly. 116 : */ 117 : class Map : public Level { 118 : public: 119 : using NameValue = std::pair<const absl::string_view, Value>; 120 : using Entries = absl::Span<const NameValue>; 121 : 122 0 : Map(Streamer& streamer) : Level(streamer, "{", "}") {} 123 : 124 : /** 125 : * Initiates a new map key. This must be followed by rendering a value, 126 : * sub-array, or sub-map. It is a programming error to delete a map that has 127 : * rendered a key without a matching value. It's also a programming error to 128 : * call this method on a map that's not the current top level. 129 : * 130 : * See also addEntries, which directly populates a list of name/value 131 : * pairs in a single call. 132 : */ 133 : void addKey(absl::string_view key); 134 : 135 : /** 136 : * Populates a list of name/value pairs in a single call. This function 137 : * makes it easy to populate structures with scalar values. It's a 138 : * programming error to call this method on a map that's not the current top 139 : * level. 140 : */ 141 : void addEntries(const Entries& entries); 142 : 143 : protected: 144 : void nextField() override; 145 : 146 : private: 147 : bool expecting_value_{false}; 148 : }; 149 : 150 : /** 151 : * Represents a JSON array while it is being serialized. No data is buffered 152 : * in the structure; just enough state to be able emit delimiters properly. 153 : */ 154 : class Array : public Level { 155 : public: 156 0 : Array(Streamer& streamer) : Level(streamer, "[", "]") {} 157 : using Entries = absl::Span<const Value>; 158 : 159 : /** 160 : * Adds values to an array. The values may be numeric or strings; strings 161 : * will be escaped if needed. It's a programming error to call this method 162 : * on an array that's not the current top level. 163 : * 164 : * @param entries the array of numeric or string values. 165 : */ 166 : void addEntries(const Entries& entries); 167 : }; 168 : 169 : /** 170 : * Makes a root map for the streamer. 171 : * 172 : * You must create a root map or array before any of the JSON population 173 : * functions can be called, as those are only available on Map and Array 174 : * objects. 175 : */ 176 : MapPtr makeRootMap(); 177 : 178 : /** 179 : * Makes a root array for the streamer. 180 : * 181 : * You must create a root map or array before any of the JSON population 182 : * functions can be called, as those are only available on Map and Array 183 : * objects. 184 : */ 185 : ArrayPtr makeRootArray(); 186 : 187 : private: 188 : friend Level; 189 : friend Map; 190 : friend Array; 191 : 192 : /** 193 : * Takes a raw string, sanitizes it using JSON syntax, surrounds it 194 : * with a prefix and suffix, and streams it out. 195 : */ 196 : void addSanitized(absl::string_view prefix, absl::string_view token, absl::string_view suffix); 197 : 198 : /** 199 : * Serializes a number. 200 : */ 201 : void addNumber(double d); 202 : void addNumber(uint64_t u); 203 : void addNumber(int64_t i); 204 : 205 : /** 206 : * Flushes out any pending fragments. 207 : */ 208 : void flush(); 209 : 210 : /** 211 : * Adds a constant string to the output stream. The string must outlive the 212 : * Streamer object, and is intended for literal strings such as punctuation. 213 : */ 214 0 : void addConstantString(absl::string_view str) { response_.addFragments({str}); } 215 : 216 : #ifndef NDEBUG 217 : /** 218 : * @return the top Level*. This is used for asserts. 219 : */ 220 : Level* topLevel() const { return levels_.top(); } 221 : 222 : /** 223 : * Pushes a new level onto the stack. 224 : */ 225 : void push(Level* level); 226 : 227 : /** 228 : * Pops a level off of a stack, asserting that it matches. 229 : */ 230 : void pop(Level* level); 231 : #endif 232 : 233 : Buffer::Instance& response_; 234 : std::string sanitize_buffer_; 235 : 236 : #ifndef NDEBUG 237 : // Keeps a stack of Maps or Arrays (subclasses of Level) to facilitate 238 : // assertions that only the top-level map/array can be written. 239 : std::stack<Level*> levels_; 240 : #endif 241 : }; 242 : 243 : } // namespace Json 244 : } // namespace Envoy