Kea  1.9.9-git
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 
13 using namespace isc::data;
14 using namespace isc::util;
15 
16 namespace isc {
17 namespace config {
18 
19 const int JSONFeed::RECEIVE_START_ST;
20 const int JSONFeed::WHITESPACE_BEFORE_JSON_ST;
21 const int JSONFeed::EOL_COMMENT_BEFORE_JSON_ST;
22 const int JSONFeed::START_COMMENT_BEFORE_JSON_ST;
23 const int JSONFeed::C_COMMENT_BEFORE_JSON_ST;
24 const int JSONFeed::STOP_COMMENT_BEFORE_JSON_ST;
25 const int JSONFeed::JSON_START_ST;
26 const int JSONFeed::INNER_JSON_ST;
27 const int JSONFeed::STRING_JSON_ST;
28 const int JSONFeed::ESCAPE_JSON_ST;
29 const int JSONFeed::EOL_COMMENT_ST;
30 const int JSONFeed::START_COMMENT_ST;
31 const int JSONFeed::C_COMMENT_ST;
32 const int JSONFeed::STOP_COMMENT_ST;
33 const int JSONFeed::JSON_END_ST;
34 const int JSONFeed::FEED_OK_ST;
35 const int JSONFeed::FEED_FAILED_ST;
36 
37 const int JSONFeed::DATA_READ_OK_EVT;
38 const int JSONFeed::NEED_MORE_DATA_EVT;
39 const int JSONFeed::MORE_DATA_PROVIDED_EVT;
40 const int JSONFeed::FEED_OK_EVT;
41 const int JSONFeed::FEED_FAILED_EVT;
42 
43 JSONFeed::JSONFeed()
44  : StateModel(), buffer_(), data_ptr_(0), error_message_(), open_scopes_(0),
45  output_() {
46 }
47 
48 void
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 
60 void
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 
75 bool
77  return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
78  (getNextEvent() == START_EVT));
79 }
80 
81 bool
83  return ((getNextEvent() == END_EVT) &&
84  (getLastEvent() == FEED_OK_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 
101 void
102 JSONFeed::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.
108  if (getNextEvent() == NEED_MORE_DATA_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 
117 void
118 JSONFeed::defineEvents() {
119  StateModel::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 
129 void
130 JSONFeed::verifyEvents() {
131  StateModel::verifyEvents();
132 
138 }
139 
140 void
141 JSONFeed::defineStates() {
142  // Call parent class implementation first.
143  StateModel::defineStates();
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 
175 void
176 JSONFeed::feedFailure(const std::string& error_msg) {
177  error_message_ = error_msg;
179 }
180 
181 void
182 JSONFeed::onModelFailure(const std::string& explanation) {
183  if (error_message_.empty()) {
184  error_message_ = explanation;
185  }
186 }
187 
188 bool
189 JSONFeed::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 
198 char
199 JSONFeed::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 
234 void
235 JSONFeed::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 
241 void
242 JSONFeed::receiveStartHandler() {
243  char c = getNextFromBuffer();
244  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
284 void
285 JSONFeed::whiteSpaceBeforeJSONHandler() {
286  char c = getNextFromBuffer();
287  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
319 void
320 JSONFeed::eolCommentBeforeJSONHandler() {
321  char c = getNextFromBuffer();
322  if (getNextEvent() != NEED_MORE_DATA_EVT) {
323  switch (c) {
324  case '\n':
326  break;
327 
328  default:
330  break;
331  }
332  }
333 }
334 
335 void
336 JSONFeed::startCommentBeforeJSONHandler() {
337  char c = getNextFromBuffer();
338  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
354 void
355 JSONFeed::cCommentBeforeJSONHandler() {
356  char c = getNextFromBuffer();
357  if (getNextEvent() != NEED_MORE_DATA_EVT) {
358  switch (c) {
359  case '*':
361  break;
362 
363  default:
365  break;
366  }
367  }
368 }
369 
370 void
371 JSONFeed::stopCommentBeforeJSONHandler() {
372  char c = getNextFromBuffer();
373  if (getNextEvent() != NEED_MORE_DATA_EVT) {
374  switch (c) {
375  case '/':
377  break;
378 
379  case '*':
381  break;
382 
383  default:
385  break;
386  }
387  }
388 }
389 
390 void
391 JSONFeed::innerJSONHandler() {
392  char c = getNextFromBuffer();
393  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
433 void
434 JSONFeed::stringJSONHandler() {
435  char c = getNextFromBuffer();
436  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
454 void
455 JSONFeed::escapeJSONHandler() {
456  char c = getNextFromBuffer();
457  if (getNextEvent() != NEED_MORE_DATA_EVT) {
458  output_.push_back(c);
459 
461  }
462 }
463 
464 void
465 JSONFeed::eolCommentHandler() {
466  char c = getNextFromBuffer();
467  if (getNextEvent() != NEED_MORE_DATA_EVT) {
468  switch (c) {
469  case '\n':
470  output_.push_back(c);
472  break;
473 
474  default:
476  break;
477  }
478  }
479 }
480 
481 void
482 JSONFeed::startCommentHandler() {
483  char c = getNextFromBuffer();
484  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
500 void
501 JSONFeed::cCommentHandler() {
502  char c = getNextFromBuffer();
503  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
521 void
522 JSONFeed::stopCommentHandler() {
523  char c = getNextFromBuffer();
524  if (getNextEvent() != NEED_MORE_DATA_EVT) {
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 
546 void
547 JSONFeed::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
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
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
Definition: json_feed.h:143
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 START_COMMENT_BEFORE_JSON_ST
Starting one of the comments beginning with a slash before actual JSON.
Definition: json_feed.h:88
Implements a finite state machine.
Definition: state_model.h:274
static const int FEED_FAILED_EVT
Invalid syntax detected.
Definition: json_feed.h:155
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
static const int EOL_COMMENT_BEFORE_JSON_ST
Skipping an end-of-line comment before actual JSON.
Definition: json_feed.h:85
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
static const int WHITESPACE_BEFORE_JSON_ST
Skipping whitespaces before actual JSON.
Definition: json_feed.h:82
static const int RECEIVE_START_ST
State indicating a beginning of a feed.
Definition: json_feed.h:79
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:367
boost::shared_ptr< Element > ElementPtr
Definition: data.h:20
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:291
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
Definition: json_feed.h:149
A generic exception thrown upon an error in the JSONFeed.
Definition: json_feed.h:30
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
static const int STRING_JSON_ST
Parsing JSON string.
Definition: json_feed.h:103
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition: edns.h:19
static const int INNER_JSON_ST
Parsing JSON.
Definition: json_feed.h:100
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 ESCAPE_JSON_ST
JSON escape next character.
Definition: json_feed.h:106
const OutputBuffer * buffer_
bool needData() const
Checks if the model needs additional data to continue.
Definition: json_feed.cc:76
static const int FEED_OK_EVT
Found opening brace and the matching closing brace.
Definition: json_feed.h:152
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
static const int C_COMMENT_BEFORE_JSON_ST
Skipping a C style comment before actual JSON.
Definition: json_feed.h:91
static const int START_COMMENT_ST
Starting one of the comments beginning with a slash.
Definition: json_feed.h:112
Defines the logger used by the top-level component of kea-dhcp-ddns.
string & output_
Definition: dns/message.cc:877
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
static const int STOP_COMMENT_BEFORE_JSON_ST
Stopping a C style comment before actual JSON.
Definition: json_feed.h:94
static const int C_COMMENT_ST
Skipping a C style comment.
Definition: json_feed.h:115
static const int JSON_END_ST
Found last closing brace or square bracket.
Definition: json_feed.h:121
bool feedOk() const
Checks if the data have been successfully processed.
Definition: json_feed.cc:82
static const int STOP_COMMENT_ST
Stopping a C style comment.
Definition: json_feed.h:118
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
data::ElementPtr toElement() const
Returns processed data as a structure of isc::data::Element objects.
Definition: json_feed.cc:88
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
static const int EOL_COMMENT_ST
Skipping an end-of-line comment.
Definition: json_feed.h:109
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:186
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
void poll()
Runs the model as long as data is available.
Definition: json_feed.cc:61
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:298
void initModel()
Initializes state model.
Definition: json_feed.cc:49
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
static const int FEED_FAILED_ST
Invalid syntax detected.
Definition: json_feed.h:133