Kea 2.7.1
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.
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
void initDictionaries()
Initializes the event and state dictionaries.
bool isModelDone() const
Returns whether or not the model has finished execution.
virtual void defineEvents()
Populates the set of events.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
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.
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
unsigned int getNextEvent() const
Fetches the model's next event.
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
virtual void verifyEvents()
Validates the contents of the set of events.
static const int END_EVT
Event issued to end the model execution.
static const int NOP_EVT
Signifies that no event has occurred.
static const int START_EVT
Event issued to start the model execution.
void abortModel(const std::string &explanation)
Aborts model execution.
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
virtual void defineStates()
Populates the set of states.
void setState(unsigned int state)
Sets the current state to the given state value.
static const int END_ST
Final state, all the state model has reached its conclusion.
unsigned int getLastEvent() const
Fetches the model's last event.
unsigned int getCurrState() const
Fetches the model's current state.
string & output_
#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.