libkea_http_la_SOURCES += http_acceptor.h
libkea_http_la_SOURCES += http_header.cc http_header.h
libkea_http_la_SOURCES += http_message.cc http_message.h
+libkea_http_la_SOURCES += http_message_parser_base.cc http_message_parser_base.h
libkea_http_la_SOURCES += http_types.h
libkea_http_la_SOURCES += listener.cc listener.h
libkea_http_la_SOURCES += post_request.cc post_request.h
--- /dev/null
+// Copyright (C) 2017 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 <http/http_message_parser_base.h>
+#include <boost/bind.hpp>
+
+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_(), 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);
+ }
+}
+
+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",
+ boost::bind(&HttpMessageParserBase::parseEndedHandler, this));
+
+ defineState(HTTP_PARSE_FAILED_ST, "HTTP_PARSE_FAILED_ST",
+ boost::bind(&HttpMessageParserBase::parseEndedHandler, this));
+}
+
+void
+HttpMessageParserBase::stateWithReadHandler(const std::string& handler_name,
+ boost::function<void(const char c)>
+ after_read_logic) {
+ char c = getNextFromBuffer();
+ // 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(c);
+ 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;
+ }
+}
+
+char
+HttpMessageParserBase::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(HttpMessageParserBaseError,
+ "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 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(HttpMessageParserBaseError,
+ "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.
+ transition(getCurrState(), NEED_MORE_DATA_EVT);
+ }
+ }
+ }
+ return (c);
+}
+
+void
+HttpMessageParserBase::invalidEventError(const std::string& handler_name,
+ const unsigned int event) {
+ isc_throw(HttpMessageParserBaseError, 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 request parsing failed");
+ break;
+
+ default:
+ invalidEventError("parseEndedHandler", getNextEvent());
+ }
+}
+
+bool
+HttpMessageParserBase::popNextFromBuffer(char& next) {
+ // If there are any characters in the buffer, pop next.
+ if (!buffer_.empty()) {
+ next = buffer_.front();
+ buffer_.pop_front();
+ 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
--- /dev/null
+// Copyright (C) 2017 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/.
+
+#ifndef HTTP_MESSAGE_PARSER_BASE_H
+#define HTTP_MESSAGE_PARSER_BASE_H
+
+#include <exceptions/exceptions.h>
+#include <http/http_message.h>
+#include <util/state_model.h>
+#include <boost/function.hpp>
+#include <list>
+#include <string>
+
+namespace isc {
+namespace http {
+
+/// @brief Exception thrown when an error during parsing HTTP message
+/// has occurred.
+///
+/// The most common errors are due to receiving malformed requests.
+class HttpMessageParserBaseError : public Exception {
+public:
+ HttpMessageParserBaseError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) { };
+};
+
+/// @brief Base class for the HTTP message parsers.
+///
+/// This is a base class for @c HttpRequestParser and @c HttpResponseParser
+/// classes. It provides common states, events and functionality for processing
+/// the received HTTP messages.
+///
+/// This class must not be used directly. Instead, an instance of the
+/// derived class should be used.
+///
+/// HTTP uses TCP as a transport which is asynchronous in nature, i.e. the
+/// HTTP message is received in chunks and multiple TCP connections can be
+/// established at the same time. Multiplexing between these connections
+/// requires providing a separate state machine per connection to "remember"
+/// the state of each transaction when the parser is waiting for asynchronous
+/// data to be delivered. While the parser is waiting for the data, it can
+/// parse requests received over other connections. This class provides means
+/// for parsing partial data received over the specific connection and
+/// interrupting data parsing to switch to a different context.
+///
+/// A new method @ref HttpMessageParserBase::poll has been created to run the
+/// parser's state machine as long as there are unparsed data in the parser's
+/// internal buffer. This method returns control to the caller when the parser
+/// runs out of data in this buffer. The caller must feed the buffer by calling
+/// @ref HttpMessageParserBase::postBuffer and then run
+/// @ref HttpMessageParserBase::poll again.
+///
+/// In case, the caller provides more data than indicated by the "Content-Length"
+/// header the parser will return from @c poll() after parsing the data which
+/// constitute the HTTP request and not parse the extraneous data. The caller
+/// should test the @ref HttpMessageParserBase::needData and
+/// @ref HttpMessageParserBase::httpParseOk to determine whether parsing has
+/// completed.
+///
+/// The @ref util::StateModel::runModel must not be used to run the parser
+/// state machine, thus it is made private method.
+class HttpMessageParserBase : public util::StateModel {
+public:
+
+ /// @name States supported by the HttpMessageParserBase.
+ ///
+ //@{
+
+ /// @brief Parsing successfully completed.
+ static const int HTTP_PARSE_OK_ST = SM_DERIVED_STATE_MIN + 1000;
+
+ /// @brief Parsing failed.
+ static const int HTTP_PARSE_FAILED_ST = SM_DERIVED_STATE_MIN + 1001;
+
+ //@}
+
+ /// @name Events used during HTTP message parsing.
+ ///
+ //@{
+
+ /// @brief Chunk of data successfully read and parsed.
+ static const int DATA_READ_OK_EVT = SM_DERIVED_EVENT_MIN + 1;
+
+ /// @brief Unable to proceed with parsing until new data is provided.
+ static const int NEED_MORE_DATA_EVT = SM_DERIVED_EVENT_MIN + 2;
+
+ /// @brief New data provided and parsing should continue.
+ static const int MORE_DATA_PROVIDED_EVT = SM_DERIVED_EVENT_MIN + 3;
+
+ /// @brief Parsing HTTP request successful.
+ static const int HTTP_PARSE_OK_EVT = SM_DERIVED_EVENT_MIN + 1000;
+
+ /// @brief Parsing HTTP request failed.
+ static const int HTTP_PARSE_FAILED_EVT = SM_DERIVED_EVENT_MIN + 1001;
+
+ //@}
+
+ /// @brief Constructor.
+ ///
+ /// @param message Reference to the HTTP request or response message.
+ HttpMessageParserBase(HttpMessage& message);
+
+ /// @brief Run the parser as long as the amount of data is sufficient.
+ ///
+ /// The data to be parsed should be provided by calling
+ /// @ref HttpMessageParserBase::postBuffer. When the parser reaches the end
+ /// of the data buffer the @ref HttpMessageParserBase::poll sets the next
+ /// event to @ref NEED_MORE_DATA_EVT and returns. The caller should then invoke
+ /// @ref HttpMessageParserBase::postBuffer again to provide more data to the
+ /// parser, and call @ref HttpMessageParserBase::poll to continue parsing.
+ ///
+ /// This method also returns when parsing completes or fails. The last
+ /// event can be examined to check whether parsing was successful or not.
+ void poll();
+
+ /// @brief Returns true if the parser needs more data to continue.
+ ///
+ /// @return true if the next event is NEED_MORE_DATA_EVT.
+ bool needData() const;
+
+ /// @brief Returns true if the message has been parsed successfully.
+ bool httpParseOk() const;
+
+ /// @brief Returns error message.
+ std::string getErrorMessage() const {
+ return (error_message_);
+ }
+
+ /// @brief Provides more input data to the parser.
+ ///
+ /// This method must be called prior to calling @ref HttpMessageParserBase::poll
+ /// to deliver data to be parsed. HTTP messages are received over TCP and
+ /// multiple reads may be necessary to retrieve the entire request. There is
+ /// no need to accumulate the entire request to start parsing it. A chunk
+ /// of data can be provided to the parser using this method and parsed right
+ /// away using @ref HttpMessageParserBase::poll.
+ ///
+ /// @param buf A pointer to the buffer holding the data.
+ /// @param buf_size Size of the data within the buffer.
+ void postBuffer(const void* buf, const size_t buf_size);
+
+private:
+
+ /// @brief Make @ref runModel private to make sure that the caller uses
+ /// @ref poll method instead.
+ using StateModel::runModel;
+
+protected:
+
+ /// @brief Define events used by the parser.
+ virtual void defineEvents();
+
+ /// @brief Verifies events used by the parser.
+ virtual void verifyEvents();
+
+ /// @brief Defines states of the parser.
+ virtual void defineStates();
+
+ /// @brief Generic parser handler which reads a single byte of data and
+ /// parses it using specified callback function.
+ ///
+ /// This generic handler is used in most of the parser states to parse a
+ /// single byte of input data. If there is no more data it simply returns.
+ /// Otherwise, if the next event is DATA_READ_OK_EVT or
+ /// MORE_DATA_PROVIDED_EVT, it calls the provided callback function to
+ /// parse the new byte of data. For all other states it throws an exception.
+ ///
+ /// @param handler_name Name of the handler function which called this
+ /// method.
+ /// @param after_read_logic Callback function to parse the byte of data.
+ /// This callback function implements state specific logic.
+ ///
+ /// @throw HttpRequestParserError when invalid event occurred.
+ void stateWithReadHandler(const std::string& handler_name,
+ boost::function<void(const char c)>
+ after_read_logic);
+
+ /// @brief Transition parser to failure state.
+ ///
+ /// This method transitions the parser to @ref HTTP_PARSE_FAILED_ST and
+ /// sets next event to HTTP_PARSE_FAILED_EVT.
+ ///
+ /// @param error_msg Error message explaining the failure.
+ void parseFailure(const std::string& error_msg);
+
+ /// @brief A method called when parsing fails.
+ ///
+ /// @param explanation Error message explaining the reason for parsing
+ /// failure.
+ virtual void onModelFailure(const std::string& explanation);
+
+ /// @brief Retrieves next byte of data from the buffer.
+ ///
+ /// During normal operation, when there is no more data in the buffer,
+ /// the parser sets NEED_MORE_DATA_EVT as next event to signal the need for
+ /// calling @ref HttpMessageParserBase::postBuffer.
+ ///
+ /// @throw HttpMessageParserBaseError If current event is already set to
+ /// NEED_MORE_DATA_EVT or MORE_DATA_PROVIDED_EVT. In the former case, it
+ /// indicates that the caller failed to provide new data using
+ /// @ref HttpMessageParserBase::postBuffer. The latter case is highly unlikely
+ /// as it indicates that no new data were provided but the state of the
+ /// parser was changed from NEED_MORE_DATA_EVT or the data were provided
+ /// but the data buffer is empty. In both cases, it is an internal server
+ /// error.
+ char getNextFromBuffer();
+
+ /// @brief This method is called when invalid event occurred in a particular
+ /// parser state.
+ ///
+ /// This method simply throws @ref HttpMessageParserBaseError informing about
+ /// invalid event occurring for the particular parser state. The error
+ /// message includes the name of the handler in which the exception
+ /// has been thrown. It also includes the event which caused the
+ /// exception.
+ ///
+ /// @param handler_name Name of the handler in which the exception is
+ /// thrown.
+ /// @param event An event which caused the exception.
+ ///
+ /// @throw HttpMessageParserBaseError.
+ void invalidEventError(const std::string& handler_name,
+ const unsigned int event);
+
+ /// @brief Handler for HTTP_PARSE_OK_ST and HTTP_PARSE_FAILED_ST.
+ ///
+ /// If parsing is successful, it calls @ref HttpRequest::create to validate
+ /// the HTTP request. In both cases it transitions the parser to the END_ST.
+ void parseEndedHandler();
+
+ /// @brief Tries to read next byte from buffer.
+ ///
+ /// @param [out] next A reference to the variable where read data should be
+ /// stored.
+ ///
+ /// @return true if character was successfully read, false otherwise.
+ bool popNextFromBuffer(char& next);
+
+ /// @brief Checks if specified value is a character.
+ ///
+ /// @return true, if specified value is a character.
+ bool isChar(const char c) const;
+
+ /// @brief Checks if specified value is a control value.
+ ///
+ /// @return true, if specified value is a control value.
+ bool isCtl(const char c) const;
+
+ /// @brief Checks if specified value is a special character.
+ ///
+ /// @return true, if specified value is a special character.
+ bool isSpecial(const char c) const;
+
+ /// @brief Reference to the parsed HTTP message.
+ HttpMessage& message_;
+
+ /// @brief Internal buffer from which parser reads data.
+ std::list<char> buffer_;
+
+ /// @brief Error message set by @ref onModelFailure.
+ std::string error_message_;
+};
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif
const int HttpRequestParser::EXPECTING_NEW_LINE2_ST;
const int HttpRequestParser::EXPECTING_NEW_LINE3_ST;
const int HttpRequestParser::HTTP_BODY_ST;
-const int HttpRequestParser::HTTP_PARSE_OK_ST;
-const int HttpRequestParser::HTTP_PARSE_FAILED_ST;
-
-const int HttpRequestParser::DATA_READ_OK_EVT;
-const int HttpRequestParser::NEED_MORE_DATA_EVT;
-const int HttpRequestParser::MORE_DATA_PROVIDED_EVT;
-const int HttpRequestParser::HTTP_PARSE_OK_EVT;
-const int HttpRequestParser::HTTP_PARSE_FAILED_EVT;
HttpRequestParser::HttpRequestParser(HttpRequest& request)
- : StateModel(), buffer_(), request_(request),
- context_(request_.context()), error_message_() {
+ : HttpMessageParserBase(request), request_(request),
+ context_(request_.context()) {
}
void
postNextEvent(START_EVT);
}
-void
-HttpRequestParser::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
-HttpRequestParser::needData() const {
- return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
- (getNextEvent() == START_EVT));
-}
-
-bool
-HttpRequestParser::httpParseOk() const {
- return ((getNextEvent() == END_EVT) &&
- (getLastEvent() == HTTP_PARSE_OK_EVT));
-}
-
-void
-HttpRequestParser::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);
- }
-}
-
-void
-HttpRequestParser::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
-HttpRequestParser::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
HttpRequestParser::defineStates() {
// Call parent class implementation first.
- StateModel::defineStates();
+ HttpMessageParserBase::defineStates();
// Define HTTP parser specific states.
defineState(RECEIVE_START_ST, "RECEIVE_START_ST",
defineState(HTTP_BODY_ST, "HTTP_BODY_ST",
boost::bind(&HttpRequestParser::bodyHandler, this));
-
- defineState(HTTP_PARSE_OK_ST, "HTTP_PARSE_OK_ST",
- boost::bind(&HttpRequestParser::parseEndedHandler, this));
-
- defineState(HTTP_PARSE_FAILED_ST, "HTTP_PARSE_FAILED_ST",
- boost::bind(&HttpRequestParser::parseEndedHandler, this));
-}
-
-void
-HttpRequestParser::parseFailure(const std::string& error_msg) {
- error_message_ = error_msg + " : " + getContextStr();
- transition(HTTP_PARSE_FAILED_ST, HTTP_PARSE_FAILED_EVT);
-}
-
-void
-HttpRequestParser::onModelFailure(const std::string& explanation) {
- if (error_message_.empty()) {
- error_message_ = explanation;
- }
-}
-
-char
-HttpRequestParser::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(HttpRequestParserError,
- "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 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(HttpRequestParserError,
- "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.
- transition(getCurrState(), NEED_MORE_DATA_EVT);
- }
- }
- }
- return (c);
-}
-
-void
-HttpRequestParser::invalidEventError(const std::string& handler_name,
- const unsigned int event) {
- isc_throw(HttpRequestParserError, handler_name << ": "
- << " invalid event " << getEventLabel(static_cast<int>(event)));
}
-void
-HttpRequestParser::stateWithReadHandler(const std::string& handler_name,
- boost::function<void(const char c)>
- after_read_logic) {
- char c = getNextFromBuffer();
- // 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(c);
- break;
- default:
- invalidEventError(handler_name, getNextEvent());
- }
- }
-}
-
-
void
HttpRequestParser::receiveStartHandler() {
char c = getNextFromBuffer();
});
}
-
-void
-HttpRequestParser::parseEndedHandler() {
- switch(getNextEvent()) {
- case HTTP_PARSE_OK_EVT:
- request_.finalize();
- transition(END_ST, END_EVT);
- break;
- case HTTP_PARSE_FAILED_EVT:
- abortModel("HTTP request parsing failed");
- break;
-
- default:
- invalidEventError("parseEndedHandler", getNextEvent());
- }
-}
-
-bool
-HttpRequestParser::popNextFromBuffer(char& next) {
- // If there are any characters in the buffer, pop next.
- if (!buffer_.empty()) {
- next = buffer_.front();
- buffer_.pop_front();
- return (true);
- }
- return (false);
-}
-
-
-bool
-HttpRequestParser::isChar(const char c) const {
- // was (c >= 0) && (c <= 127)
- return (c >= 0);
-}
-
-bool
-HttpRequestParser::isCtl(const char c) const {
- return (((c >= 0) && (c <= 31)) || (c == 127));
-}
-
-bool
-HttpRequestParser::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;
-}
-
-
} // namespace http
} // namespace isc
#ifndef HTTP_REQUEST_PARSER_H
#define HTTP_REQUEST_PARSER_H
-#include <exceptions/exceptions.h>
+#include <http/http_message_parser_base.h>
#include <http/request.h>
-#include <util/state_model.h>
-#include <boost/function.hpp>
#include <boost/shared_ptr.hpp>
-#include <list>
-#include <stdint.h>
-#include <string>
namespace isc {
namespace http {
/// has occurred.
///
/// The most common errors are due to receiving malformed requests.
-class HttpRequestParserError : public Exception {
+class HttpRequestParserError : public HttpMessageParserBaseError {
public:
HttpRequestParserError(const char* file, size_t line, const char* what) :
- isc::Exception(file, line, what) { };
+ HttpMessageParserBaseError(file, line, what) { };
};
class HttpRequestParser;
/// @brief A generic parser for HTTP requests.
///
/// This class implements a parser for HTTP requests. The parser derives from
-/// @ref isc::util::StateModel class and implements its own state machine on
+/// @ref HttpMessageParserBase class and implements its own state machine on
/// top of it. The states of the parser reflect various parts of the HTTP
/// message being parsed, e.g. parsing HTTP method, parsing URI, parsing
/// message body etc. The descriptions of all parser states are provided
/// below together with the constants defining these states.
///
-/// HTTP uses TCP as a transport which is asynchronous in nature, i.e. the
-/// HTTP message is received in chunks and multiple TCP connections can be
-/// established at the same time. Multiplexing between these connections
-/// requires providing a separate state machine per connection to "remember"
-/// the state of each transaction when the parser is waiting for asynchronous
-/// data to be delivered. While the parser is waiting for the data, it can
-/// parse requests received over other connections. This class provides means
-/// for parsing partial data received over the specific connection and
-/// interrupting data parsing to switch to a different context.
-///
/// The request parser validates the syntax of the received message as it
/// progresses with parsing the data. Though, it doesn't interpret the received
/// data until the whole message is parsed. In most cases we want to apply some
/// If any of these restrictions is not met in the received message, an
/// exception will be thrown, thereby @ref HttpRequestParser will fail parsing
/// the message.
-///
-/// A new method @ref HttpRequestParser::poll has been created to run the
-/// parser's state machine as long as there are unparsed data in the parser's
-/// internal buffer. This method returns control to the caller when the parser
-/// runs out of data in this buffer. The caller must feed the buffer by calling
-/// @ref HttpRequestParser::postBuffer and then run @ref HttpRequestParser::poll
-/// again.
-///
-/// In case the caller provides more data than indicated by the "Content-Length"
-/// header the parser will return from poll() after parsing the data which
-/// constitute the HTTP request and not parse the extraneous data. The caller
-/// should test the @ref HttpRequestParser::needData and
-/// @ref HttpRequestParser::httpParseOk to determine whether parsing has
-/// completed.
-///
-/// The @ref util::StateModel::runModel must not be used to run the
-/// @ref HttpRequestParser state machine, thus it is made private method.
-class HttpRequestParser : public util::StateModel {
+class HttpRequestParser : public HttpMessageParserBase {
public:
/// @name States supported by the HttpRequestParser.
/// @brief Parsing body of a HTTP message.
static const int HTTP_BODY_ST = SM_DERIVED_STATE_MIN + 21;
- /// @brief Parsing successfully completed.
- static const int HTTP_PARSE_OK_ST = SM_DERIVED_STATE_MIN + 100;
-
- /// @brief Parsing failed.
- static const int HTTP_PARSE_FAILED_ST = SM_DERIVED_STATE_MIN + 101;
-
//@}
- /// @name Events used during HTTP message parsing.
- ///
- //@{
-
- /// @brief Chunk of data successfully read and parsed.
- static const int DATA_READ_OK_EVT = SM_DERIVED_EVENT_MIN + 1;
-
- /// @brief Unable to proceed with parsing until new data is provided.
- static const int NEED_MORE_DATA_EVT = SM_DERIVED_EVENT_MIN + 2;
-
- /// @brief New data provided and parsing should continue.
- static const int MORE_DATA_PROVIDED_EVT = SM_DERIVED_EVENT_MIN + 3;
-
- /// @brief Parsing HTTP request successful.
- static const int HTTP_PARSE_OK_EVT = SM_DERIVED_EVENT_MIN + 100;
-
- /// @brief Parsing HTTP request failed.
- static const int HTTP_PARSE_FAILED_EVT = SM_DERIVED_EVENT_MIN + 101;
-
- //@}
-
/// @brief Constructor.
///
/// Creates new instance of the parser.
/// states and events, and sets the initial model state to RECEIVE_START_ST.
void initModel();
- /// @brief Run the parser as long as the amount of data is sufficient.
- ///
- /// The data to be parsed should be provided by calling
- /// @ref HttpRequestParser::postBuffer. When the parser reaches the end of
- /// the data buffer the @ref HttpRequestParser::poll sets the next event to
- /// @ref NEED_MORE_DATA_EVT and returns. The caller should then invoke
- /// @ref HttpRequestParser::postBuffer again to provide more data to the
- /// parser, and call @ref HttpRequestParser::poll to continue parsing.
- ///
- /// This method also returns when parsing completes or fails. The last
- /// event can be examined to check whether parsing was successful or not.
- void poll();
-
- /// @brief Returns true if the parser needs more data to continue.
- ///
- /// @return true if the next event is NEED_MORE_DATA_EVT.
- bool needData() const;
-
- /// @brief Returns true if a request has been parsed successfully.
- bool httpParseOk() const;
-
- /// @brief Returns error message.
- std::string getErrorMessage() const {
- return (error_message_);
- }
-
- /// @brief Provides more input data to the parser.
- ///
- /// This method must be called prior to calling @ref HttpRequestParser::poll
- /// to deliver data to be parsed. HTTP requests are received over TCP and
- /// multiple reads may be necessary to retrieve the entire request. There is
- /// no need to accumulate the entire request to start parsing it. A chunk
- /// of data can be provided to the parser using this method and parsed right
- /// away using @ref HttpRequestParser::poll.
- ///
- /// @param buf A pointer to the buffer holding the data.
- /// @param buf_size Size of the data within the buffer.
- void postBuffer(const void* buf, const size_t buf_size);
-
private:
- /// @brief Make @ref runModel private to make sure that the caller uses
- /// @ref poll method instead.
- using StateModel::runModel;
-
- /// @brief Define events used by the parser.
- virtual void defineEvents();
-
- /// @brief Verifies events used by the parser.
- virtual void verifyEvents();
-
/// @brief Defines states of the parser.
virtual void defineStates();
- /// @brief Transition parser to failure state.
- ///
- /// This method transitions the parser to @ref HTTP_PARSE_FAILED_ST and
- /// sets next event to HTTP_PARSE_FAILED_EVT.
- ///
- /// @param error_msg Error message explaining the failure.
- void parseFailure(const std::string& error_msg);
-
- /// @brief A method called when parsing fails.
- ///
- /// @param explanation Error message explaining the reason for parsing
- /// failure.
- virtual void onModelFailure(const std::string& explanation);
-
- /// @brief Retrieves next byte of data from the buffer.
- ///
- /// During normal operation, when there is no more data in the buffer,
- /// the parser sets NEED_MORE_DATA_EVT as next event to signal the need for
- /// calling @ref HttpRequestParser::postBuffer.
- ///
- /// @throw HttpRequestParserError If current event is already set to
- /// NEED_MORE_DATA_EVT or MORE_DATA_PROVIDED_EVT. In the former case, it
- /// indicates that the caller failed to provide new data using
- /// @ref HttpRequestParser::postBuffer. The latter case is highly unlikely
- /// as it indicates that no new data were provided but the state of the
- /// parser was changed from NEED_MORE_DATA_EVT or the data were provided
- /// but the data buffer is empty. In both cases, it is an internal server
- /// error.
- char getNextFromBuffer();
-
- /// @brief This method is called when invalid event occurred in a particular
- /// parser state.
- ///
- /// This method simply throws @ref HttpRequestParserError informing about
- /// invalid event occurring for the particular parser state. The error
- /// message includes the name of the handler in which the exception
- /// has been thrown. It also includes the event which caused the
- /// exception.
- ///
- /// @param handler_name Name of the handler in which the exception is
- /// thrown.
- /// @param event An event which caused the exception.
- ///
- /// @throw HttpRequestParserError.
- void invalidEventError(const std::string& handler_name,
- const unsigned int event);
-
- /// @brief Generic parser handler which reads a single byte of data and
- /// parses it using specified callback function.
- ///
- /// This generic handler is used in most of the parser states to parse a
- /// single byte of input data. If there is no more data it simply returns.
- /// Otherwise, if the next event is DATA_READ_OK_EVT or
- /// MORE_DATA_PROVIDED_EVT, it calls the provided callback function to
- /// parse the new byte of data. For all other states it throws an exception.
- ///
- /// @param handler_name Name of the handler function which called this
- /// method.
- /// @param after_read_logic Callback function to parse the byte of data.
- /// This callback function implements state specific logic.
- ///
- /// @throw HttpRequestParserError when invalid event occurred.
- void stateWithReadHandler(const std::string& handler_name,
- boost::function<void(const char c)>
- after_read_logic);
-
/// @name State handlers.
///
//@{
/// @brief Handler for HTTP_BODY_ST.
void bodyHandler();
- /// @brief Handler for HTTP_PARSE_OK_ST and HTTP_PARSE_FAILED_ST.
- ///
- /// If parsing is successful, it calls @ref HttpRequest::create to validate
- /// the HTTP request. In both cases it transitions the parser to the END_ST.
- void parseEndedHandler();
-
- /// @brief Tries to read next byte from buffer.
- ///
- /// @param [out] next A reference to the variable where read data should be
- /// stored.
- ///
- /// @return true if character was successfully read, false otherwise.
- bool popNextFromBuffer(char& next);
-
- /// @brief Checks if specified value is a character.
- ///
- /// @return true, if specified value is a character.
- bool isChar(const char c) const;
-
- /// @brief Checks if specified value is a control value.
- ///
- /// @return true, if specified value is a control value.
- bool isCtl(const char c) const;
-
- /// @brief Checks if specified value is a special character.
- ///
- /// @return true, if specified value is a special character.
- bool isSpecial(const char c) const;
-
- /// @brief Internal buffer from which parser reads data.
- std::list<char> buffer_;
-
/// @brief Reference to the request object specified in the constructor.
HttpRequest& request_;
/// @brief Pointer to the internal context of the @ref HttpRequest object.
HttpRequestContextPtr context_;
-
- /// @brief Error message set by @ref onModelFailure.
- std::string error_message_;
};
} // namespace http