diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 12:15:43 +0000 |
commit | f5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch) | |
tree | 49e44c6f87febed37efb953ab5485aa49f6481a7 /src/lib/cc/json_feed.cc | |
parent | Initial commit. (diff) | |
download | isc-kea-upstream.tar.xz isc-kea-upstream.zip |
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/cc/json_feed.cc')
-rw-r--r-- | src/lib/cc/json_feed.cc | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/src/lib/cc/json_feed.cc b/src/lib/cc/json_feed.cc new file mode 100644 index 0000000..7cd1e31 --- /dev/null +++ b/src/lib/cc/json_feed.cc @@ -0,0 +1,563 @@ +// 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/. + +#include <config.h> + +#include <cc/data.h> +#include <cc/json_feed.h> +#include <functional> + +using namespace isc::data; +using namespace isc::util; + +namespace isc { +namespace config { + +const int JSONFeed::RECEIVE_START_ST; +const int JSONFeed::WHITESPACE_BEFORE_JSON_ST; +const int JSONFeed::EOL_COMMENT_BEFORE_JSON_ST; +const int JSONFeed::START_COMMENT_BEFORE_JSON_ST; +const int JSONFeed::C_COMMENT_BEFORE_JSON_ST; +const int JSONFeed::STOP_COMMENT_BEFORE_JSON_ST; +const int JSONFeed::JSON_START_ST; +const int JSONFeed::INNER_JSON_ST; +const int JSONFeed::STRING_JSON_ST; +const int JSONFeed::ESCAPE_JSON_ST; +const int JSONFeed::EOL_COMMENT_ST; +const int JSONFeed::START_COMMENT_ST; +const int JSONFeed::C_COMMENT_ST; +const int JSONFeed::STOP_COMMENT_ST; +const int JSONFeed::JSON_END_ST; +const int JSONFeed::FEED_OK_ST; +const int JSONFeed::FEED_FAILED_ST; + +const int JSONFeed::DATA_READ_OK_EVT; +const int JSONFeed::NEED_MORE_DATA_EVT; +const int JSONFeed::MORE_DATA_PROVIDED_EVT; +const int JSONFeed::FEED_OK_EVT; +const int JSONFeed::FEED_FAILED_EVT; + +JSONFeed::JSONFeed() + : StateModel(), buffer_(), data_ptr_(0), error_message_(), open_scopes_(0), + output_() { +} + +void +JSONFeed::initModel() { + // Initialize dictionaries of events and states. + initDictionaries(); + + // Set the current state to starting state and enter the run loop. + setState(RECEIVE_START_ST); + + // Parsing starts from here. + postNextEvent(START_EVT); +} + +void +JSONFeed::poll() { + try { + // Process the input data until no more data is available or until + // JSON feed ends with matching closing brace. + do { + getState(getCurrState())->run(); + + } while (!isModelDone() && (getNextEvent() != NOP_EVT) && + (getNextEvent() != NEED_MORE_DATA_EVT)); + } catch (const std::exception& ex) { + abortModel(ex.what()); + } +} + +bool +JSONFeed::needData() const { + return ((getNextEvent() == NEED_MORE_DATA_EVT) || + (getNextEvent() == START_EVT)); +} + +bool +JSONFeed::feedOk() const { + return ((getNextEvent() == END_EVT) && + (getLastEvent() == FEED_OK_EVT)); +} + +ElementPtr +JSONFeed::toElement() const { + if (needData()) { + isc_throw(JSONFeedError, "unable to retrieve the data form the" + " JSON feed while parsing hasn't finished"); + } + try { + return (Element::fromWire(output_)); + + } catch (const std::exception& ex) { + isc_throw(JSONFeedError, ex.what()); + } +} + +void +JSONFeed::postBuffer(const void* buf, const size_t buf_size) { + if (buf_size > 0) { + // The next event is NEED_MORE_DATA_EVT when the parser wants to + // signal that more data is needed. This method is called to supply + // more data and thus it should change the next event to + // MORE_DATA_PROVIDED_EVT. + if (getNextEvent() == NEED_MORE_DATA_EVT) { + transition(getCurrState(), MORE_DATA_PROVIDED_EVT); + } + buffer_.assign(static_cast<const char*>(buf), + static_cast<const char*>(buf) + buf_size); + data_ptr_ = 0; + } +} + +void +JSONFeed::defineEvents() { + StateModel::defineEvents(); + + // Define JSONFeed specific events. + defineEvent(DATA_READ_OK_EVT, "DATA_READ_OK_EVT"); + defineEvent(NEED_MORE_DATA_EVT, "NEED_MORE_DATA_EVT"); + defineEvent(MORE_DATA_PROVIDED_EVT, "MORE_DATA_PROVIDED_EVT"); + defineEvent(FEED_OK_EVT, "FEED_OK_EVT"); + defineEvent(FEED_FAILED_EVT, "FEED_FAILED_EVT"); +} + +void +JSONFeed::verifyEvents() { + StateModel::verifyEvents(); + + getEvent(DATA_READ_OK_EVT); + getEvent(NEED_MORE_DATA_EVT); + getEvent(MORE_DATA_PROVIDED_EVT); + getEvent(FEED_OK_EVT); + getEvent(FEED_FAILED_EVT); +} + +void +JSONFeed::defineStates() { + // Call parent class implementation first. + StateModel::defineStates(); + + defineState(RECEIVE_START_ST, "RECEIVE_START_ST", + std::bind(&JSONFeed::receiveStartHandler, this)); + defineState(WHITESPACE_BEFORE_JSON_ST, "WHITESPACE_BEFORE_JSON_ST", + std::bind(&JSONFeed::whiteSpaceBeforeJSONHandler, this)); + defineState(EOL_COMMENT_BEFORE_JSON_ST, "EOL_COMMENT_BEFORE_JSON_ST", + std::bind(&JSONFeed::eolCommentBeforeJSONHandler, this)); + defineState(START_COMMENT_BEFORE_JSON_ST, "START_COMMENT_BEFORE_JSON_ST", + std::bind(&JSONFeed::startCommentBeforeJSONHandler, this)); + defineState(C_COMMENT_BEFORE_JSON_ST, "C_COMMENT_BEFORE_JSON_ST", + std::bind(&JSONFeed::cCommentBeforeJSONHandler, this)); + defineState(STOP_COMMENT_BEFORE_JSON_ST, "STOP_COMMENT_BEFORE_JSON_ST", + std::bind(&JSONFeed::stopCommentBeforeJSONHandler, this)); + defineState(INNER_JSON_ST, "INNER_JSON_ST", + std::bind(&JSONFeed::innerJSONHandler, this)); + defineState(STRING_JSON_ST, "STRING_JSON_ST", + std::bind(&JSONFeed::stringJSONHandler, this)); + defineState(ESCAPE_JSON_ST, "ESCAPE_JSON_ST", + std::bind(&JSONFeed::escapeJSONHandler, this)); + defineState(EOL_COMMENT_ST, "EOL_COMMENT_ST", + std::bind(&JSONFeed::eolCommentHandler, this)); + defineState(START_COMMENT_ST, "START_COMMENT_ST", + std::bind(&JSONFeed::startCommentHandler, this)); + defineState(C_COMMENT_ST, "C_COMMENT_ST", + std::bind(&JSONFeed::cCommentHandler, this)); + defineState(STOP_COMMENT_ST, "STOP_COMMENT_ST", + std::bind(&JSONFeed::stopCommentHandler, this)); + defineState(JSON_END_ST, "JSON_END_ST", + std::bind(&JSONFeed::endJSONHandler, this)); +} + +void +JSONFeed::feedFailure(const std::string& error_msg) { + error_message_ = error_msg; + transition(FEED_FAILED_ST, FEED_FAILED_EVT); +} + +void +JSONFeed::onModelFailure(const std::string& explanation) { + if (error_message_.empty()) { + error_message_ = explanation; + } +} + +bool +JSONFeed::popNextFromBuffer(char& next) { + // If there are any characters in the buffer, pop next. + if (!buffer_.empty() && (data_ptr_ < buffer_.size())) { + next = buffer_[data_ptr_++]; + return (true); + } + return (false); +} + +char +JSONFeed::getNextFromBuffer() { + unsigned int ev = getNextEvent(); + char c = '\0'; + // The caller should always provide additional data when the + // NEED_MORE_DATA_EVT occurs. If the next event is still + // NEED_MORE_DATA_EVT it indicates that the caller hasn't provided + // the data. + if (ev == NEED_MORE_DATA_EVT) { + isc_throw(JSONFeedError, + "JSONFeed requires new data to progress, but no data" + " have been provided. The transaction is aborted to avoid" + " a deadlock."); + + } else { + // Try to pop next character from the buffer. + const bool data_exist = popNextFromBuffer(c); + if (!data_exist) { + // There is no more data so it is really not possible that we're + // at MORE_DATA_PROVIDED_EVT. + if (ev == MORE_DATA_PROVIDED_EVT) { + isc_throw(JSONFeedError, + "JSONFeed state indicates that new data have been" + " provided to be parsed, but the transaction buffer" + " contains no new data."); + + } else { + // If there is no more data we should set NEED_MORE_DATA_EVT + // event to indicate that new data should be provided. + transition(getCurrState(), NEED_MORE_DATA_EVT); + } + } + } + return (c); +} + +void +JSONFeed::invalidEventError(const std::string& handler_name, + const unsigned int event) { + isc_throw(JSONFeedError, handler_name << ": invalid event " + << getEventLabel(static_cast<int>(event))); +} + +void +JSONFeed::receiveStartHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (getNextEvent()) { + case START_EVT: + switch (c) { + case '\t': + case '\n': + case '\r': + case ' ': + transition(WHITESPACE_BEFORE_JSON_ST, DATA_READ_OK_EVT); + return; + + case '#': + transition(EOL_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + return; + + case '/': + transition(START_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + return; + + case '{': + case '[': + output_.push_back(c); + ++open_scopes_; + transition(INNER_JSON_ST, DATA_READ_OK_EVT); + return; + + // Cannot start by a string + case '"': + default: + feedFailure("invalid first character " + std::string(1, c)); + break; + } + break; + + default: + invalidEventError("receiveStartHandler", getNextEvent()); + } + } +} + +void +JSONFeed::whiteSpaceBeforeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '\t': + case '\n': + case '\r': + case ' ': + transition(getCurrState(), DATA_READ_OK_EVT); + break; + + case '#': + transition(EOL_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + return; + + case '/': + transition(START_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + return; + + case '{': + case '[': + output_.push_back(c); + ++open_scopes_; + transition(INNER_JSON_ST, DATA_READ_OK_EVT); + break; + + // Cannot start by a string + case '"': + default: + feedFailure("invalid character " + std::string(1, c)); + } + } +} + +void +JSONFeed::eolCommentBeforeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '\n': + transition(WHITESPACE_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + + default: + postNextEvent(DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::startCommentBeforeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '/': + transition(EOL_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + + case '*': + transition(C_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + + default: + feedFailure("invalid characters /" + std::string(1, c)); + } + } +} + +void +JSONFeed::cCommentBeforeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '*': + transition(STOP_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + + default: + postNextEvent(DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::stopCommentBeforeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '/': + transition(WHITESPACE_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + + case '*': + transition(getCurrState(), DATA_READ_OK_EVT); + break; + + default: + transition(C_COMMENT_BEFORE_JSON_ST, DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::innerJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch(c) { + case '{': + case '[': + output_.push_back(c); + transition(getCurrState(), DATA_READ_OK_EVT); + ++open_scopes_; + break; + + case '}': + case ']': + output_.push_back(c); + if (--open_scopes_ == 0) { + transition(JSON_END_ST, FEED_OK_EVT); + + } else { + postNextEvent(DATA_READ_OK_EVT); + } + break; + + case '"': + output_.push_back(c); + transition(STRING_JSON_ST, DATA_READ_OK_EVT); + break; + + case '#': + transition(EOL_COMMENT_ST, DATA_READ_OK_EVT); + break; + + case '/': + transition(START_COMMENT_ST, DATA_READ_OK_EVT); + break; + + default: + output_.push_back(c); + postNextEvent(DATA_READ_OK_EVT); + } + } +} + +void +JSONFeed::stringJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + output_.push_back(c); + + switch(c) { + case '"': + transition(INNER_JSON_ST, DATA_READ_OK_EVT); + break; + + case '\\': + transition(ESCAPE_JSON_ST, DATA_READ_OK_EVT); + break; + + default: + transition(getCurrState(), DATA_READ_OK_EVT); + } + } +} + +void +JSONFeed::escapeJSONHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + output_.push_back(c); + + transition(STRING_JSON_ST, DATA_READ_OK_EVT); + } +} + +void +JSONFeed::eolCommentHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '\n': + output_.push_back(c); + transition(INNER_JSON_ST, DATA_READ_OK_EVT); + break; + + default: + postNextEvent(DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::startCommentHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '/': + transition(EOL_COMMENT_ST, DATA_READ_OK_EVT); + break; + + case '*': + transition(C_COMMENT_ST, DATA_READ_OK_EVT); + break; + + default: + feedFailure("invalid characters /" + std::string(1, c)); + } + } +} + +void +JSONFeed::cCommentHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '*': + transition(STOP_COMMENT_ST, DATA_READ_OK_EVT); + break; + + case '\n': + output_.push_back(c); + postNextEvent(DATA_READ_OK_EVT); + break; + + default: + postNextEvent(DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::stopCommentHandler() { + char c = getNextFromBuffer(); + if (getNextEvent() != NEED_MORE_DATA_EVT) { + switch (c) { + case '/': + transition(INNER_JSON_ST, DATA_READ_OK_EVT); + break; + + case '*': + transition(getCurrState(), DATA_READ_OK_EVT); + break; + + case '\n': + output_.push_back(c); + transition(C_COMMENT_ST, DATA_READ_OK_EVT); + break; + + default: + transition(C_COMMENT_ST, DATA_READ_OK_EVT); + break; + } + } +} + +void +JSONFeed::endJSONHandler() { + switch (getNextEvent()) { + case FEED_OK_EVT: + transition(END_ST, END_EVT); + break; + + case FEED_FAILED_EVT: + abortModel("reading into JSON feed failed"); + break; + + default: + invalidEventError("endJSONHandler", getNextEvent()); + } +} + +} // end of namespace config +} // end of namespace isc |