summaryrefslogtreecommitdiffstats
path: root/src/lib/http/http_message_parser_base.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 14:53:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-21 14:53:22 +0000
commit52c021ee0b0c6ad2128ed550c694aad0d11d4c3f (patch)
tree83cf8627b94336cf4bee7479b9749263bbfd3a06 /src/lib/http/http_message_parser_base.cc
parentInitial commit. (diff)
downloadisc-kea-52c021ee0b0c6ad2128ed550c694aad0d11d4c3f.tar.xz
isc-kea-52c021ee0b0c6ad2128ed550c694aad0d11d4c3f.zip
Adding upstream version 2.5.7.upstream/2.5.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/http/http_message_parser_base.cc')
-rw-r--r--src/lib/http/http_message_parser_base.cc307
1 files changed, 307 insertions, 0 deletions
diff --git a/src/lib/http/http_message_parser_base.cc b/src/lib/http/http_message_parser_base.cc
new file mode 100644
index 0000000..000e343
--- /dev/null
+++ b/src/lib/http/http_message_parser_base.cc
@@ -0,0 +1,307 @@
+// Copyright (C) 2017-2020 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 <http/http_message_parser_base.h>
+#include <functional>
+#include <sstream>
+
+using namespace isc::util;
+
+namespace isc {
+namespace http {
+
+const int HttpMessageParserBase::HTTP_PARSE_OK_ST;
+const int HttpMessageParserBase::HTTP_PARSE_FAILED_ST;
+
+const int HttpMessageParserBase::DATA_READ_OK_EVT;
+const int HttpMessageParserBase::NEED_MORE_DATA_EVT;
+const int HttpMessageParserBase::MORE_DATA_PROVIDED_EVT;
+const int HttpMessageParserBase::HTTP_PARSE_OK_EVT;
+const int HttpMessageParserBase::HTTP_PARSE_FAILED_EVT;
+
+
+HttpMessageParserBase::HttpMessageParserBase(HttpMessage& message)
+ : StateModel(), message_(message), buffer_(), buffer_pos_(0),
+ error_message_() {
+}
+
+void
+HttpMessageParserBase::poll() {
+ try {
+ // Run the parser until it runs out of input data or until
+ // parsing completes.
+ do {
+ getState(getCurrState())->run();
+
+ } while (!isModelDone() && (getNextEvent() != NOP_EVT) &&
+ (getNextEvent() != NEED_MORE_DATA_EVT));
+ } catch (const std::exception& ex) {
+ abortModel(ex.what());
+ }
+}
+
+bool
+HttpMessageParserBase::needData() const {
+ return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
+ (getNextEvent() == START_EVT));
+}
+
+bool
+HttpMessageParserBase::httpParseOk() const {
+ return ((getNextEvent() == END_EVT) &&
+ (getLastEvent() == HTTP_PARSE_OK_EVT));
+}
+
+void
+HttpMessageParserBase::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_.insert(buffer_.end(), static_cast<const char*>(buf),
+ static_cast<const char*>(buf) + buf_size);
+ }
+}
+
+std::string
+HttpMessageParserBase::getBufferAsString(const size_t limit) const {
+ std::string message(buffer_.begin(), buffer_.end());
+ return (logFormatHttpMessage(message, limit));
+}
+
+std::string
+HttpMessageParserBase::logFormatHttpMessage(const std::string& message,
+ const size_t limit) {
+ if ((limit > 0) && !message.empty()) {
+ if (limit < message.size()) {
+ std::ostringstream s;
+ s << message.substr(0, limit)
+ << ".........\n(truncating HTTP message larger than "
+ << limit << " characters)\n";
+ return (s.str());
+ }
+ }
+
+ // Return original message if it is empty or does not exceed the
+ // limit.
+ return (message);
+}
+
+
+void
+HttpMessageParserBase::defineEvents() {
+ StateModel::defineEvents();
+
+ // Define HTTP parser 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(HTTP_PARSE_OK_EVT, "HTTP_PARSE_OK_EVT");
+ defineEvent(HTTP_PARSE_FAILED_EVT, "HTTP_PARSE_FAILED_EVT");
+}
+
+void
+HttpMessageParserBase::verifyEvents() {
+ StateModel::verifyEvents();
+
+ getEvent(DATA_READ_OK_EVT);
+ getEvent(NEED_MORE_DATA_EVT);
+ getEvent(MORE_DATA_PROVIDED_EVT);
+ getEvent(HTTP_PARSE_OK_EVT);
+ getEvent(HTTP_PARSE_FAILED_EVT);
+}
+
+void
+HttpMessageParserBase::defineStates() {
+ // Call parent class implementation first.
+ StateModel::defineStates();
+
+ defineState(HTTP_PARSE_OK_ST, "HTTP_PARSE_OK_ST",
+ std::bind(&HttpMessageParserBase::parseEndedHandler, this));
+
+ defineState(HTTP_PARSE_FAILED_ST, "HTTP_PARSE_FAILED_ST",
+ std::bind(&HttpMessageParserBase::parseEndedHandler, this));
+}
+
+void
+HttpMessageParserBase::stateWithReadHandler(const std::string& handler_name,
+ std::function<void(const char c)>
+ after_read_logic) {
+ std::string bytes;
+ getNextFromBuffer(bytes);
+ // Do nothing if we reached the end of buffer.
+ if (getNextEvent() != NEED_MORE_DATA_EVT) {
+ switch(getNextEvent()) {
+ case DATA_READ_OK_EVT:
+ case MORE_DATA_PROVIDED_EVT:
+ after_read_logic(bytes[0]);
+ break;
+ default:
+ invalidEventError(handler_name, getNextEvent());
+ }
+ }
+}
+
+void
+HttpMessageParserBase::stateWithMultiReadHandler(const std::string& handler_name,
+ std::function<void(const std::string&)>
+ after_read_logic) {
+ std::string bytes;
+ getNextFromBuffer(bytes, 0);
+ // Do nothing if we reached the end of buffer.
+ if (getNextEvent() != NEED_MORE_DATA_EVT) {
+ switch(getNextEvent()) {
+ case DATA_READ_OK_EVT:
+ case MORE_DATA_PROVIDED_EVT:
+ after_read_logic(bytes);
+ break;
+ default:
+ invalidEventError(handler_name, getNextEvent());
+ }
+ }
+}
+
+void
+HttpMessageParserBase::parseFailure(const std::string& error_msg) {
+ error_message_ = error_msg + " : " + getContextStr();
+ transition(HTTP_PARSE_FAILED_ST, HTTP_PARSE_FAILED_EVT);
+}
+
+void
+HttpMessageParserBase::onModelFailure(const std::string& explanation) {
+ if (error_message_.empty()) {
+ error_message_ = explanation;
+ }
+}
+
+void
+HttpMessageParserBase::getNextFromBuffer(std::string& bytes, const size_t limit) {
+ unsigned int ev = getNextEvent();
+ bytes = "\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(HttpParseError,
+ "HTTP request parser requires new data to progress, but no data"
+ " have been provided. The transaction is aborted to avoid"
+ " a deadlock. This is a Kea HTTP server logic error!");
+
+ } else {
+ // Try to retrieve characters from the buffer.
+ const bool data_exist = popNextFromBuffer(bytes, limit);
+ 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(HttpParseError,
+ "HTTP server state indicates that new data have been"
+ " provided to be parsed, but the transaction buffer"
+ " contains no new data. This is a Kea HTTP server logic"
+ " error!");
+
+ } else {
+ // If there is no more data we should set NEED_MORE_DATA_EVT
+ // event to indicate that new data should be provided.
+ postNextEvent(NEED_MORE_DATA_EVT);
+ }
+ }
+ }
+}
+
+void
+HttpMessageParserBase::invalidEventError(const std::string& handler_name,
+ const unsigned int event) {
+ isc_throw(HttpParseError, handler_name << ": invalid event "
+ << getEventLabel(static_cast<int>(event)));
+}
+
+void
+HttpMessageParserBase::parseEndedHandler() {
+ switch(getNextEvent()) {
+ case HTTP_PARSE_OK_EVT:
+ message_.finalize();
+ transition(END_ST, END_EVT);
+ break;
+ case HTTP_PARSE_FAILED_EVT:
+ abortModel("HTTP message parsing failed");
+ break;
+
+ default:
+ invalidEventError("parseEndedHandler", getNextEvent());
+ }
+}
+
+bool
+HttpMessageParserBase::popNextFromBuffer(std::string& next, const size_t limit) {
+ // If there are any characters in the buffer, pop next.
+ if (buffer_pos_ < buffer_.size()) {
+ next = buffer_.substr(buffer_pos_, limit == 0 ? std::string::npos : limit);
+
+ if (limit > 0) {
+ buffer_pos_ += limit;
+ }
+
+ if ((buffer_pos_ > buffer_.size()) || (limit == 0)) {
+ buffer_pos_ = buffer_.size();
+ }
+ return (true);
+ }
+ return (false);
+}
+
+bool
+HttpMessageParserBase::isChar(const char c) const {
+ // was (c >= 0) && (c <= 127)
+ return (c >= 0);
+}
+
+bool
+HttpMessageParserBase::isCtl(const char c) const {
+ return (((c >= 0) && (c <= 31)) || (c == 127));
+}
+
+bool
+HttpMessageParserBase::isSpecial(const char c) const {
+ switch (c) {
+ case '(':
+ case ')':
+ case '<':
+ case '>':
+ case '@':
+ case ',':
+ case ';':
+ case ':':
+ case '\\':
+ case '"':
+ case '/':
+ case '[':
+ case ']':
+ case '?':
+ case '=':
+ case '{':
+ case '}':
+ case ' ':
+ case '\t':
+ return true;
+
+ default:
+ ;
+ }
+
+ return false;
+}
+
+
+} // end of namespace isc::http
+} // end of namespace isc