Kea 2.7.1
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
10#include <functional>
11#include <sstream>
12
13using namespace isc::util;
14
15namespace isc {
16namespace http {
17
20
26
27
29 : StateModel(), message_(message), buffer_(), buffer_pos_(0),
30 error_message_() {
31}
32
33void
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) &&
43 } catch (const std::exception& ex) {
44 abortModel(ex.what());
45 }
46}
47
48bool
53
54bool
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.
69 }
70 buffer_.insert(buffer_.end(), static_cast<const char*>(buf),
71 static_cast<const char*>(buf) + buf_size);
72 }
73}
74
75std::string
77 std::string message(buffer_.begin(), buffer_.end());
78 return (logFormatHttpMessage(message, limit));
79}
80
81std::string
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
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
122
123void
125 // Call parent class implementation first.
127
128 defineState(HTTP_PARSE_OK_ST, "HTTP_PARSE_OK_ST",
130
131 defineState(HTTP_PARSE_FAILED_ST, "HTTP_PARSE_FAILED_ST",
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.
143 switch(getNextEvent()) {
144 case DATA_READ_OK_EVT:
146 after_read_logic(bytes[0]);
147 break;
148 default:
149 invalidEventError(handler_name, getNextEvent());
150 }
151 }
152}
153
154void
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.
162 switch(getNextEvent()) {
163 case DATA_READ_OK_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();
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) {
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) {
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.
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
231 switch(getNextEvent()) {
235 break;
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
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
bool isSpecial(const signed char c) const
Checks if specified value is a special character.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
bool isChar(const signed char c) const
Checks if specified value is a character.
void parseEndedHandler()
Handler for HTTP_PARSE_OK_ST and HTTP_PARSE_FAILED_ST.
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
bool needData() const
Returns true if the parser needs more data to continue.
virtual void verifyEvents() override
Verifies events used by the parser.
void poll()
Run the parser as long as the amount of data is sufficient.
virtual void onModelFailure(const std::string &explanation) override
A method called when parsing fails.
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...
virtual void defineEvents() override
Define events used by the parser.
std::string error_message_
Error message set by onModelFailure.
void invalidEventError(const std::string &handler_name, const unsigned int event)
This method is called when invalid event occurred in a particular parser state.
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
bool popNextFromBuffer(std::string &next, const size_t limit=1)
Tries to read next byte from buffer.
HttpMessageParserBase(HttpMessage &message)
Constructor.
HttpMessage & message_
Reference to the parsed HTTP message.
std::string buffer_
Internal buffer from which parser reads data.
static const int HTTP_PARSE_FAILED_EVT
Parsing HTTP request failed.
bool httpParseOk() const
Returns true if the message has been parsed successfully.
size_t buffer_pos_
Position of the next character to read from the buffer.
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...
bool isCtl(const signed char c) const
Checks if specified value is a control value.
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
std::string getBufferAsString(const size_t limit=0) const
Returns parser's input buffer as string.
void postBuffer(const void *buf, const size_t buf_size)
Provides more input data to the parser.
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
virtual void defineStates() override
Defines states of the parser.
static const int HTTP_PARSE_FAILED_ST
Parsing failed.
Base class for HttpRequest and HttpResponse.
virtual void finalize()=0
Complete parsing HTTP message or creating an HTTP outbound message.
Exception thrown when an error during parsing HTTP message has occurred.
Implements a finite state machine.
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
bool isModelDone() const
Returns whether or not the model has finished execution.
virtual void defineEvents()
Populates the set of events.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
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.
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
unsigned int getNextEvent() const
Fetches the model's next event.
void defineEvent(unsigned int value, const std::string &label)
Adds an event value and associated label to the set of events.
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
virtual void verifyEvents()
Validates the contents of the set of events.
static const int END_EVT
Event issued to end the model execution.
static const int NOP_EVT
Signifies that no event has occurred.
static const int START_EVT
Event issued to start the model execution.
void abortModel(const std::string &explanation)
Aborts model execution.
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
virtual void defineStates()
Populates the set of states.
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event.
static const int END_ST
Final state, all the state model has reached its conclusion.
unsigned int getLastEvent() const
Fetches the model's last event.
unsigned int getCurrState() const
Fetches the model's current state.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
Defines the logger used by the top-level component of kea-lfc.