Kea  1.9.9-git
request_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2016-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 
9 #include <http/request_parser.h>
10 #include <functional>
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  std::bind(&HttpRequestParser::receiveStartHandler, this));
65 
66  defineState(HTTP_METHOD_ST, "HTTP_METHOD_ST",
67  std::bind(&HttpRequestParser::httpMethodHandler, this));
68 
69  defineState(HTTP_URI_ST, "HTTP_URI_ST",
70  std::bind(&HttpRequestParser::uriHandler, this));
71 
72  defineState(HTTP_VERSION_H_ST, "HTTP_VERSION_H_ST",
73  std::bind(&HttpRequestParser::versionHTTPHandler, this, 'H',
75 
76  defineState(HTTP_VERSION_T1_ST, "HTTP_VERSION_T1_ST",
77  std::bind(&HttpRequestParser::versionHTTPHandler, this, 'T',
79 
80  defineState(HTTP_VERSION_T2_ST, "HTTP_VERSION_T2_ST",
81  std::bind(&HttpRequestParser::versionHTTPHandler, this, 'T',
83 
84  defineState(HTTP_VERSION_P_ST, "HTTP_VERSION_P_ST",
85  std::bind(&HttpRequestParser::versionHTTPHandler, this, 'P',
87 
88  defineState(HTTP_VERSION_SLASH_ST, "HTTP_VERSION_SLASH_ST",
89  std::bind(&HttpRequestParser::versionHTTPHandler, this, '/',
91 
92  defineState(HTTP_VERSION_MAJOR_START_ST, "HTTP_VERSION_MAJOR_START_ST",
93  std::bind(&HttpRequestParser::versionNumberStartHandler, this,
95  &context_->http_version_major_));
96 
97  defineState(HTTP_VERSION_MAJOR_ST, "HTTP_VERSION_MAJOR_ST",
98  std::bind(&HttpRequestParser::versionNumberHandler, this,
100  &context_->http_version_major_));
101 
102  defineState(HTTP_VERSION_MINOR_START_ST, "HTTP_VERSION_MINOR_START_ST",
103  std::bind(&HttpRequestParser::versionNumberStartHandler, this,
105  &context_->http_version_minor_));
106 
107  defineState(HTTP_VERSION_MINOR_ST, "HTTP_VERSION_MINOR_ST",
108  std::bind(&HttpRequestParser::versionNumberHandler, this,
110  &context_->http_version_minor_));
111 
112  defineState(EXPECTING_NEW_LINE1_ST, "EXPECTING_NEW_LINE1_ST",
113  std::bind(&HttpRequestParser::expectingNewLineHandler, this,
115 
116  defineState(HEADER_LINE_START_ST, "HEADER_LINE_START_ST",
117  std::bind(&HttpRequestParser::headerLineStartHandler, this));
118 
119  defineState(HEADER_LWS_ST, "HEADER_LWS_ST",
120  std::bind(&HttpRequestParser::headerLwsHandler, this));
121 
122  defineState(HEADER_NAME_ST, "HEADER_NAME_ST",
123  std::bind(&HttpRequestParser::headerNameHandler, this));
124 
125  defineState(SPACE_BEFORE_HEADER_VALUE_ST, "SPACE_BEFORE_HEADER_VALUE_ST",
126  std::bind(&HttpRequestParser::spaceBeforeHeaderValueHandler, this));
127 
128  defineState(HEADER_VALUE_ST, "HEADER_VALUE_ST",
129  std::bind(&HttpRequestParser::headerValueHandler, this));
130 
131  defineState(EXPECTING_NEW_LINE2_ST, "EXPECTING_NEW_LINE2",
132  std::bind(&HttpRequestParser::expectingNewLineHandler, this,
134 
135  defineState(EXPECTING_NEW_LINE3_ST, "EXPECTING_NEW_LINE3_ST",
136  std::bind(&HttpRequestParser::expectingNewLineHandler, this,
138 
139  defineState(HTTP_BODY_ST, "HTTP_BODY_ST",
140  std::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  } else {
298  }
299  } catch (const std::exception& ex) {
300  // There is no body in this message. If the body is required
301  // parsing fails.
302  if (request_.requiresBody()) {
303  parseFailure("HTTP message lacks a body");
304 
305  } else {
306  // Body not required so simply terminate parsing.
308  }
309  }
310 
311  } else {
312  // This is 1st or 2nd new line, so let's transition to the
313  // next state required by this handler.
314  transition(next_state, DATA_READ_OK_EVT);
315  }
316  } else {
317  parseFailure("expecting new line after CR, found " +
318  std::string(1, c));
319  }
320  });
321 }
322 
323 void
324 HttpRequestParser::headerLineStartHandler() {
325  stateWithReadHandler("headerLineStartHandler", [this](const char c) {
326  // If we're parsing HTTP headers and we found CR it marks the
327  // end of headers section.
328  if (c == '\r') {
330 
331  } else if (!context_->headers_.empty() && ((c == ' ') || (c == '\t'))) {
332  // New line in headers section followed by space or tab is an LWS,
333  // a line break within header value.
335 
336  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
337  parseFailure("invalid character " + std::string(1, c) +
338  " in header name");
339 
340  } else {
341  // Update header name with the parse letter.
342  context_->headers_.push_back(HttpHeaderContext());
343  context_->headers_.back().name_.push_back(c);
345  }
346  });
347 }
348 
349 void
350 HttpRequestParser::headerLwsHandler() {
351  stateWithReadHandler("headerLwsHandler", [this](const char c) {
352  if (c == '\r') {
353  // Found CR during parsing a header value. Next value
354  // should be new line.
356 
357  } else if ((c == ' ') || (c == '\t')) {
358  // Space and tab is used to mark LWS. Simply swallow
359  // this character.
361 
362  } else if (isCtl(c)) {
363  parseFailure("control character found in the HTTP header " +
364  context_->headers_.back().name_);
365 
366  } else {
367  // We're parsing header value, so let's update it.
368  context_->headers_.back().value_.push_back(c);
370  }
371  });
372 }
373 
374 void
375 HttpRequestParser::headerNameHandler() {
376  stateWithReadHandler("headerNameHandler", [this](const char c) {
377  // Colon follows header name and it has its own state.
378  if (c == ':') {
380 
381  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
382  parseFailure("invalid character " + std::string(1, c) +
383  " found in the HTTP header name");
384 
385  } else {
386  // Parsing a header name, so update it.
387  context_->headers_.back().name_.push_back(c);
389  }
390  });
391 }
392 
393 void
394 HttpRequestParser::spaceBeforeHeaderValueHandler() {
395  stateWithReadHandler("spaceBeforeHeaderValueHandler", [this](const char c) {
396  if (c == ' ') {
397  // Remove leading whitespace from the header value.
399 
400  } else if (c == '\r') {
401  // If CR found during parsing header value, it marks the end
402  // of this value.
404 
405  } else if (isCtl(c)) {
406  parseFailure("control character found in the HTTP header "
407  + context_->headers_.back().name_);
408 
409  } else {
410  // Still parsing the value, so let's update it.
411  context_->headers_.back().value_.push_back(c);
413  }
414  });
415 }
416 
417 void
418 HttpRequestParser::headerValueHandler() {
419  stateWithReadHandler("headerValueHandler", [this](const char c) {
420  // If CR found during parsing header value, it marks the end
421  // of this value.
422  if (c == '\r') {
424 
425  } else if (isCtl(c)) {
426  parseFailure("control character found in the HTTP header "
427  + context_->headers_.back().name_);
428 
429  } else {
430  // Still parsing the value, so let's update it.
431  context_->headers_.back().value_.push_back(c);
433  }
434  });
435 }
436 
437 void
438 HttpRequestParser::bodyHandler() {
439  stateWithMultiReadHandler("bodyHandler", [this](const std::string& body) {
440  // We don't validate the body at this stage. Simply record the
441  // number of characters specified within "Content-Length".
442  context_->body_ += body;
443  size_t content_length = request_.getHeaderValueAsUint64("Content-Length");
444  if (context_->body_.length() < content_length) {
446 
447  } else {
448  // If there was some extraneous data, ignore it.
449  if (context_->body_.length() > content_length) {
450  context_->body_.resize(content_length);
451  }
453  }
454  });
455 }
456 
457 } // namespace http
458 } // 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 HTTP_METHOD_ST
Parsing HTTP method, e.g. GET, POST etc.
static const int HTTP_VERSION_SLASH_ST
Parsing slash character in "HTTP/Y.X".
static const int HTTP_VERSION_T2_ST
Parsing second occurrence of "T" in "HTTP".
static const int HTTP_VERSION_H_ST
Parsing letter "H" of "HTTP".
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
static const int HTTP_VERSION_MAJOR_START_ST
Starting to parse major HTTP version number.
static const int HEADER_VALUE_ST
Parsing header value.
virtual void defineStates()
Defines states of the parser.
static const int HTTP_VERSION_MINOR_ST
Parsing minor HTTP version number.
static const int RECEIVE_START_ST
State indicating a beginning of parsing.
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:295
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:291
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
bool isSpecial(const char c) const
Checks if specified value is a special character.
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
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...
static const int HEADER_LWS_ST
Parsing LWS (Linear White Space), i.e.
Definition: edns.h:19
static const int SPACE_BEFORE_HEADER_VALUE_ST
Parsing space before header value.
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.
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.
bool isCtl(const char c) const
Checks if specified value is a control value.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
static const int EXPECTING_NEW_LINE2_ST
Expecting new line after parsing header value.
static const int EXPECTING_NEW_LINE3_ST
Expecting second new line marking end of HTTP headers.
bool isChar(const char c) const
Checks if specified value is a character.
Defines the logger used by the top-level component of kea-dhcp-ddns.
static const int HEADER_NAME_ST
Parsing header name.
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
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
virtual void create()
Commits information held in the context into the request.
Definition: request.cc:58
static const int HEADER_LINE_START_ST
Starting to parse a header line.
static const int HTTP_URI_ST
Parsing URI.
Base class for the HTTP message parsers.
static const int HTTP_VERSION_P_ST
Parsing letter "P" in "HTTP".
Represents HTTP request message.
Definition: request.h:49
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
static const int HTTP_VERSION_T1_ST
Parsing first occurrence of "T" in "HTTP".
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
static const int HTTP_VERSION_MAJOR_ST
Parsing major HTTP version number.
static const int EXPECTING_NEW_LINE1_ST
Parsing first new line (after HTTP version number).
uint64_t getHeaderValueAsUint64(const std::string &header_name) const
Returns a value of the specified HTTP header as number.
Definition: http_message.cc:79
void initModel()
Initialize the state model for parsing.
static const int HTTP_BODY_ST
Parsing body of a HTTP message.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
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 requiresBody() const
Checks if the body is required for the HTTP message.
Definition: http_message.cc:44