Kea  2.1.7-git
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 
13 using namespace isc::util;
14 
15 namespace isc {
16 namespace http {
17 
18 const int HttpMessageParserBase::HTTP_PARSE_OK_ST;
19 const int HttpMessageParserBase::HTTP_PARSE_FAILED_ST;
20 
21 const int HttpMessageParserBase::DATA_READ_OK_EVT;
22 const int HttpMessageParserBase::NEED_MORE_DATA_EVT;
23 const int HttpMessageParserBase::MORE_DATA_PROVIDED_EVT;
24 const int HttpMessageParserBase::HTTP_PARSE_OK_EVT;
25 const int HttpMessageParserBase::HTTP_PARSE_FAILED_EVT;
26 
27 
28 HttpMessageParserBase::HttpMessageParserBase(HttpMessage& message)
29  : StateModel(), message_(message), buffer_(), buffer_pos_(0),
30  error_message_() {
31 }
32 
33 void
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 
48 bool
50  return ((getNextEvent() == NEED_MORE_DATA_EVT) ||
51  (getNextEvent() == START_EVT));
52 }
53 
54 bool
56  return ((getNextEvent() == END_EVT) &&
58 }
59 
60 void
61 HttpMessageParserBase::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 
75 std::string
76 HttpMessageParserBase::getBufferAsString(const size_t limit) const {
77  std::string message(buffer_.begin(), buffer_.end());
78  return (logFormatHttpMessage(message, limit));
79 }
80 
81 std::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 
100 void
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 
112 void
114  StateModel::verifyEvents();
115 
121 }
122 
123 void
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 
135 void
136 HttpMessageParserBase::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:
146  after_read_logic(bytes[0]);
147  break;
148  default:
149  invalidEventError(handler_name, getNextEvent());
150  }
151  }
152 }
153 
154 void
155 HttpMessageParserBase::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:
165  after_read_logic(bytes);
166  break;
167  default:
168  invalidEventError(handler_name, getNextEvent());
169  }
170  }
171 }
172 
173 void
174 HttpMessageParserBase::parseFailure(const std::string& error_msg) {
175  error_message_ = error_msg + " : " + getContextStr();
177 }
178 
179 void
180 HttpMessageParserBase::onModelFailure(const std::string& explanation) {
181  if (error_message_.empty()) {
182  error_message_ = explanation;
183  }
184 }
185 
186 void
187 HttpMessageParserBase::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 
222 void
223 HttpMessageParserBase::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 
229 void
231  switch(getNextEvent()) {
232  case HTTP_PARSE_OK_EVT:
233  message_.finalize();
235  break;
237  abortModel("HTTP message parsing failed");
238  break;
239 
240  default:
241  invalidEventError("parseEndedHandler", getNextEvent());
242  }
243 }
244 
245 bool
246 HttpMessageParserBase::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 
263 bool
264 HttpMessageParserBase::isChar(const char c) const {
265  // was (c >= 0) && (c <= 127)
266  return (c >= 0);
267 }
268 
269 bool
270 HttpMessageParserBase::isCtl(const char c) const {
271  return (((c >= 0) && (c <= 31)) || (c == 127));
272 }
273 
274 bool
275 HttpMessageParserBase::isSpecial(const char c) const {
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 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
static const int NOP_EVT
Signifies that no event has occurred.
Definition: state_model.h:292
std::string getBufferAsString(const size_t limit=0) const
Returns parser&#39;s input buffer as string.
bool isModelDone() const
Returns whether or not the model has finished execution.
Definition: state_model.cc:403
Implements a finite state machine.
Definition: state_model.h:274
static const int MORE_DATA_PROVIDED_EVT
New data provided and parsing should continue.
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 DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
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
static std::string logFormatHttpMessage(const std::string &message, const size_t limit=0)
Formats provided HTTP message for logging.
std::string getEventLabel(const int event) const
Fetches the label associated with an event value.
Definition: state_model.cc:432
const StatePtr getState(unsigned int value)
Fetches the state referred to by value.
Definition: state_model.cc:213
bool httpParseOk() const
Returns true if the message has been parsed successfully.
virtual void defineStates()
Defines states of the parser.
std::string error_message_
Error message set by onModelFailure.
void parseEndedHandler()
Handler for HTTP_PARSE_OK_ST and HTTP_PARSE_FAILED_ST.
void abortModel(const std::string &explanation)
Aborts model execution.
Definition: state_model.cc:282
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
HttpMessage & message_
Reference to the parsed HTTP message.
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
virtual void onModelFailure(const std::string &explanation)
A method called when parsing fails.
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
unsigned int getLastEvent() const
Fetches the model&#39;s last event.
Definition: state_model.cc:367
bool needData() const
Returns true if the parser needs more data to continue.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
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...
size_t buffer_pos_
Position of the next character to read from the buffer.
Definition: edns.h:19
Base class for HttpRequest and HttpResponse.
Definition: http_message.h:62
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
void invalidEventError(const std::string &handler_name, const unsigned int event)
This method is called when invalid event occurred in a particular parser state.
unsigned int getNextEvent() const
Fetches the model&#39;s next event.
Definition: state_model.cc:373
virtual void verifyEvents()
Verifies events used by the parser.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
unsigned int getCurrState() const
Fetches the model&#39;s current state.
Definition: state_model.cc:355
bool isSpecial(const char c) const
Checks if specified value is a special character.
std::string buffer_
Internal buffer from which parser reads data.
Defines the logger used by the top-level component of kea-lfc.
bool popNextFromBuffer(std::string &next, const size_t limit=1)
Tries to read next byte from buffer.
virtual void finalize()=0
Complete parsing HTTP message or creating an HTTP outbound message.
bool isCtl(const char c) const
Checks if specified value is a control value.
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 defineEvents()
Define events used by the parser.
bool isChar(const char c) const
Checks if specified value is a character.
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
static const int HTTP_PARSE_FAILED_EVT
Parsing HTTP request failed.
static const int END_ST
Final state, all the state model has reached its conclusion.
Definition: state_model.h:282
void poll()
Run the parser as long as the amount of data is sufficient.
const EventPtr & getEvent(unsigned int value)
Fetches the event referred to by value.
Definition: state_model.cc:186
static const int HTTP_PARSE_FAILED_ST
Parsing failed.
Exception thrown when an error during parsing HTTP message has occurred.
static const int END_EVT
Event issued to end the model execution.
Definition: state_model.h:298
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
void postBuffer(const void *buf, const size_t buf_size)
Provides more input data to the parser.
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...