Kea  1.5.0
request_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-2018 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/request_parser.h>
10 #include <boost/bind.hpp>
11 #include <iostream>
12 
13 using namespace isc::util;
14 
15 namespace isc {
16 namespace http {
17 
18 const int HttpRequestParser::RECEIVE_START_ST;
19 const int HttpRequestParser::HTTP_METHOD_ST;
20 const int HttpRequestParser::HTTP_URI_ST;
21 const int HttpRequestParser::HTTP_VERSION_H_ST;
22 const int HttpRequestParser::HTTP_VERSION_T1_ST;
23 const int HttpRequestParser::HTTP_VERSION_T2_ST;
24 const int HttpRequestParser::HTTP_VERSION_P_ST;
25 const int HttpRequestParser::HTTP_VERSION_SLASH_ST;
26 const int HttpRequestParser::HTTP_VERSION_MAJOR_START_ST;
27 const int HttpRequestParser::HTTP_VERSION_MAJOR_ST;
28 const int HttpRequestParser::HTTP_VERSION_MINOR_START_ST;
29 const int HttpRequestParser::HTTP_VERSION_MINOR_ST;
30 const int HttpRequestParser::EXPECTING_NEW_LINE1_ST;
31 const int HttpRequestParser::HEADER_LINE_START_ST;
32 const int HttpRequestParser::HEADER_LWS_ST;
33 const int HttpRequestParser::HEADER_NAME_ST;
34 const int HttpRequestParser::SPACE_BEFORE_HEADER_VALUE_ST;
35 const int HttpRequestParser::HEADER_VALUE_ST;
36 const int HttpRequestParser::EXPECTING_NEW_LINE2_ST;
37 const int HttpRequestParser::EXPECTING_NEW_LINE3_ST;
38 const int HttpRequestParser::HTTP_BODY_ST;
39 
40 HttpRequestParser::HttpRequestParser(HttpRequest& request)
41  : HttpMessageParserBase(request), request_(request),
42  context_(request_.context()) {
43 }
44 
45 void
47  // Initialize dictionaries of events and states.
49 
50  // Set the current state to starting state and enter the run loop.
52 
53  // Parsing starts from here.
55 }
56 
57 void
58 HttpRequestParser::defineStates() {
59  // Call parent class implementation first.
61 
62  // Define HTTP parser specific states.
63  defineState(RECEIVE_START_ST, "RECEIVE_START_ST",
64  boost::bind(&HttpRequestParser::receiveStartHandler, this));
65 
66  defineState(HTTP_METHOD_ST, "HTTP_METHOD_ST",
67  boost::bind(&HttpRequestParser::httpMethodHandler, this));
68 
69  defineState(HTTP_URI_ST, "HTTP_URI_ST",
70  boost::bind(&HttpRequestParser::uriHandler, this));
71 
72  defineState(HTTP_VERSION_H_ST, "HTTP_VERSION_H_ST",
73  boost::bind(&HttpRequestParser::versionHTTPHandler, this, 'H',
75 
76  defineState(HTTP_VERSION_T1_ST, "HTTP_VERSION_T1_ST",
77  boost::bind(&HttpRequestParser::versionHTTPHandler, this, 'T',
79 
80  defineState(HTTP_VERSION_T2_ST, "HTTP_VERSION_T2_ST",
81  boost::bind(&HttpRequestParser::versionHTTPHandler, this, 'T',
83 
84  defineState(HTTP_VERSION_P_ST, "HTTP_VERSION_P_ST",
85  boost::bind(&HttpRequestParser::versionHTTPHandler, this, 'P',
87 
88  defineState(HTTP_VERSION_SLASH_ST, "HTTP_VERSION_SLASH_ST",
89  boost::bind(&HttpRequestParser::versionHTTPHandler, this, '/',
91 
92  defineState(HTTP_VERSION_MAJOR_START_ST, "HTTP_VERSION_MAJOR_START_ST",
93  boost::bind(&HttpRequestParser::versionNumberStartHandler, this,
95  &context_->http_version_major_));
96 
97  defineState(HTTP_VERSION_MAJOR_ST, "HTTP_VERSION_MAJOR_ST",
98  boost::bind(&HttpRequestParser::versionNumberHandler, this,
100  &context_->http_version_major_));
101 
102  defineState(HTTP_VERSION_MINOR_START_ST, "HTTP_VERSION_MINOR_START_ST",
103  boost::bind(&HttpRequestParser::versionNumberStartHandler, this,
105  &context_->http_version_minor_));
106 
107  defineState(HTTP_VERSION_MINOR_ST, "HTTP_VERSION_MINOR_ST",
108  boost::bind(&HttpRequestParser::versionNumberHandler, this,
110  &context_->http_version_minor_));
111 
112  defineState(EXPECTING_NEW_LINE1_ST, "EXPECTING_NEW_LINE1_ST",
113  boost::bind(&HttpRequestParser::expectingNewLineHandler, this,
115 
116  defineState(HEADER_LINE_START_ST, "HEADER_LINE_START_ST",
117  boost::bind(&HttpRequestParser::headerLineStartHandler, this));
118 
119  defineState(HEADER_LWS_ST, "HEADER_LWS_ST",
120  boost::bind(&HttpRequestParser::headerLwsHandler, this));
121 
122  defineState(HEADER_NAME_ST, "HEADER_NAME_ST",
123  boost::bind(&HttpRequestParser::headerNameHandler, this));
124 
125  defineState(SPACE_BEFORE_HEADER_VALUE_ST, "SPACE_BEFORE_HEADER_VALUE_ST",
126  boost::bind(&HttpRequestParser::spaceBeforeHeaderValueHandler, this));
127 
128  defineState(HEADER_VALUE_ST, "HEADER_VALUE_ST",
129  boost::bind(&HttpRequestParser::headerValueHandler, this));
130 
131  defineState(EXPECTING_NEW_LINE2_ST, "EXPECTING_NEW_LINE2",
132  boost::bind(&HttpRequestParser::expectingNewLineHandler, this,
134 
135  defineState(EXPECTING_NEW_LINE3_ST, "EXPECTING_NEW_LINE3_ST",
136  boost::bind(&HttpRequestParser::expectingNewLineHandler, this,
138 
139  defineState(HTTP_BODY_ST, "HTTP_BODY_ST",
140  boost::bind(&HttpRequestParser::bodyHandler, this));
141 }
142 
143 void
144 HttpRequestParser::receiveStartHandler() {
145  std::string bytes;
146  getNextFromBuffer(bytes);
147  if (getNextEvent() != NEED_MORE_DATA_EVT) {
148  switch(getNextEvent()) {
149  case START_EVT:
150  // The first byte should contain a first character of the
151  // HTTP method name.
152  if (!isChar(bytes[0]) || isCtl(bytes[0]) || isSpecial(bytes[0])) {
153  parseFailure("invalid first character " + std::string(1, bytes[0]) +
154  " in HTTP method name");
155 
156  } else {
157  context_->method_.push_back(bytes[0]);
159  }
160  break;
161 
162  default:
163  invalidEventError("receiveStartHandler", getNextEvent());
164  }
165  }
166 }
167 
168 void
169 HttpRequestParser::httpMethodHandler() {
170  stateWithReadHandler("httpMethodHandler", [this](const char c) {
171  // Space character terminates the HTTP method name. Next thing
172  // is the URI.
173  if (c == ' ') {
175 
176  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
177  parseFailure("invalid character " + std::string(1, c) +
178  " in HTTP method name");
179 
180  } else {
181  // Still parsing the method. Append the next character to the
182  // method name.
183  context_->method_.push_back(c);
185  }
186  });
187 }
188 
189 void
190 HttpRequestParser::uriHandler() {
191  stateWithReadHandler("uriHandler", [this](const char c) {
192  // Space character terminates the URI.
193  if (c == ' ') {
195 
196  } else if (isCtl(c)) {
197  parseFailure("control character found in HTTP URI");
198 
199  } else {
200  // Still parsing the URI. Append the next character to the
201  // method name.
202  context_->uri_.push_back(c);
204  }
205  });
206 }
207 
208 void
209 HttpRequestParser::versionHTTPHandler(const char expected_letter,
210  const unsigned int next_state) {
211  stateWithReadHandler("versionHTTPHandler",
212  [this, expected_letter, next_state](const char c) {
213  // We're handling one of the letters: 'H', 'T' or 'P'.
214  if (c == expected_letter) {
215  // The HTTP version is specified as "HTTP/X.Y". If the current
216  // character is a slash we're starting to parse major HTTP version
217  // number. Let's reset the version numbers.
218  if (c == '/') {
219  context_->http_version_major_ = 0;
220  context_->http_version_minor_ = 0;
221  }
222  // In all cases, let's transition to next specified state.
223  transition(next_state, DATA_READ_OK_EVT);
224 
225  } else {
226  // Unexpected character found. Parsing fails.
227  parseFailure("unexpected character " + std::string(1, c) +
228  " in HTTP version string");
229  }
230  });
231 }
232 
233 void
234 HttpRequestParser::versionNumberStartHandler(const unsigned int next_state,
235  unsigned int* storage) {
236  stateWithReadHandler("versionNumberStartHandler",
237  [this, next_state, storage](const char c) mutable {
238  // HTTP version number must be a digit.
239  if (isdigit(c)) {
240  // Update the version number using new digit being parsed.
241  *storage = *storage * 10 + c - '0';
242  transition(next_state, DATA_READ_OK_EVT);
243 
244  } else {
245  parseFailure("expected digit in HTTP version, found " +
246  std::string(1, c));
247  }
248  });
249 }
250 
251 void
252 HttpRequestParser::versionNumberHandler(const char following_character,
253  const unsigned int next_state,
254  unsigned int* const storage) {
255  stateWithReadHandler("versionNumberHandler",
256  [this, following_character, next_state, storage](const char c)
257  mutable {
258  // We're getting to the end of the version number, let's transition
259  // to next state.
260  if (c == following_character) {
261  transition(next_state, DATA_READ_OK_EVT);
262 
263  } else if (isdigit(c)) {
264  // Current character is a digit, so update the version number.
265  *storage = *storage * 10 + c - '0';
266 
267  } else {
268  parseFailure("expected digit in HTTP version, found " +
269  std::string(1, c));
270  }
271  });
272 }
273 
274 void
275 HttpRequestParser::expectingNewLineHandler(const unsigned int next_state) {
276  stateWithReadHandler("expectingNewLineHandler", [this, next_state](const char c) {
277  // Only a new line character is allowed in this state.
278  if (c == '\n') {
279  // If next state is HTTP_PARSE_OK_ST it means that we're
280  // parsing 3rd new line in the HTTP request message. This
281  // terminates the HTTP request (if there is no body) or marks the
282  // beginning of the body.
283  if (next_state == HTTP_PARSE_OK_ST) {
284  // Whether there is a body in this message or not, we should
285  // parse the HTTP headers to validate it and to check if there
286  // is "Content-Length" specified. The "Content-Length" is
287  // required for parsing body.
288  request_.create();
289  try {
290  // This will throw exception if there is no Content-Length.
291  uint64_t content_length =
292  request_.getHeaderValueAsUint64("Content-Length");
293  if (content_length > 0) {
294  // There is body in this request, so let's parse it.
296  }
297  } catch (const std::exception& ex) {
298  // There is no body in this message. If the body is required
299  // parsing fails.
300  if (request_.requiresBody()) {
301  parseFailure("HTTP message lacks a body");
302 
303  } else {
304  // Body not required so simply terminate parsing.
306  }
307  }
308 
309  } else {
310  // This is 1st or 2nd new line, so let's transition to the
311  // next state required by this handler.
312  transition(next_state, DATA_READ_OK_EVT);
313  }
314  } else {
315  parseFailure("expecting new line after CR, found " +
316  std::string(1, c));
317  }
318  });
319 }
320 
321 void
322 HttpRequestParser::headerLineStartHandler() {
323  stateWithReadHandler("headerLineStartHandler", [this](const char c) {
324  // If we're parsing HTTP headers and we found CR it marks the
325  // end of headers section.
326  if (c == '\r') {
328 
329  } else if (!context_->headers_.empty() && ((c == ' ') || (c == '\t'))) {
330  // New line in headers section followed by space or tab is an LWS,
331  // a line break within header value.
333 
334  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
335  parseFailure("invalid character " + std::string(1, c) +
336  " in header name");
337 
338  } else {
339  // Update header name with the parse letter.
340  context_->headers_.push_back(HttpHeaderContext());
341  context_->headers_.back().name_.push_back(c);
343  }
344  });
345 }
346 
347 void
348 HttpRequestParser::headerLwsHandler() {
349  stateWithReadHandler("headerLwsHandler", [this](const char c) {
350  if (c == '\r') {
351  // Found CR during parsing a header value. Next value
352  // should be new line.
354 
355  } else if ((c == ' ') || (c == '\t')) {
356  // Space and tab is used to mark LWS. Simply swallow
357  // this character.
359 
360  } else if (isCtl(c)) {
361  parseFailure("control character found in the HTTP header " +
362  context_->headers_.back().name_);
363 
364  } else {
365  // We're parsing header value, so let's update it.
366  context_->headers_.back().value_.push_back(c);
368  }
369  });
370 }
371 
372 void
373 HttpRequestParser::headerNameHandler() {
374  stateWithReadHandler("headerNameHandler", [this](const char c) {
375  // Colon follows header name and it has its own state.
376  if (c == ':') {
378 
379  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
380  parseFailure("invalid character " + std::string(1, c) +
381  " found in the HTTP header name");
382 
383  } else {
384  // Parsing a header name, so update it.
385  context_->headers_.back().name_.push_back(c);
387  }
388  });
389 }
390 
391 void
392 HttpRequestParser::spaceBeforeHeaderValueHandler() {
393  stateWithReadHandler("spaceBeforeHeaderValueHandler", [this](const char c) {
394  if (c == ' ') {
395  // Remove leading whitespace from the header value.
397 
398  } else if (c == '\r') {
399  // If CR found during parsing header value, it marks the end
400  // of this value.
402 
403  } else if (isCtl(c)) {
404  parseFailure("control character found in the HTTP header "
405  + context_->headers_.back().name_);
406 
407  } else {
408  // Still parsing the value, so let's update it.
409  context_->headers_.back().value_.push_back(c);
411  }
412  });
413 }
414 
415 void
416 HttpRequestParser::headerValueHandler() {
417  stateWithReadHandler("headerValueHandler", [this](const char c) {
418  // If CR found during parsing header value, it marks the end
419  // of this value.
420  if (c == '\r') {
422 
423  } else if (isCtl(c)) {
424  parseFailure("control character found in the HTTP header "
425  + context_->headers_.back().name_);
426 
427  } else {
428  // Still parsing the value, so let's update it.
429  context_->headers_.back().value_.push_back(c);
431  }
432  });
433 }
434 
435 void
436 HttpRequestParser::bodyHandler() {
437  stateWithMultiReadHandler("bodyHandler", [this](const std::string& body) {
438  // We don't validate the body at this stage. Simply record the
439  // number of characters specified within "Content-Length".
440  context_->body_ += body;
441  size_t content_length = request_.getHeaderValueAsUint64("Content-Length");
442  if (context_->body_.length() < content_length) {
443  transition(HTTP_BODY_ST, DATA_READ_OK_EVT);
444 
445  } else {
446  // If there was some extraneous data, ignore it.
447  if (context_->body_.length() > content_length) {
448  context_->body_.resize(content_length);
449  }
451  }
452  });
453 }
454 
455 } // namespace http
456 } // namespace isc
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:194
isc::http::HttpRequestParser::HTTP_VERSION_T1_ST
static const int HTTP_VERSION_T1_ST
Parsing first occurrence of "T" in "HTTP".
Definition: request_parser.h:74
isc::http::HttpMessageParserBase::HTTP_PARSE_OK_EVT
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
Definition: http_message_parser_base.h:93
isc::http::HttpRequestParser::initModel
void initModel()
Initialize the state model for parsing.
Definition: request_parser.cc:46
isc::http::HttpMessageParserBase::HTTP_PARSE_OK_ST
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
Definition: http_message_parser_base.h:72
isc::http::HttpRequestParser::RECEIVE_START_ST
static const int RECEIVE_START_ST
State indicating a beginning of parsing.
Definition: request_parser.h:62
isc::http::HttpRequestParser::HTTP_VERSION_MAJOR_START_ST
static const int HTTP_VERSION_MAJOR_START_ST
Starting to parse major HTTP version number.
Definition: request_parser.h:86
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:256
isc::http::HttpRequestParser::EXPECTING_NEW_LINE2_ST
static const int EXPECTING_NEW_LINE2_ST
Expecting new line after parsing header value.
Definition: request_parser.h:117
isc::http::HttpRequestParser::HEADER_VALUE_ST
static const int HEADER_VALUE_ST
Parsing header value.
Definition: request_parser.h:114
isc::http::HttpRequestParser::HTTP_VERSION_H_ST
static const int HTTP_VERSION_H_ST
Parsing letter "H" of "HTTP".
Definition: request_parser.h:71
isc::http::HttpMessageParserBase
Base class for the HTTP message parsers.
Definition: http_message_parser_base.h:64
isc::http::HttpRequestParser::EXPECTING_NEW_LINE1_ST
static const int EXPECTING_NEW_LINE1_ST
Parsing first new line (after HTTP version number).
Definition: request_parser.h:98
isc::http::HttpMessageParserBase::defineStates
virtual void defineStates()
Defines states of the parser.
Definition: http_message_parser_base.cc:124
isc::http::HttpRequestParser::HTTP_VERSION_T2_ST
static const int HTTP_VERSION_T2_ST
Parsing second occurrence of "T" in "HTTP".
Definition: request_parser.h:77
isc::util
Definition: edns.h:19
isc
Defines the logger used by the top-level component of kea-dhcp-ddns.
Definition: agent_parser.cc:144
isc::http::HttpRequest
Represents HTTP request message.
Definition: request.h:48
isc::http::HttpMessage::getHeaderValueAsUint64
uint64_t getHeaderValueAsUint64(const std::string &header_name) const
Returns a value of the specified HTTP header as number.
Definition: http_message.cc:79
isc::http::HttpRequestParser::HTTP_VERSION_SLASH_ST
static const int HTTP_VERSION_SLASH_ST
Parsing slash character in "HTTP/Y.X".
Definition: request_parser.h:83
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:87
isc::http::HttpRequestParser::HTTP_METHOD_ST
static const int HTTP_METHOD_ST
Parsing HTTP method, e.g. GET, POST etc.
Definition: request_parser.h:65
isc::http::HttpRequestParser::HTTP_BODY_ST
static const int HTTP_BODY_ST
Parsing body of a HTTP message.
Definition: request_parser.h:123
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::HttpRequestParser::HTTP_VERSION_P_ST
static const int HTTP_VERSION_P_ST
Parsing letter "P" in "HTTP".
Definition: request_parser.h:80
isc::http::HttpRequestParser::HTTP_VERSION_MAJOR_ST
static const int HTTP_VERSION_MAJOR_ST
Parsing major HTTP version number.
Definition: request_parser.h:89
isc::http::HttpMessageParserBase::isChar
bool isChar(const char c) const
Checks if specified value is a character.
Definition: http_message_parser_base.cc:264
isc::http::HttpMessageParserBase::isSpecial
bool isSpecial(const char c) const
Checks if specified value is a special character.
Definition: http_message_parser_base.cc:275
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::HttpRequestParser::SPACE_BEFORE_HEADER_VALUE_ST
static const int SPACE_BEFORE_HEADER_VALUE_ST
Parsing space before header value.
Definition: request_parser.h:111
isc::http::HttpRequestParser::HEADER_LINE_START_ST
static const int HEADER_LINE_START_ST
Starting to parse a header line.
Definition: request_parser.h:101
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::util::StateModel::initDictionaries
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:146
isc::http::HttpRequestParser::HTTP_VERSION_MINOR_ST
static const int HTTP_VERSION_MINOR_ST
Parsing minor HTTP version number.
Definition: request_parser.h:95
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:84
isc::http::HttpRequestParser::HTTP_VERSION_MINOR_START_ST
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.
Definition: request_parser.h:92
isc::http::HttpRequestParser::HEADER_LWS_ST
static const int HEADER_LWS_ST
Parsing LWS (Linear White Space), i.e.
Definition: request_parser.h:105
isc::http::HttpRequestParser::HTTP_URI_ST
static const int HTTP_URI_ST
Parsing URI.
Definition: request_parser.h:68
isc::util::StateModel::getCurrState
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:331
isc::http::HttpMessageParserBase::stateWithReadHandler
void stateWithReadHandler(const std::string &handler_name, boost::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::HttpRequestParser::HEADER_NAME_ST
static const int HEADER_NAME_ST
Parsing header name.
Definition: request_parser.h:108
isc::util::StateModel::getNextEvent
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:346
isc::util::StateModel::postNextEvent
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:304
isc::http::HttpMessageParserBase::isCtl
bool isCtl(const char c) const
Checks if specified value is a control value.
Definition: http_message_parser_base.cc:270
isc::util::StateModel::START_EVT
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:292
isc::http::HttpMessageParserBase::stateWithMultiReadHandler
void stateWithMultiReadHandler(const std::string &handler_name, boost::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::HttpRequest::create
virtual void create()
Commits information held in the context into the request.
Definition: request.cc:47
request_parser.h
isc::util::StateModel::setState
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:281
isc::http::HttpMessage::requiresBody
bool requiresBody() const
Checks if the body is required for the HTTP message.
Definition: http_message.cc:44
isc::http::HttpRequestParser::EXPECTING_NEW_LINE3_ST
static const int EXPECTING_NEW_LINE3_ST
Expecting second new line marking end of HTTP headers.
Definition: request_parser.h:120