HttpMessageParserBase::stateWithReadHandler(const std::string& handler_name,
boost::function<void(const char c)>
after_read_logic) {
- char c = getNextFromBuffer();
+ 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(c);
+ after_read_logic(bytes[0]);
+ break;
+ default:
+ invalidEventError(handler_name, getNextEvent());
+ }
+ }
+}
+
+void
+HttpMessageParserBase::stateWithMultiReadHandler(const std::string& handler_name,
+ boost::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());
}
}
-char
-HttpMessageParserBase::getNextFromBuffer() {
+void
+HttpMessageParserBase::getNextFromBuffer(std::string& bytes, const size_t limit) {
unsigned int ev = getNextEvent();
- char c = '\0';
+ 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
" 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);
+ // 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.
} 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);
+ postNextEvent(NEED_MORE_DATA_EVT);
}
}
}
- return (c);
}
void
}
bool
-HttpMessageParserBase::popNextFromBuffer(char& next) {
+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_[buffer_pos_++];
+ 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)
#include <util/state_model.h>
#include <boost/function.hpp>
#include <string>
-#include <vector>
namespace isc {
namespace http {
boost::function<void(const char c)>
after_read_logic);
+ /// @brief Generic parser handler which reads multiple bytes of data and
+ /// parses it using specified callback function.
+ ///
+ /// This handler is mostly used for parsing body of the HTTP message,
+ /// where we don't validate the content read. Reading multiple bytes
+ /// is the most efficient. 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.
+ ///
+ /// @param handler_name Name of the handler function which called this
+ /// method.
+ /// @param after_read_logic Callback function to parse multiple bytes of
+ /// data. This callback function implements state specific logic.
+ ///
+ /// @throw HttpRequestParserError when invalid event occurred.
+ void stateWithMultiReadHandler(const std::string& handler_name,
+ boost::function<void(const std::string&)>
+ after_read_logic);
+
/// @brief Transition parser to failure state.
///
/// This method transitions the parser to @ref HTTP_PARSE_FAILED_ST and
/// failure.
virtual void onModelFailure(const std::string& explanation);
- /// @brief Retrieves next byte of data from the buffer.
+ /// @brief Retrieves next bytes 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.
///
+ /// @param [out] bytes Reference to the variable where read data should be stored.
+ /// @param limit Maximum number of bytes to be read.
+ ///
/// @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
/// 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();
+ void getNextFromBuffer(std::string& bytes, const size_t limit = 1);
/// @brief This method is called when invalid event occurred in a particular
/// parser state.
///
/// @param [out] next A reference to the variable where read data should be
/// stored.
+ /// @param limit Maximum number of characters to be read.
///
- /// @return true if character was successfully read, false otherwise.
- bool popNextFromBuffer(char& next);
+ /// @return true if data was successfully read, false otherwise.
+ bool popNextFromBuffer(std::string& next, const size_t limit = 1);
/// @brief Checks if specified value is a character.
///
HttpMessage& message_;
/// @brief Internal buffer from which parser reads data.
- std::vector<char> buffer_;
+ std::string buffer_;
/// @brief Position of the next character to read from the buffer.
size_t buffer_pos_;
void
HttpRequestParser::receiveStartHandler() {
- char c = getNextFromBuffer();
+ std::string bytes;
+ getNextFromBuffer(bytes);
if (getNextEvent() != NEED_MORE_DATA_EVT) {
switch(getNextEvent()) {
case START_EVT:
// The first byte should contain a first character of the
// HTTP method name.
- if (!isChar(c) || isCtl(c) || isSpecial(c)) {
- parseFailure("invalid first character " + std::string(1, c) +
+ if (!isChar(bytes[0]) || isCtl(bytes[0]) || isSpecial(bytes[0])) {
+ parseFailure("invalid first character " + std::string(1, bytes[0]) +
" in HTTP method name");
} else {
- context_->method_.push_back(c);
+ context_->method_.push_back(bytes[0]);
transition(HTTP_METHOD_ST, DATA_READ_OK_EVT);
}
break;
void
HttpRequestParser::bodyHandler() {
- stateWithReadHandler("bodyHandler", [this](const char c) {
+ stateWithMultiReadHandler("bodyHandler", [this](const std::string& body) {
// We don't validate the body at this stage. Simply record the
// number of characters specified within "Content-Length".
- context_->body_.push_back(c);
- if (context_->body_.length() <
- request_.getHeaderValueAsUint64("Content-Length")) {
+ context_->body_ += body;
+ size_t content_length = request_.getHeaderValueAsUint64("Content-Length");
+ if (context_->body_.length() < content_length) {
transition(HTTP_BODY_ST, DATA_READ_OK_EVT);
+
} else {
+ // If there was some extraneous data, ignore it.
+ if (context_->body_.length() > content_length) {
+ context_->body_.resize(content_length);
+ }
transition(HTTP_PARSE_OK_ST, HTTP_PARSE_OK_EVT);
}
});
void
HttpResponseParser::receiveStartHandler() {
- char c = getNextFromBuffer();
+ std::string bytes;
+ getNextFromBuffer(bytes);
if (getNextEvent() != NEED_MORE_DATA_EVT) {
switch(getNextEvent()) {
case START_EVT:
- if (c == 'H') {
+ if (bytes[0] == 'H') {
transition(HTTP_VERSION_T1_ST, DATA_READ_OK_EVT);
} else {
- parseFailure("unexpected first character " + std::string(1, c) +
+ parseFailure("unexpected first character " + std::string(1, bytes[0]) +
": expected \'H\'");
}
break;
void
HttpResponseParser::bodyHandler() {
- stateWithReadHandler("bodyHandler", [this](const char c) {
+ stateWithMultiReadHandler("bodyHandler", [this](const std::string& body) {
// We don't validate the body at this stage. Simply record the
// number of characters specified within "Content-Length".
- context_->body_.push_back(c);
- if (context_->body_.length() <
- response_.getHeaderValueAsUint64("Content-Length")) {
+ context_->body_ += body;
+ size_t content_length = response_.getHeaderValueAsUint64("Content-Length");
+ if (context_->body_.length() < content_length) {
transition(HTTP_BODY_ST, DATA_READ_OK_EVT);
+
} else {
+ // If there was some extraneous data, ignore it.
+ if (context_->body_.length() > content_length) {
+ context_->body_.resize(content_length);
+ }
transition(HTTP_PARSE_OK_ST, HTTP_PARSE_OK_EVT);
}
});