Kea: http_message_parser_base.cc Source File

| Kea 3.0.0

http_message_parser_base.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2024 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 <http/http_message_parser_base.h>
10#include <functional>
11#include <sstream>
12
13using namespace isc::util;
14
15namespace isc {
16namespace http {
17
18const int HttpMessageParserBase::HTTP_PARSE_OK_ST;
19const int HttpMessageParserBase::HTTP_PARSE_FAILED_ST;
20
21const int HttpMessageParserBase::DATA_READ_OK_EVT;
22const int HttpMessageParserBase::NEED_MORE_DATA_EVT;
23const int HttpMessageParserBase::MORE_DATA_PROVIDED_EVT;
24const int HttpMessageParserBase::HTTP_PARSE_OK_EVT;
25const int HttpMessageParserBase::HTTP_PARSE_FAILED_EVT;
26
27
28HttpMessageParserBase::HttpMessageParserBase(HttpMessage& message)
29 : StateModel(), message_(message), buffer_(), buffer_pos_(0),
30 error_message_() {
31}
32
33void
34HttpMessageParserBase::poll() {
35 try {
36 // Run the parser until it runs out of input data or until
37 // parsing completes.
38 do {
39 getState(getCurrState())->run();
40
41 } while (!isModelDone() && (getNextEvent() != NOP_EVT) &&
42 (getNextEvent() != NEED_MORE_DATA_EVT));
43 } catch (const std::exception& ex) {
44 abortModel(ex.what());
45 }
46}
47
48bool
49HttpMessageParserBase::needData() const {
50 return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
51 (getNextEvent() == START_EVT));
52}
53
54bool
55HttpMessageParserBase::httpParseOk() const {
56 return ((getNextEvent() == END_EVT) &&
57 (getLastEvent() == HTTP_PARSE_OK_EVT));
58}
59
60void
61HttpMessageParserBase::postBuffer(const void* buf, const size_t buf_size) {
62 if (buf_size > 0) {
63 // The next event is NEED_MORE_DATA_EVT when the parser wants to
64 // signal that more data is needed. This method is called to supply
65 // more data and thus it should change the next event to
66 // MORE_DATA_PROVIDED_EVT.
67 if (getNextEvent() == NEED_MORE_DATA_EVT) {
68 transition(getCurrState(), MORE_DATA_PROVIDED_EVT);
69 }
70 buffer_.insert(buffer_.end(), static_cast<const char*>(buf),
71 static_cast<const char*>(buf) + buf_size);
72 }
73}
74
75std::string
76HttpMessageParserBase::getBufferAsString(const size_t limit) const {
77 std::string message(buffer_.begin(), buffer_.end());
78 return (logFormatHttpMessage(message, limit));
79}
80
81std::string
82HttpMessageParserBase::logFormatHttpMessage(const std::string& message,
83 const size_t limit) {
84 if ((limit > 0) && !message.empty()) {
85 if (limit < message.size()) {
86 std::ostringstream s;
87 s << message.substr(0, limit)
88 << ".........\n(truncating HTTP message larger than "
89 << limit << " characters)\n";
90 return (s.str());
91 }
92 }
93
94 // Return original message if it is empty or does not exceed the
95 // limit.
96 return (message);
97}
98
99
100void
101HttpMessageParserBase::defineEvents() {
102 StateModel::defineEvents();
103
104 // Define HTTP parser specific events.
105 defineEvent(DATA_READ_OK_EVT, "DATA_READ_OK_EVT");
106 defineEvent(NEED_MORE_DATA_EVT, "NEED_MORE_DATA_EVT");
107 defineEvent(MORE_DATA_PROVIDED_EVT, "MORE_DATA_PROVIDED_EVT");
108 defineEvent(HTTP_PARSE_OK_EVT, "HTTP_PARSE_OK_EVT");
109 defineEvent(HTTP_PARSE_FAILED_EVT, "HTTP_PARSE_FAILED_EVT");
110}
111
112void
113HttpMessageParserBase::verifyEvents() {
114 StateModel::verifyEvents();
115
116 getEvent(DATA_READ_OK_EVT);
117 getEvent(NEED_MORE_DATA_EVT);
118 getEvent(MORE_DATA_PROVIDED_EVT);
119 getEvent(HTTP_PARSE_OK_EVT);
120 getEvent(HTTP_PARSE_FAILED_EVT);
121}
122
123void
124HttpMessageParserBase::defineStates() {
125 // Call parent class implementation first.
126 StateModel::defineStates();
127
128 defineState(HTTP_PARSE_OK_ST, "HTTP_PARSE_OK_ST",
129 std::bind(&HttpMessageParserBase::parseEndedHandler, this));
130
131 defineState(HTTP_PARSE_FAILED_ST, "HTTP_PARSE_FAILED_ST",
132 std::bind(&HttpMessageParserBase::parseEndedHandler, this));
133}
134
135void
136HttpMessageParserBase::stateWithReadHandler(const std::string& handler_name,
137 std::function<void(const char c)>
138 after_read_logic) {
139 std::string bytes;
140 getNextFromBuffer(bytes);
141 // Do nothing if we reached the end of buffer.
142 if (getNextEvent() != NEED_MORE_DATA_EVT) {
143 switch(getNextEvent()) {
144 case DATA_READ_OK_EVT:
145 case MORE_DATA_PROVIDED_EVT:
146 after_read_logic(bytes[0]);
147 break;
148 default:
149 invalidEventError(handler_name, getNextEvent());
150 }
151 }
152}
153
154void
155HttpMessageParserBase::stateWithMultiReadHandler(const std::string& handler_name,
156 std::function<void(const std::string&)>
157 after_read_logic) {
158 std::string bytes;
159 getNextFromBuffer(bytes, 0);
160 // Do nothing if we reached the end of buffer.
161 if (getNextEvent() != NEED_MORE_DATA_EVT) {
162 switch(getNextEvent()) {
163 case DATA_READ_OK_EVT:
164 case MORE_DATA_PROVIDED_EVT:
165 after_read_logic(bytes);
166 break;
167 default:
168 invalidEventError(handler_name, getNextEvent());
169 }
170 }
171}
172
173void
174HttpMessageParserBase::parseFailure(const std::string& error_msg) {
175 error_message_ = error_msg + " : " + getContextStr();
176 transition(HTTP_PARSE_FAILED_ST, HTTP_PARSE_FAILED_EVT);
177}
178
179void
180HttpMessageParserBase::onModelFailure(const std::string& explanation) {
181 if (error_message_.empty()) {
182 error_message_ = explanation;
183 }
184}
185
186void
187HttpMessageParserBase::getNextFromBuffer(std::string& bytes, const size_t limit) {
188 unsigned int ev = getNextEvent();
189 bytes = "\0";
190 // The caller should always provide additional data when the
191 // NEED_MORE_DATA_EVT occurs. If the next event is still
192 // NEED_MORE_DATA_EVT it indicates that the caller hasn't provided
193 // the data.
194 if (ev == NEED_MORE_DATA_EVT) {
195 isc_throw(HttpParseError,
196 "HTTP request parser requires new data to progress, but no data"
197 " have been provided. The transaction is aborted to avoid"
198 " a deadlock. This is a Kea HTTP server logic error!");
199
200 } else {
201 // Try to retrieve characters from the buffer.
202 const bool data_exist = popNextFromBuffer(bytes, limit);
203 if (!data_exist) {
204 // There is no more data so it is really not possible that we're
205 // at MORE_DATA_PROVIDED_EVT.
206 if (ev == MORE_DATA_PROVIDED_EVT) {
207 isc_throw(HttpParseError,
208 "HTTP server state indicates that new data have been"
209 " provided to be parsed, but the transaction buffer"
210 " contains no new data. This is a Kea HTTP server logic"
211 " error!");
212
213 } else {
214 // If there is no more data we should set NEED_MORE_DATA_EVT
215 // event to indicate that new data should be provided.
216 postNextEvent(NEED_MORE_DATA_EVT);
217 }
218 }
219 }
220}
221
222void
223HttpMessageParserBase::invalidEventError(const std::string& handler_name,
224 const unsigned int event) {
225 isc_throw(HttpParseError, handler_name << ": invalid event "
226 << getEventLabel(static_cast<int>(event)));
227}
228
229void
230HttpMessageParserBase::parseEndedHandler() {
231 switch(getNextEvent()) {
232 case HTTP_PARSE_OK_EVT:
233 message_.finalize();
234 transition(END_ST, END_EVT);
235 break;
236 case HTTP_PARSE_FAILED_EVT:
237 abortModel("HTTP message parsing failed");
238 break;
239
240 default:
241 invalidEventError("parseEndedHandler", getNextEvent());
242 }
243}
244
245bool
246HttpMessageParserBase::popNextFromBuffer(std::string& next, const size_t limit) {
247 // If there are any characters in the buffer, pop next.
248 if (buffer_pos_ < buffer_.size()) {
249 next = buffer_.substr(buffer_pos_, limit == 0 ? std::string::npos : limit);
250
251 if (limit > 0) {
252 buffer_pos_ += limit;
253 }
254
255 if ((buffer_pos_ > buffer_.size()) || (limit == 0)) {
256 buffer_pos_ = buffer_.size();
257 }
258 return (true);
259 }
260 return (false);
261}
262
263bool
264HttpMessageParserBase::isChar(const signed char c) const {
265 return (c >= 0);
266}
267
268bool
269HttpMessageParserBase::isCtl(const signed char c) const {
270 return (((c >= 0) && (c <= 31)) || (c == 127));
271}
272
273bool
274HttpMessageParserBase::isSpecial(const signed char c) const {
275 switch (c) {
276 case '(':
277 case ')':
278 case '<':
279 case '>':
280 case '@':
281 case ',':
282 case ';':
283 case ':':
284 case '\\':
285 case '"':
286 case '/':
287 case '[':
288 case ']':
289 case '?':
290 case '=':
291 case '{':
292 case '}':
293 case ' ':
294 case '\t':
295 return true;
296
297 default:
298 ;
299 }
300
301 return false;
302}
303
304
305} // end of namespace isc::http
306} // end of namespace isc
isc::http::HttpMessageParserBase::getNextFromBuffer
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
Definition http_message_parser_base.cc:187
isc::http::HttpMessageParserBase::DATA_READ_OK_EVT
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
Definition http_message_parser_base.h:85
isc::http::HttpMessageParserBase::NEED_MORE_DATA_EVT
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
Definition http_message_parser_base.h:88
isc::http::HttpMessageParserBase::isSpecial
bool isSpecial(const signed char c) const
Checks if specified value is a special character.
Definition http_message_parser_base.cc:274
isc::http::HttpMessageParserBase::parseFailure
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
Definition http_message_parser_base.cc:174
isc::http::HttpMessageParserBase::isChar
bool isChar(const signed char c) const
Checks if specified value is a character.
Definition http_message_parser_base.cc:264
isc::http::HttpMessageParserBase::parseEndedHandler
void parseEndedHandler()
Handler for HTTP_PARSE_OK_ST and HTTP_PARSE_FAILED_ST.
Definition http_message_parser_base.cc:230
isc::http::HttpMessageParserBase::MORE_DATA_PROVIDED_EVT
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
Definition http_message_parser_base.h:91
isc::http::HttpMessageParserBase::needData
bool needData() const
Returns true if the parser needs more data to continue.
Definition http_message_parser_base.cc:49
isc::http::HttpMessageParserBase::verifyEvents
virtual void verifyEvents() override
Verifies events used by the parser.
Definition http_message_parser_base.cc:113
isc::http::HttpMessageParserBase::poll
void poll()
Run the parser as long as the amount of data is sufficient.
Definition http_message_parser_base.cc:34
isc::http::HttpMessageParserBase::onModelFailure
virtual void onModelFailure(const std::string &explanation) override
A method called when parsing fails.
Definition http_message_parser_base.cc:180
isc::http::HttpMessageParserBase::stateWithReadHandler
void stateWithReadHandler(const std::string &handler_name, std::function< void(const char c)> after_read_logic)
Generic parser handler which reads a single byte of data and parses it using specified callback funct...
Definition http_message_parser_base.cc:136
isc::http::HttpMessageParserBase::defineEvents
virtual void defineEvents() override
Define events used by the parser.
Definition http_message_parser_base.cc:101
isc::http::HttpMessageParserBase::error_message_
std::string error_message_
Error message set by onModelFailure.
Definition http_message_parser_base.h:311
isc::http::HttpMessageParserBase::invalidEventError
void invalidEventError(const std::string &handler_name, const unsigned int event)
This method is called when invalid event occurred in a particular parser state.
Definition http_message_parser_base.cc:223
isc::http::HttpMessageParserBase::HTTP_PARSE_OK_ST
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
Definition http_message_parser_base.h:73
isc::http::HttpMessageParserBase::popNextFromBuffer
bool popNextFromBuffer(std::string &next, const size_t limit=1)
Tries to read next byte from buffer.
Definition http_message_parser_base.cc:246
isc::http::HttpMessageParserBase::HttpMessageParserBase
HttpMessageParserBase(HttpMessage &message)
Constructor.
Definition http_message_parser_base.cc:28
isc::http::HttpMessageParserBase::message_
HttpMessage & message_
Reference to the parsed HTTP message.
Definition http_message_parser_base.h:302
isc::http::HttpMessageParserBase::buffer_
std::string buffer_
Internal buffer from which parser reads data.
Definition http_message_parser_base.h:305
isc::http::HttpMessageParserBase::HTTP_PARSE_FAILED_EVT
static const int HTTP_PARSE_FAILED_EVT
Parsing HTTP request failed.
Definition http_message_parser_base.h:97
isc::http::HttpMessageParserBase::httpParseOk
bool httpParseOk() const
Returns true if the message has been parsed successfully.
Definition http_message_parser_base.cc:55
isc::http::HttpMessageParserBase::buffer_pos_
size_t buffer_pos_
Position of the next character to read from the buffer.
Definition http_message_parser_base.h:308
isc::http::HttpMessageParserBase::stateWithMultiReadHandler
void stateWithMultiReadHandler(const std::string &handler_name, std::function< void(const std::string &)> after_read_logic)
Generic parser handler which reads multiple bytes of data and parses it using specified callback func...
Definition http_message_parser_base.cc:155
isc::http::HttpMessageParserBase::isCtl
bool isCtl(const signed char c) const
Checks if specified value is a control value.
Definition http_message_parser_base.cc:269
isc::http::HttpMessageParserBase::logFormatHttpMessage
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
Definition http_message_parser_base.cc:82
isc::http::HttpMessageParserBase::getBufferAsString
std::string getBufferAsString(const size_t limit=0) const
Returns parser's input buffer as string.
Definition http_message_parser_base.cc:76
isc::http::HttpMessageParserBase::postBuffer
void postBuffer(const void *buf, const size_t buf_size)
Provides more input data to the parser.
Definition http_message_parser_base.cc:61
isc::http::HttpMessageParserBase::HTTP_PARSE_OK_EVT
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
Definition http_message_parser_base.h:94
isc::http::HttpMessageParserBase::defineStates
virtual void defineStates() override
Defines states of the parser.
Definition http_message_parser_base.cc:124
isc::http::HttpMessageParserBase::HTTP_PARSE_FAILED_ST
static const int HTTP_PARSE_FAILED_ST
Parsing failed.
Definition http_message_parser_base.h:76
isc::http::HttpMessage
Base class for HttpRequest and HttpResponse.
Definition http_message.h:62
isc::http::HttpParseError
Exception thrown when an error during parsing HTTP message has occurred.
Definition http_message_parser_base.h:23
isc::util::StateModel::getEvent
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition state_model.cc:186
isc::util::StateModel::isModelDone
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition state_model.cc:403
isc::util::StateModel::defineEvents
virtual void defineEvents()
Populates the set of events.
Definition state_model.cc:229
isc::util::StateModel::postNextEvent
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition state_model.cc:320
isc::util::StateModel::defineState
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:196
isc::util::StateModel::getState
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition state_model.cc:213
isc::util::StateModel::StateModel
StateModel()
Constructor.
Definition state_model.cc:89
isc::util::StateModel::getNextEvent
unsigned int getNextEvent() const
Fetches the model's next event.
Definition state_model.cc:373
isc::util::StateModel::defineEvent
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:170
isc::util::StateModel::transition
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:264
isc::util::StateModel::verifyEvents
virtual void verifyEvents()
Validates the contents of the set of events.
Definition state_model.cc:237
isc::util::StateModel::END_EVT
static const int END_EVT
Event issued to end the model execution.
Definition state_model.h:298
isc::util::StateModel::NOP_EVT
static const int NOP_EVT
Signifies that no event has occurred.
Definition state_model.h:292
isc::util::StateModel::START_EVT
static const int START_EVT
Event issued to start the model execution.
Definition state_model.h:295
isc::util::StateModel::abortModel
void abortModel(const std::string &explanation)
Aborts model execution.
Definition state_model.cc:282
isc::util::StateModel::getEventLabel
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition state_model.cc:432
isc::util::StateModel::defineStates
virtual void defineStates()
Populates the set of states.
Definition state_model.cc:245
isc::util::StateModel::getContextStr
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event.
Definition state_model.cc:443
isc::util::StateModel::END_ST
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition state_model.h:282
isc::util::StateModel::getLastEvent
unsigned int getLastEvent() const
Fetches the model's last event.
Definition state_model.cc:367
isc::util::StateModel::getCurrState
unsigned int getCurrState() const
Fetches the model's current state.
Definition state_model.cc:355
isc_throw
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Definition exceptions/exceptions.h:210
http_message_parser_base.h
isc::http
Definition auth_config.h:19
isc::util
Definition time_utils.cc:43
isc
Defines the logger used by the top-level component of kea-lfc.
Definition agent_parser.cc:148