Kea 2.6.0
json_feed.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2021 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
9#include <cc/data.h>
10#include <cc/json_feed.h>
11#include <functional>
12
13using namespace isc::data;
14using namespace isc::util;
15
16namespace isc {
17namespace config {
18
33const int JSONFeed::JSON_END_ST;
34const int JSONFeed::FEED_OK_ST;
36
40const int JSONFeed::FEED_OK_EVT;
42
44 : StateModel(), buffer_(), data_ptr_(0), error_message_(), open_scopes_(0),
45 output_() {
46}
47
48void
50 // Initialize dictionaries of events and states.
52
53 // Set the current state to starting state and enter the run loop.
55
56 // Parsing starts from here.
58}
59
60void
62 try {
63 // Process the input data until no more data is available or until
64 // JSON feed ends with matching closing brace.
65 do {
66 getState(getCurrState())->run();
67
68 } while (!isModelDone() && (getNextEvent() != NOP_EVT) &&
70 } catch (const std::exception& ex) {
71 abortModel(ex.what());
72 }
73}
74
75bool
77 return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
78 (getNextEvent() == START_EVT));
79}
80
81bool
83 return ((getNextEvent() == END_EVT) &&
85}
86
89 if (needData()) {
90 isc_throw(JSONFeedError, "unable to retrieve the data form the"
91 " JSON feed while parsing hasn't finished");
92 }
93 try {
94 return (Element::fromWire(output_));
95
96 } catch (const std::exception& ex) {
98 }
99}
100
101void
102JSONFeed::postBuffer(const void* buf, const size_t buf_size) {
103 if (buf_size > 0) {
104 // The next event is NEED_MORE_DATA_EVT when the parser wants to
105 // signal that more data is needed. This method is called to supply
106 // more data and thus it should change the next event to
107 // MORE_DATA_PROVIDED_EVT.
110 }
111 buffer_.assign(static_cast<const char*>(buf),
112 static_cast<const char*>(buf) + buf_size);
113 data_ptr_ = 0;
114 }
115}
116
117void
118JSONFeed::defineEvents() {
120
121 // Define JSONFeed specific events.
122 defineEvent(DATA_READ_OK_EVT, "DATA_READ_OK_EVT");
123 defineEvent(NEED_MORE_DATA_EVT, "NEED_MORE_DATA_EVT");
124 defineEvent(MORE_DATA_PROVIDED_EVT, "MORE_DATA_PROVIDED_EVT");
125 defineEvent(FEED_OK_EVT, "FEED_OK_EVT");
126 defineEvent(FEED_FAILED_EVT, "FEED_FAILED_EVT");
127}
128
129void
130JSONFeed::verifyEvents() {
132
138}
139
140void
141JSONFeed::defineStates() {
142 // Call parent class implementation first.
144
145 defineState(RECEIVE_START_ST, "RECEIVE_START_ST",
146 std::bind(&JSONFeed::receiveStartHandler, this));
147 defineState(WHITESPACE_BEFORE_JSON_ST, "WHITESPACE_BEFORE_JSON_ST",
148 std::bind(&JSONFeed::whiteSpaceBeforeJSONHandler, this));
149 defineState(EOL_COMMENT_BEFORE_JSON_ST, "EOL_COMMENT_BEFORE_JSON_ST",
150 std::bind(&JSONFeed::eolCommentBeforeJSONHandler, this));
151 defineState(START_COMMENT_BEFORE_JSON_ST, "START_COMMENT_BEFORE_JSON_ST",
152 std::bind(&JSONFeed::startCommentBeforeJSONHandler, this));
153 defineState(C_COMMENT_BEFORE_JSON_ST, "C_COMMENT_BEFORE_JSON_ST",
154 std::bind(&JSONFeed::cCommentBeforeJSONHandler, this));
155 defineState(STOP_COMMENT_BEFORE_JSON_ST, "STOP_COMMENT_BEFORE_JSON_ST",
156 std::bind(&JSONFeed::stopCommentBeforeJSONHandler, this));
157 defineState(INNER_JSON_ST, "INNER_JSON_ST",
158 std::bind(&JSONFeed::innerJSONHandler, this));
159 defineState(STRING_JSON_ST, "STRING_JSON_ST",
160 std::bind(&JSONFeed::stringJSONHandler, this));
161 defineState(ESCAPE_JSON_ST, "ESCAPE_JSON_ST",
162 std::bind(&JSONFeed::escapeJSONHandler, this));
163 defineState(EOL_COMMENT_ST, "EOL_COMMENT_ST",
164 std::bind(&JSONFeed::eolCommentHandler, this));
165 defineState(START_COMMENT_ST, "START_COMMENT_ST",
166 std::bind(&JSONFeed::startCommentHandler, this));
167 defineState(C_COMMENT_ST, "C_COMMENT_ST",
168 std::bind(&JSONFeed::cCommentHandler, this));
169 defineState(STOP_COMMENT_ST, "STOP_COMMENT_ST",
170 std::bind(&JSONFeed::stopCommentHandler, this));
171 defineState(JSON_END_ST, "JSON_END_ST",
172 std::bind(&JSONFeed::endJSONHandler, this));
173}
174
175void
176JSONFeed::feedFailure(const std::string& error_msg) {
177 error_message_ = error_msg;
179}
180
181void
182JSONFeed::onModelFailure(const std::string& explanation) {
183 if (error_message_.empty()) {
184 error_message_ = explanation;
185 }
186}
187
188bool
189JSONFeed::popNextFromBuffer(char& next) {
190 // If there are any characters in the buffer, pop next.
191 if (!buffer_.empty() && (data_ptr_ < buffer_.size())) {
192 next = buffer_[data_ptr_++];
193 return (true);
194 }
195 return (false);
196}
197
198char
199JSONFeed::getNextFromBuffer() {
200 unsigned int ev = getNextEvent();
201 char c = '\0';
202 // The caller should always provide additional data when the
203 // NEED_MORE_DATA_EVT occurs. If the next event is still
204 // NEED_MORE_DATA_EVT it indicates that the caller hasn't provided
205 // the data.
206 if (ev == NEED_MORE_DATA_EVT) {
207 isc_throw(JSONFeedError,
208 "JSONFeed requires new data to progress, but no data"
209 " have been provided. The transaction is aborted to avoid"
210 " a deadlock.");
211
212 } else {
213 // Try to pop next character from the buffer.
214 const bool data_exist = popNextFromBuffer(c);
215 if (!data_exist) {
216 // There is no more data so it is really not possible that we're
217 // at MORE_DATA_PROVIDED_EVT.
218 if (ev == MORE_DATA_PROVIDED_EVT) {
219 isc_throw(JSONFeedError,
220 "JSONFeed state indicates that new data have been"
221 " provided to be parsed, but the transaction buffer"
222 " contains no new data.");
223
224 } else {
225 // If there is no more data we should set NEED_MORE_DATA_EVT
226 // event to indicate that new data should be provided.
228 }
229 }
230 }
231 return (c);
232}
233
234void
235JSONFeed::invalidEventError(const std::string& handler_name,
236 const unsigned int event) {
237 isc_throw(JSONFeedError, handler_name << ": invalid event "
238 << getEventLabel(static_cast<int>(event)));
239}
240
241void
242JSONFeed::receiveStartHandler() {
243 char c = getNextFromBuffer();
245 switch (getNextEvent()) {
246 case START_EVT:
247 switch (c) {
248 case '\t':
249 case '\n':
250 case '\r':
251 case ' ':
253 return;
254
255 case '#':
257 return;
258
259 case '/':
261 return;
262
263 case '{':
264 case '[':
265 output_.push_back(c);
266 ++open_scopes_;
268 return;
269
270 // Cannot start by a string
271 case '"':
272 default:
273 feedFailure("invalid first character " + std::string(1, c));
274 break;
275 }
276 break;
277
278 default:
279 invalidEventError("receiveStartHandler", getNextEvent());
280 }
281 }
282}
283
284void
285JSONFeed::whiteSpaceBeforeJSONHandler() {
286 char c = getNextFromBuffer();
288 switch (c) {
289 case '\t':
290 case '\n':
291 case '\r':
292 case ' ':
294 break;
295
296 case '#':
298 return;
299
300 case '/':
302 return;
303
304 case '{':
305 case '[':
306 output_.push_back(c);
307 ++open_scopes_;
309 break;
310
311 // Cannot start by a string
312 case '"':
313 default:
314 feedFailure("invalid character " + std::string(1, c));
315 }
316 }
317}
318
319void
320JSONFeed::eolCommentBeforeJSONHandler() {
321 char c = getNextFromBuffer();
323 switch (c) {
324 case '\n':
326 break;
327
328 default:
330 break;
331 }
332 }
333}
334
335void
336JSONFeed::startCommentBeforeJSONHandler() {
337 char c = getNextFromBuffer();
339 switch (c) {
340 case '/':
342 break;
343
344 case '*':
346 break;
347
348 default:
349 feedFailure("invalid characters /" + std::string(1, c));
350 }
351 }
352}
353
354void
355JSONFeed::cCommentBeforeJSONHandler() {
356 char c = getNextFromBuffer();
358 switch (c) {
359 case '*':
361 break;
362
363 default:
365 break;
366 }
367 }
368}
369
370void
371JSONFeed::stopCommentBeforeJSONHandler() {
372 char c = getNextFromBuffer();
374 switch (c) {
375 case '/':
377 break;
378
379 case '*':
381 break;
382
383 default:
385 break;
386 }
387 }
388}
389
390void
391JSONFeed::innerJSONHandler() {
392 char c = getNextFromBuffer();
394 switch(c) {
395 case '{':
396 case '[':
397 output_.push_back(c);
399 ++open_scopes_;
400 break;
401
402 case '}':
403 case ']':
404 output_.push_back(c);
405 if (--open_scopes_ == 0) {
407
408 } else {
410 }
411 break;
412
413 case '"':
414 output_.push_back(c);
416 break;
417
418 case '#':
420 break;
421
422 case '/':
424 break;
425
426 default:
427 output_.push_back(c);
429 }
430 }
431}
432
433void
434JSONFeed::stringJSONHandler() {
435 char c = getNextFromBuffer();
437 output_.push_back(c);
438
439 switch(c) {
440 case '"':
442 break;
443
444 case '\\':
446 break;
447
448 default:
450 }
451 }
452}
453
454void
455JSONFeed::escapeJSONHandler() {
456 char c = getNextFromBuffer();
458 output_.push_back(c);
459
461 }
462}
463
464void
465JSONFeed::eolCommentHandler() {
466 char c = getNextFromBuffer();
468 switch (c) {
469 case '\n':
470 output_.push_back(c);
472 break;
473
474 default:
476 break;
477 }
478 }
479}
480
481void
482JSONFeed::startCommentHandler() {
483 char c = getNextFromBuffer();
485 switch (c) {
486 case '/':
488 break;
489
490 case '*':
492 break;
493
494 default:
495 feedFailure("invalid characters /" + std::string(1, c));
496 }
497 }
498}
499
500void
501JSONFeed::cCommentHandler() {
502 char c = getNextFromBuffer();
504 switch (c) {
505 case '*':
507 break;
508
509 case '\n':
510 output_.push_back(c);
512 break;
513
514 default:
516 break;
517 }
518 }
519}
520
521void
522JSONFeed::stopCommentHandler() {
523 char c = getNextFromBuffer();
525 switch (c) {
526 case '/':
528 break;
529
530 case '*':
532 break;
533
534 case '\n':
535 output_.push_back(c);
537 break;
538
539 default:
541 break;
542 }
543 }
544}
545
546void
547JSONFeed::endJSONHandler() {
548 switch (getNextEvent()) {
549 case FEED_OK_EVT:
551 break;
552
553 case FEED_FAILED_EVT:
554 abortModel("reading into JSON feed failed");
555 break;
556
557 default:
558 invalidEventError("endJSONHandler", getNextEvent());
559 }
560}
561
562} // end of namespace config
563} // end of namespace isc
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception thrown upon an error in the JSONFeed.
Definition: json_feed.h:30
static const int ESCAPE_JSON_ST
JSON escape next character.
Definition: json_feed.h:106
static const int FEED_FAILED_ST
Invalid syntax detected.
Definition: json_feed.h:133
static const int EOL_COMMENT_BEFORE_JSON_ST
Skipping an end-of-line comment before actual JSON.
Definition: json_feed.h:85
static const int START_COMMENT_ST
Starting one of the comments beginning with a slash.
Definition: json_feed.h:112
void postBuffer(const void *buf, const size_t buf_size)
Receives additional data read from a data stream.
Definition: json_feed.cc:102
static const int EOL_COMMENT_ST
Skipping an end-of-line comment.
Definition: json_feed.h:109
static const int WHITESPACE_BEFORE_JSON_ST
Skipping whitespaces before actual JSON.
Definition: json_feed.h:82
static const int STOP_COMMENT_BEFORE_JSON_ST
Stopping a C style comment before actual JSON.
Definition: json_feed.h:94
static const int START_COMMENT_BEFORE_JSON_ST
Starting one of the comments beginning with a slash before actual JSON.
Definition: json_feed.h:88
bool feedOk() const
Checks if the data have been successfully processed.
Definition: json_feed.cc:82
bool needData() const
Checks if the model needs additional data to continue.
Definition: json_feed.cc:76
static const int STRING_JSON_ST
Parsing JSON string.
Definition: json_feed.h:103
static const int INNER_JSON_ST
Parsing JSON.
Definition: json_feed.h:100
static const int JSON_START_ST
Found first opening brace or square bracket.
Definition: json_feed.h:97
void poll()
Runs the model as long as data is available.
Definition: json_feed.cc:61
static const int FEED_FAILED_EVT
Invalid syntax detected.
Definition: json_feed.h:155
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
Definition: json_feed.h:149
static const int STOP_COMMENT_ST
Stopping a C style comment.
Definition: json_feed.h:118
void initModel()
Initializes state model.
Definition: json_feed.cc:49
static const int FEED_OK_EVT
Found opening brace and the matching closing brace.
Definition: json_feed.h:152
data::ElementPtr toElement() const
Returns processed data as a structure of isc::data::Element objects.
Definition: json_feed.cc:88
static const int RECEIVE_START_ST
State indicating a beginning of a feed.
Definition: json_feed.h:79
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
Definition: json_feed.h:146
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
Definition: json_feed.h:143
JSONFeed()
Constructor.
Definition: json_feed.cc:43
static const int FEED_OK_ST
Found opening and closing brace or square bracket.
Definition: json_feed.h:128
static const int C_COMMENT_ST
Skipping a C style comment.
Definition: json_feed.h:115
static const int C_COMMENT_BEFORE_JSON_ST
Skipping a C style comment before actual JSON.
Definition: json_feed.h:91
static const int JSON_END_ST
Found last closing brace or square bracket.
Definition: json_feed.h:121
static ElementPtr fromWire(std::stringstream &in, int length)
These function pparse the wireformat at the given stringstream (of the given length).
Definition: data.cc:992
Implements a finite state machine.
Definition: state_model.h:274
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:186
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
virtual void defineEvents()
Populates the set of events.
Definition: state_model.cc:229
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
void defineState(unsigned int value, const std::string &label, StateHandler handler, const StatePausing &state_pausing=STATE_PAUSE_NEVER)
Adds an state value and associated label to the set of states.
Definition: state_model.cc:196
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
Definition: state_model.cc:170
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
Definition: state_model.cc:264
virtual void verifyEvents()
Validates the contents of the set of events.
Definition: state_model.cc:237
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:298
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:245
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:291
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:367
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
string & output_
Definition: dns/message.cc:883
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< Element > ElementPtr
Definition: data.h:28
Defines the logger used by the top-level component of kea-lfc.