Kea  1.5.0
json_feed.cc
Go to the documentation of this file.
1 // Copyright (C) 2017-2018 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 <boost/bind.hpp>
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::JSON_START_ST;
22 const int JSONFeed::INNER_JSON_ST;
23 const int JSONFeed::STRING_JSON_ST;
24 const int JSONFeed::ESCAPE_JSON_ST;
25 const int JSONFeed::JSON_END_ST;
26 const int JSONFeed::FEED_OK_ST;
27 const int JSONFeed::FEED_FAILED_ST;
28 
29 const int JSONFeed::DATA_READ_OK_EVT;
30 const int JSONFeed::NEED_MORE_DATA_EVT;
31 const int JSONFeed::MORE_DATA_PROVIDED_EVT;
32 const int JSONFeed::FEED_OK_EVT;
33 const int JSONFeed::FEED_FAILED_EVT;
34 
35 JSONFeed::JSONFeed()
36  : StateModel(), buffer_(), data_ptr_(0), error_message_(), open_scopes_(0),
37  output_() {
38 }
39 
40 void
42  // Initialize dictionaries of events and states.
44 
45  // Set the current state to starting state and enter the run loop.
47 
48  // Parsing starts from here.
50 }
51 
52 void
54  try {
55  // Process the input data until no more data is available or until
56  // JSON feed ends with matching closing brace.
57  do {
58  getState(getCurrState())->run();
59 
60  } while (!isModelDone() && (getNextEvent() != NOP_EVT) &&
62  } catch (const std::exception& ex) {
63  abortModel(ex.what());
64  }
65 }
66 
67 bool
69  return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
70  (getNextEvent() == START_EVT));
71 }
72 
73 bool
75  return ((getNextEvent() == END_EVT) &&
76  (getLastEvent() == FEED_OK_EVT));
77 }
78 
81  if (needData()) {
82  isc_throw(JSONFeedError, "unable to retrieve the data form the"
83  " JSON feed while parsing hasn't finished");
84  }
85  try {
86  return (Element::fromWire(output_));
87 
88  } catch (const std::exception& ex) {
90  }
91 }
92 
93 void
94 JSONFeed::postBuffer(const void* buf, const size_t buf_size) {
95  if (buf_size > 0) {
96  // The next event is NEED_MORE_DATA_EVT when the parser wants to
97  // signal that more data is needed. This method is called to supply
98  // more data and thus it should change the next event to
99  // MORE_DATA_PROVIDED_EVT.
100  if (getNextEvent() == NEED_MORE_DATA_EVT) {
102  }
103  buffer_.assign(static_cast<const char*>(buf),
104  static_cast<const char*>(buf) + buf_size);
105  data_ptr_ = 0;
106  }
107 }
108 
109 void
110 JSONFeed::defineEvents() {
111  StateModel::defineEvents();
112 
113  // Define JSONFeed specific events.
114  defineEvent(DATA_READ_OK_EVT, "DATA_READ_OK_EVT");
115  defineEvent(NEED_MORE_DATA_EVT, "NEED_MORE_DATA_EVT");
116  defineEvent(MORE_DATA_PROVIDED_EVT, "MORE_DATA_PROVIDED_EVT");
117  defineEvent(FEED_OK_EVT, "FEED_OK_EVT");
118  defineEvent(FEED_FAILED_EVT, "FEED_FAILED_EVT");
119 }
120 
121 void
122 JSONFeed::verifyEvents() {
123  StateModel::verifyEvents();
124 
130 }
131 
132 void
133 JSONFeed::defineStates() {
134  // Call parent class implementation first.
135  StateModel::defineStates();
136 
137  defineState(RECEIVE_START_ST, "RECEIVE_START_ST",
138  boost::bind(&JSONFeed::receiveStartHandler, this));
139  defineState(WHITESPACE_BEFORE_JSON_ST, "WHITESPACE_BEFORE_JSON_ST",
140  boost::bind(&JSONFeed::whiteSpaceBeforeJSONHandler, this));
141  defineState(INNER_JSON_ST, "INNER_JSON_ST",
142  boost::bind(&JSONFeed::innerJSONHandler, this));
143  defineState(STRING_JSON_ST, "STRING_JSON_ST",
144  boost::bind(&JSONFeed::stringJSONHandler, this));
145  defineState(ESCAPE_JSON_ST, "ESCAPE_JSON_ST",
146  boost::bind(&JSONFeed::escapeJSONHandler, this));
147  defineState(JSON_END_ST, "JSON_END_ST",
148  boost::bind(&JSONFeed::endJSONHandler, this));
149 }
150 
151 void
152 JSONFeed::feedFailure(const std::string& error_msg) {
153  error_message_ = error_msg;
155 }
156 
157 void
158 JSONFeed::onModelFailure(const std::string& explanation) {
159  if (error_message_.empty()) {
160  error_message_ = explanation;
161  }
162 }
163 
164 bool
165 JSONFeed::popNextFromBuffer(char& next) {
166  // If there are any characters in the buffer, pop next.
167  if (!buffer_.empty() && (data_ptr_ < buffer_.size())) {
168  next = buffer_[data_ptr_++];
169  return (true);
170  }
171  return (false);
172 }
173 
174 char
175 JSONFeed::getNextFromBuffer() {
176  unsigned int ev = getNextEvent();
177  char c = '\0';
178  // The caller should always provide additional data when the
179  // NEED_MORE_DATA_EVT occurs. If the next event is still
180  // NEED_MORE_DATA_EVT it indicates that the caller hasn't provided
181  // the data.
182  if (ev == NEED_MORE_DATA_EVT) {
183  isc_throw(JSONFeedError,
184  "JSONFeed requires new data to progress, but no data"
185  " have been provided. The transaction is aborted to avoid"
186  " a deadlock.");
187 
188  } else {
189  // Try to pop next character from the buffer.
190  const bool data_exist = popNextFromBuffer(c);
191  if (!data_exist) {
192  // There is no more data so it is really not possible that we're
193  // at MORE_DATA_PROVIDED_EVT.
194  if (ev == MORE_DATA_PROVIDED_EVT) {
195  isc_throw(JSONFeedError,
196  "JSONFeed state indicates that new data have been"
197  " provided to be parsed, but the transaction buffer"
198  " contains no new data.");
199 
200  } else {
201  // If there is no more data we should set NEED_MORE_DATA_EVT
202  // event to indicate that new data should be provided.
204  }
205  }
206  }
207  return (c);
208 }
209 
210 void
211 JSONFeed::invalidEventError(const std::string& handler_name,
212  const unsigned int event) {
213  isc_throw(JSONFeedError, handler_name << ": "
214  << " invalid event " << getEventLabel(static_cast<int>(event)));
215 }
216 
217 void
218 JSONFeed::receiveStartHandler() {
219  char c = getNextFromBuffer();
220  if (getNextEvent() != NEED_MORE_DATA_EVT) {
221  switch (getNextEvent()) {
222  case START_EVT:
223  switch (c) {
224  case '\t':
225  case '\n':
226  case '\r':
227  case ' ':
229  return;
230 
231  case '{':
232  case '[':
233  output_.push_back(c);
234  ++open_scopes_;
236  return;
237 
238  // Cannot start by a string
239  case '"':
240  default:
241  feedFailure("invalid first character " + std::string(1, c));
242  break;
243  }
244  break;
245 
246  default:
247  invalidEventError("receiveStartHandler", getNextEvent());
248  }
249  }
250 }
251 
252 void
253 JSONFeed::whiteSpaceBeforeJSONHandler() {
254  char c = getNextFromBuffer();
255  if (getNextEvent() != NEED_MORE_DATA_EVT) {
256  switch (c) {
257  case '\t':
258  case '\n':
259  case '\r':
260  case ' ':
262  break;
263 
264  case '{':
265  case '[':
266  output_.push_back(c);
267  ++open_scopes_;
269  break;
270 
271  // Cannot start by a string
272  case '"':
273  default:
274  feedFailure("invalid character " + std::string(1, c));
275  }
276  }
277 }
278 
279 void
280 JSONFeed::innerJSONHandler() {
281  char c = getNextFromBuffer();
282  if (getNextEvent() != NEED_MORE_DATA_EVT) {
283  output_.push_back(c);
284 
285  switch(c) {
286  case '{':
287  case '[':
289  ++open_scopes_;
290  break;
291 
292  case '}':
293  case ']':
294  if (--open_scopes_ == 0) {
296 
297  } else {
299  }
300  break;
301 
302  case '"':
304  break;
305 
306  default:
308  }
309  }
310 }
311 
312 void
313 JSONFeed::stringJSONHandler() {
314  char c = getNextFromBuffer();
315  if (getNextEvent() != NEED_MORE_DATA_EVT) {
316  output_.push_back(c);
317 
318  switch(c) {
319  case '"':
321  break;
322 
323  case '\\':
325  break;
326 
327  default:
329  }
330  }
331 }
332 
333 void
334 JSONFeed::escapeJSONHandler() {
335  char c = getNextFromBuffer();
336  if (getNextEvent() != NEED_MORE_DATA_EVT) {
337  output_.push_back(c);
338 
340  }
341 }
342 
343 void
344 JSONFeed::endJSONHandler() {
345  switch (getNextEvent()) {
346  case FEED_OK_EVT:
348  break;
349 
350  case FEED_FAILED_EVT:
351  abortModel("reading into JSON feed failed");
352  break;
353 
354  default:
355  invalidEventError("endJSONHandler", getNextEvent());
356  }
357 }
358 
359 
360 } // end of namespace config
361 } // 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:194
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:289
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
Definition: json_feed.h:116
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:365
void postBuffer(const void *buf, const size_t buf_size)
Receives additional data read from a data stream.
Definition: json_feed.cc:94
Implements a finite state machine.
Definition: state_model.h:271
static const int FEED_FAILED_EVT
Invalid syntax detected.
Definition: json_feed.h:128
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:168
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:385
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:211
static const int WHITESPACE_BEFORE_JSON_ST
Skipping whitespaces before actual JSON.
Definition: json_feed.h:79
static const int RECEIVE_START_ST
State indicating a beginning of a feed.
Definition: json_feed.h:76
bool needData() const
Checks if the model needs additional data to continue.
Definition: json_feed.cc:68
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:272
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:292
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:281
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
Definition: json_feed.h:122
A generic exception thrown upon an error in the JSONFeed.
Definition: json_feed.h:30
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:341
static const int STRING_JSON_ST
Parsing JSON string.
Definition: json_feed.h:88
#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:85
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
Definition: json_feed.h:119
static const int ESCAPE_JSON_ST
JSON escape next character.
Definition: json_feed.h:91
static const int FEED_OK_EVT
Found opening brace and the matching closing brace.
Definition: json_feed.h:125
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:346
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:331
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:256
static const int JSON_END_ST
Found last closing brace or square bracket.
Definition: json_feed.h:94
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:146
data::ElementPtr toElement() const
Returns processed data as a structure of isc::data::Element objects.
Definition: json_feed.cc:80
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:279
bool feedOk() const
Checks if the data have been successfully processed.
Definition: json_feed.cc:74
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:184
void poll()
Runs the model as long as data is available.
Definition: json_feed.cc:53
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:295
void initModel()
Initializes state model.
Definition: json_feed.cc:41
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:304
static const int FEED_FAILED_ST
Invalid syntax detected.
Definition: json_feed.h:106