1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef JSON_FEED_H
#define JSON_FEED_H

#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <util/state_model.h>
#include <boost/shared_ptr.hpp><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <stdint.h><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <string><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <vector><--- Include file:  not found. Please note: Cppcheck does not need standard library headers to get proper results.

namespace isc {
namespace config {

class JSONFeed;

/// @brief Pointer to the @ref JSONFeed.
typedef boost::shared_ptr<JSONFeed> JSONFeedPtr;

/// @brief Pointer to the const @ref JSONFeed.
typedef boost::shared_ptr<const JSONFeed> ConstJSONFeedPtr;

/// @brief A generic exception thrown upon an error in the @ref JSONFeed.
class JSONFeedError : public Exception {
public:
    JSONFeedError(const char* file, size_t line, const char* what) :
        isc::Exception(file, line, what) { };
};

/// @brief State model for asynchronous read of data in JSON format.
///
/// Kea control channel uses stream sockets for forwarding commands received
/// by the Kea Control Agent to respective Kea services. The responses may
/// contain large amounts of data (e.g. lease queries may return thousands
/// of leases). Such responses rarely fit into a single data buffer and
/// require multiple calls to receive/read or asynchronous receive/read.
///
/// A receiver performing multiple reads from a socket must be able to
/// locate the boundaries of the command within the data stream. The
/// @ref JSONFeed state model solves this problem.
///
/// When the partial data is read from the stream socket it should be provided
/// to the @ref JSONFeed using @ref JSONFeed::postBuffer and then the
/// @ref JSONFeed::poll should be called to start processing the received
/// data. The actual JSON structure can be preceded by whitespaces. When first
/// occurrence of one of the '{' or '[' characters is found in the stream it is
/// considered a beginning of the JSON structure. The model includes an internal
/// counter of new '{' and '[' occurrences. The counter increases one of these
/// characters is found. When any of the '}' or ']' is found, the counter
/// is decreased. When the counter is decreased to 0 it indicates that the
/// entire JSON structure has been received and processed.
///
/// As '{', '}', '[' and ']' can be embedded in JSON strings two states
/// for strings and escape in strings are required. Note the processing
/// of escapes is greatly simplified compared to ECMA 404 figure 5.
///
/// Added support for '#' to end of line (bash), '//' to end of line (C++)
/// and '/*' to '*/' (C) comments both before JSON and inside JSON.

/// Note that this mechanism doesn't check if the JSON structure is well
/// formed. It merely detects the end of the JSON structure if this structure
/// is well formed. The structure is validated when @ref JSONFeed::toElement
/// is called to retrieve the data structures encapsulated with
/// @ref isc::data::Element objects.
class JSONFeed : public util::StateModel {
public:

    /// @name States supported by the @ref JSONFeed
    ///
    //@{

    /// @brief State indicating a beginning of a feed.
    static const int RECEIVE_START_ST = SM_DERIVED_STATE_MIN + 1;

    /// @brief Skipping whitespaces before actual JSON.
    static const int WHITESPACE_BEFORE_JSON_ST = SM_DERIVED_STATE_MIN + 2;

    /// @brief Skipping an end-of-line comment before actual JSON.
    static const int EOL_COMMENT_BEFORE_JSON_ST = SM_DERIVED_STATE_MIN + 3;

    /// @brief Starting one of the comments beginning with a slash before actual JSON.
    static const int START_COMMENT_BEFORE_JSON_ST = SM_DERIVED_STATE_MIN + 4;

    /// @brief Skipping a C style comment before actual JSON.
    static const int C_COMMENT_BEFORE_JSON_ST = SM_DERIVED_STATE_MIN + 5;

    /// @brief Stopping a C style comment before actual JSON.
    static const int STOP_COMMENT_BEFORE_JSON_ST = SM_DERIVED_STATE_MIN + 6;

    /// @brief Found first opening brace or square bracket.
    static const int JSON_START_ST = SM_DERIVED_STATE_MIN + 7;

    /// @brief Parsing JSON.
    static const int INNER_JSON_ST = SM_DERIVED_STATE_MIN + 8;

    /// @brief Parsing JSON string.
    static const int STRING_JSON_ST = SM_DERIVED_STATE_MIN + 9;

    /// @brief JSON escape next character.
    static const int ESCAPE_JSON_ST = SM_DERIVED_STATE_MIN + 10;

    /// @brief Skipping an end-of-line comment.
    static const int EOL_COMMENT_ST = SM_DERIVED_STATE_MIN + 11;

    /// @brief Starting one of the comments beginning with a slash.
    static const int START_COMMENT_ST = SM_DERIVED_STATE_MIN + 12;

    /// @brief Skipping a C style comment.
    static const int C_COMMENT_ST = SM_DERIVED_STATE_MIN + 13;

    /// @brief Stopping a C style comment.
    static const int STOP_COMMENT_ST = SM_DERIVED_STATE_MIN + 14;

    /// @brief Found last closing brace or square bracket.
    static const int JSON_END_ST = SM_DERIVED_STATE_MIN + 15;

    /// @brief Found opening and closing brace or square bracket.
    ///
    /// This doesn't however indicate that the JSON is well formed. It
    /// only means that matching closing brace or square bracket was
    /// found.
    static const int FEED_OK_ST = SM_DERIVED_STATE_MIN + 100;

    /// @brief Invalid syntax detected.
    ///
    /// For example, non matching braces or invalid characters found.
    static const int FEED_FAILED_ST = SM_DERIVED_STATE_MIN + 101;

    //@}


    /// @name Events used during data processing.
    ///
    //@{

    /// @brief Chunk of data successfully read and parsed.
    static const int DATA_READ_OK_EVT = SM_DERIVED_EVENT_MIN + 1;

    /// @brief Unable to proceed with parsing until new data is provided.
    static const int NEED_MORE_DATA_EVT = SM_DERIVED_EVENT_MIN + 2;

    /// @brief New data provided and parsing should continue.
    static const int MORE_DATA_PROVIDED_EVT = SM_DERIVED_EVENT_MIN + 3;

    /// @brief Found opening brace and the matching closing brace.
    static const int FEED_OK_EVT = SM_DERIVED_EVENT_MIN + 100;

    /// @brief Invalid syntax detected.
    static const int FEED_FAILED_EVT = SM_DERIVED_EVENT_MIN + 101;

    //@}

    /// @brief Constructor.
    JSONFeed();

    /// @brief Initializes state model.
    ///
    /// Initializes events and states. It sets the model to @c RECEIVE_START_ST
    /// and the next event to @c START_EVT.
    void initModel();

    /// @brief Runs the model as long as data is available.
    ///
    /// It processes the input data character by character until it reaches the
    /// end of the input buffer, in which case it returns. The next event is set
    /// to @c NEED_MORE_DATA_EVT to indicate the need for providing additional
    /// data using @ref JSONFeed::postBuffer. This function also returns when
    /// the end of the JSON structure has been detected or when an error has
    /// occurred.
    void poll();

    /// @brief Checks if the model needs additional data to continue.
    ///
    /// The caller can use this method to check if the model expects additional
    /// data to be provided to finish processing input data.
    ///
    /// @return true if more data is needed, false otherwise.
    bool needData() const;

    /// @brief Checks if the data have been successfully processed.
    bool feedOk() const;

    /// @brief Returns error string when data processing has failed.
    std::string getErrorMessage() const {
        return (error_message_);
    }

    /// @brief Returns the text parsed into the buffer.
    std::string getProcessedText() const {
        return (output_);
    }

    /// @brief Returns processed data as a structure of @ref isc::data::Element
    /// objects.
    ///
    /// @throw JSONFeedError if the received JSON is not well formed.
    data::ElementPtr toElement() const;

    /// @brief Receives additional data read from a data stream.
    ///
    /// A caller invokes this method to pass additional chunk of data received
    /// from the stream.
    ///
    /// @param buf Pointer to a buffer holding additional input data.
    /// @param buf_size Size of the data in the input buffer.
    void postBuffer(const void* buf, const size_t buf_size);


private:

    /// @brief Make @ref runModel private to make sure that the caller uses
    /// @ref poll method instead.
    using StateModel::runModel;

    /// @brief Define events used by the feed.
    virtual void defineEvents();<--- Function in derived class

    /// @brief Verifies events used by the feed.
    virtual void verifyEvents();<--- Function in derived class

    /// @brief Defines states of the feed.
    virtual void defineStates();<--- Function in derived class

    /// @brief Transition to failure state.
    ///
    /// This method transitions the model to @ref FEED_FAILED_ST and
    /// sets next event to FEED_FAILED_EVT.
    ///
    /// @param error_msg Error message explaining the failure.
    void feedFailure(const std::string& error_msg);

    /// @brief A method called when state model fails.
    ///
    /// @param explanation Error message explaining the reason for failure.
    virtual void onModelFailure(const std::string& explanation);<--- Function in derived class

    /// @brief Retrieves next byte of data from the buffer.
    ///
    /// During normal operation, when there is no more data in the buffer,
    /// the NEED_MORE_DATA_EVT is set as next event to signal the need for
    /// calling @ref JSONFeed::postBuffer.
    ///
    /// @throw JSONFeedError If current event is already set to
    /// NEED_MORE_DATA_EVT or MORE_DATA_PROVIDED_EVT. In the former case, it
    /// indicates that the caller failed to provide new data using
    /// @ref JSONFeed::postBuffer. The latter case is highly unlikely
    /// as it indicates that no new data were provided but the state of the
    /// parser was changed from NEED_MORE_DATA_EVT or the data were provided
    /// but the data buffer is empty. In both cases, it is a programming
    /// error.
    char getNextFromBuffer();

    /// @brief This method is called when invalid event occurred in a particular
    /// state.
    ///
    /// This method simply throws @ref JSONFeedError informing about invalid
    /// event occurring for the particular state. The error message includes
    /// the name of the handler in which the exception has been thrown.
    /// It also includes the event which caused the exception.
    ///
    /// @param handler_name Name of the handler in which the exception is
    /// thrown.
    /// @param event An event which caused the exception.
    ///
    /// @throw JSONFeedError.
    void invalidEventError(const std::string& handler_name,
                           const unsigned int event);

    /// @brief Tries to read next byte from buffer.
    ///
    /// @param [out] next A reference to the variable where read data should be
    /// stored.
    ///
    /// @return true if character was successfully read, false otherwise.
    bool popNextFromBuffer(char& next);

    /// @name State handlers.
    ///
    //@{

    /// @brief Handler for RECEIVE_START_ST.
    void receiveStartHandler();

    /// @brief Handler for WHITESPACE_BEFORE_JSON_ST.
    void whiteSpaceBeforeJSONHandler();

    /// @brief Handler for EOL_COMMENT_BEFORE_JSON_ST.
    void eolCommentBeforeJSONHandler();

    /// @brief Handler for START_COMMENT_BEFORE_JSON_ST.
    void startCommentBeforeJSONHandler();

    /// @brief Handler for C_COMMENT_BEFORE_JSON_ST.
    void cCommentBeforeJSONHandler();

    /// @brief Handler for STOP_COMMENT_BEFORE_JSON_ST.
    void stopCommentBeforeJSONHandler();

    /// @brief Handler for the FIRST_BRACE_ST.
    void innerJSONHandler();

    /// @brief Handler for the STRING_JSON_ST.
    void stringJSONHandler();

    /// @brief Handler for the ESCAPE_JSON_ST;
    void escapeJSONHandler();

    /// @brief Handler for EOL_COMMENT_ST.
    void eolCommentHandler();

    /// @brief Handler for START_COMMENT_ST.
    void startCommentHandler();

    /// @brief Handler for C_COMMENT_ST.
    void cCommentHandler();

    /// @brief Handler for STOP_COMMENT_ST.
    void stopCommentHandler();

    /// @brief Handler for the JSON_END_ST.
    void endJSONHandler();

    //@}

    /// @brief Internal buffer from which the feed reads data.
    std::vector<char> buffer_;

    /// @brief Holds pointer to the next byte in the buffer to be read.
    size_t data_ptr_;

    /// @brief Error message set by @ref onModelFailure.
    std::string error_message_;

    /// @brief A counter increased when '{' or '[' is found and decreased when
    /// '}' or ']' is found in the stream.
    uint64_t open_scopes_;

    /// @brief Holds processed data.
    std::string output_;
};

} // end of namespace config
} // end of namespace isc

#endif // JSON_FEED_H