Kea 2.5.8
http_message_parser_base.cc
Go to the documentation of this file.
1// Copyright (C) 2017-2020 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
50 return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
51 (getNextEvent() == START_EVT));
52}
53
54bool
56 return ((getNextEvent() == END_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.
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
115
121}
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 char c) const {
265 // was (c >= 0) && (c <= 127)
266 return (c >= 0);
267}
268
269bool
270HttpMessageParserBase::isCtl(const char c) const {
271 return (((c >= 0) && (c <= 31)) || (c == 127));
272}
273
274bool
276 switch (c) {
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 ' ':
295 case '\t':
296 return true;
297
298 default:
299 ;
300 }
301
302 return false;
303}
304
305
306} // end of namespace isc::http
307} // 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.
virtual void verifyEvents()
Verifies events used by the parser.
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
bool isCtl(const char c) const
Checks if specified value is a control value.
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 isChar(const char c) const
Checks if specified value is a character.
bool needData() const
Returns true if the parser needs more data to continue.
void poll()
Run the parser as long as the amount of data is sufficient.
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...
std::string error_message_
Error message set by onModelFailure.
virtual void onModelFailure(const std::string &explanation)
A method called when parsing fails.
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.
virtual void defineEvents()
Define events used by the parser.
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...
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.
bool isSpecial(const char c) const
Checks if specified value is a special character.
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()
Defines states of the parser.
static const int HTTP_PARSE_FAILED_ST
Parsing failed.
Base class for HttpRequest and HttpResponse.
Definition: http_message.h:62
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.
Definition: state_model.h:274
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:186
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
virtual void defineEvents()
Populates the set of events.
Definition: state_model.cc:229
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
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
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
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
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
virtual void verifyEvents()
Validates the contents of the set of events.
Definition: state_model.cc:237
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:298
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
virtual void defineStates()
Populates the set of states.
Definition: state_model.cc:245
std::string getContextStr() const
Convenience method which returns a string rendition of the current state and next event.
Definition: state_model.cc:443
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
unsigned int getLastEvent() const
Fetches the model's last event.
Definition: state_model.cc:367
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
#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.