Kea  1.5.0
response_parser.cc
Go to the documentation of this file.
1 // Copyright (C) 2017-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/response_parser.h>
10 #include <boost/bind.hpp>
11 
12 using namespace isc::util;
13 
14 namespace isc {
15 namespace http {
16 
17 const int HttpResponseParser::RECEIVE_START_ST;
18 const int HttpResponseParser::HTTP_VERSION_H_ST;
19 const int HttpResponseParser::HTTP_VERSION_T1_ST;
20 const int HttpResponseParser::HTTP_VERSION_T2_ST;
21 const int HttpResponseParser::HTTP_VERSION_P_ST;
22 const int HttpResponseParser::HTTP_VERSION_SLASH_ST;
23 const int HttpResponseParser::HTTP_VERSION_MAJOR_START_ST;
24 const int HttpResponseParser::HTTP_VERSION_MAJOR_ST;
25 const int HttpResponseParser::HTTP_VERSION_MINOR_START_ST;
26 const int HttpResponseParser::HTTP_VERSION_MINOR_ST;
27 const int HttpResponseParser::HTTP_STATUS_CODE_START_ST;
28 const int HttpResponseParser::HTTP_STATUS_CODE_ST;
29 const int HttpResponseParser::HTTP_PHRASE_START_ST;
30 const int HttpResponseParser::HTTP_PHRASE_ST;
31 const int HttpResponseParser::EXPECTING_NEW_LINE1_ST;
32 const int HttpResponseParser::HEADER_LINE_START_ST;
33 const int HttpResponseParser::HEADER_LWS_ST;
34 const int HttpResponseParser::HEADER_NAME_ST;
35 const int HttpResponseParser::SPACE_BEFORE_HEADER_VALUE_ST;
36 const int HttpResponseParser::HEADER_VALUE_ST;
37 const int HttpResponseParser::EXPECTING_NEW_LINE2_ST;
38 const int HttpResponseParser::EXPECTING_NEW_LINE3_ST;
39 const int HttpResponseParser::HTTP_BODY_ST;
40 
41 HttpResponseParser::HttpResponseParser(HttpResponse& response)
42  : HttpMessageParserBase(response), response_(response),
43  context_(response.context()) {
44 }
45 
46 void
48  // Initialize dictionaries of events and states.
50 
51  // Set the current state to starting state and enter the run loop.
53 
54  // Parsing starts from here.
56 }
57 
58 void
59 HttpResponseParser::defineStates() {
60  // Call parent class implementation first.
62 
63  // Define HTTP parser specific states.
64  defineState(RECEIVE_START_ST, "RECEIVE_START_ST",
65  boost::bind(&HttpResponseParser::receiveStartHandler, this));
66 
67  defineState(HTTP_VERSION_T1_ST, "HTTP_VERSION_T1_ST",
68  boost::bind(&HttpResponseParser::versionHTTPHandler, this, 'T',
70 
71  defineState(HTTP_VERSION_T2_ST, "HTTP_VERSION_T2_ST",
72  boost::bind(&HttpResponseParser::versionHTTPHandler, this, 'T',
74 
75  defineState(HTTP_VERSION_P_ST, "HTTP_VERSION_P_ST",
76  boost::bind(&HttpResponseParser::versionHTTPHandler, this, 'P',
78 
79  defineState(HTTP_VERSION_SLASH_ST, "HTTP_VERSION_SLASH_ST",
80  boost::bind(&HttpResponseParser::versionHTTPHandler, this, '/',
82 
83  defineState(HTTP_VERSION_MAJOR_START_ST, "HTTP_VERSION_MAJOR_START_ST",
84  boost::bind(&HttpResponseParser::numberStartHandler, this,
86  "HTTP version",
87  &context_->http_version_major_));
88 
89  defineState(HTTP_VERSION_MAJOR_ST, "HTTP_VERSION_MAJOR_ST",
90  boost::bind(&HttpResponseParser::numberHandler, this,
92  "HTTP version",
93  &context_->http_version_major_));
94 
95  defineState(HTTP_VERSION_MINOR_START_ST, "HTTP_VERSION_MINOR_START_ST",
96  boost::bind(&HttpResponseParser::numberStartHandler, this,
98  "HTTP version",
99  &context_->http_version_minor_));
100 
101  defineState(HTTP_VERSION_MINOR_ST, "HTTP_VERSION_MINOR_ST",
102  boost::bind(&HttpResponseParser::numberHandler, this,
104  "HTTP version",
105  &context_->http_version_minor_));
106 
107  defineState(HTTP_STATUS_CODE_START_ST, "HTTP_STATUS_CODE_START_ST",
108  boost::bind(&HttpResponseParser::numberStartHandler, this,
110  "HTTP status code",
111  &context_->status_code_));
112 
113  defineState(HTTP_STATUS_CODE_ST, "HTTP_STATUS_CODE_ST",
114  boost::bind(&HttpResponseParser::numberHandler, this,
116  "HTTP status code",
117  &context_->status_code_));
118 
119  defineState(HTTP_PHRASE_START_ST, "HTTP_PHRASE_START_ST",
120  boost::bind(&HttpResponseParser::phraseStartHandler, this));
121 
122  defineState(HTTP_PHRASE_ST, "HTTP_PHRASE_ST",
123  boost::bind(&HttpResponseParser::phraseHandler, this));
124 
125  defineState(EXPECTING_NEW_LINE1_ST, "EXPECTING_NEW_LINE1_ST",
126  boost::bind(&HttpResponseParser::expectingNewLineHandler, this,
128 
129  defineState(HEADER_LINE_START_ST, "HEADER_LINE_START_ST",
130  boost::bind(&HttpResponseParser::headerLineStartHandler, this));
131 
132  defineState(HEADER_LWS_ST, "HEADER_LWS_ST",
133  boost::bind(&HttpResponseParser::headerLwsHandler, this));
134 
135  defineState(HEADER_NAME_ST, "HEADER_NAME_ST",
136  boost::bind(&HttpResponseParser::headerNameHandler, this));
137 
138  defineState(SPACE_BEFORE_HEADER_VALUE_ST, "SPACE_BEFORE_HEADER_VALUE_ST",
139  boost::bind(&HttpResponseParser::spaceBeforeHeaderValueHandler, this));
140 
141  defineState(HEADER_VALUE_ST, "HEADER_VALUE_ST",
142  boost::bind(&HttpResponseParser::headerValueHandler, this));
143 
144  defineState(EXPECTING_NEW_LINE2_ST, "EXPECTING_NEW_LINE2",
145  boost::bind(&HttpResponseParser::expectingNewLineHandler, this,
147 
148  defineState(EXPECTING_NEW_LINE3_ST, "EXPECTING_NEW_LINE3_ST",
149  boost::bind(&HttpResponseParser::expectingNewLineHandler, this,
151 
152  defineState(HTTP_BODY_ST, "HTTP_BODY_ST",
153  boost::bind(&HttpResponseParser::bodyHandler, this));
154 }
155 
156 void
157 HttpResponseParser::receiveStartHandler() {
158  std::string bytes;
159  getNextFromBuffer(bytes);
160  if (getNextEvent() != NEED_MORE_DATA_EVT) {
161  switch(getNextEvent()) {
162  case START_EVT:
163  if (bytes[0] == 'H') {
165 
166  } else {
167  parseFailure("unexpected first character " + std::string(1, bytes[0]) +
168  ": expected \'H\'");
169  }
170  break;
171 
172  default:
173  invalidEventError("receiveStartHandler", getNextEvent());
174  }
175  }
176 }
177 
178 void
179 HttpResponseParser::versionHTTPHandler(const char expected_letter,
180  const unsigned int next_state) {
181  stateWithReadHandler("versionHTTPHandler",
182  [this, expected_letter, next_state](const char c) {
183  // We're handling one of the letters: 'H', 'T' or 'P'.
184  if (c == expected_letter) {
185  // The HTTP version is specified as "HTTP/X.Y". If the current
186  // character is a slash we're starting to parse major HTTP version
187  // number. Let's reset the version numbers.
188  if (c == '/') {
189  context_->http_version_major_ = 0;
190  context_->http_version_minor_ = 0;
191  }
192  // In all cases, let's transition to next specified state.
193  transition(next_state, DATA_READ_OK_EVT);
194 
195  } else {
196  // Unexpected character found. Parsing fails.
197  parseFailure("unexpected character " + std::string(1, c) +
198  " in HTTP version string");
199  }
200  });
201 }
202 
203 void
204 HttpResponseParser::numberStartHandler(const unsigned int next_state,
205  const std::string& number_name,
206  unsigned int* storage) {
207  stateWithReadHandler("numberStartHandler",
208  [this, next_state, number_name, storage](const char c) mutable {
209  // HTTP version number must be a digit.
210  if (isdigit(c)) {
211  // Update the version number using new digit being parsed.
212  *storage = *storage * 10 + c - '0';
213  transition(next_state, DATA_READ_OK_EVT);
214 
215  } else {
216  parseFailure("expected digit in " + number_name + ", found " +
217  std::string(1, c));
218  }
219  });
220 }
221 
222 void
223 HttpResponseParser::numberHandler(const char following_character,
224  const unsigned int next_state,
225  const std::string& number_name,
226  unsigned int* const storage) {
227  stateWithReadHandler("numberHandler",
228  [this, following_character, number_name, next_state, storage](const char c)
229  mutable {
230  // We're getting to the end of the version number, let's transition
231  // to next state.
232  if (c == following_character) {
233  transition(next_state, DATA_READ_OK_EVT);
234 
235  } else if (isdigit(c)) {
236  // Current character is a digit, so update the version number.
237  *storage = *storage * 10 + c - '0';
238 
239  } else {
240  parseFailure("expected digit in " + number_name + ", found " +
241  std::string(1, c));
242  }
243  });
244 }
245 
246 void
247 HttpResponseParser::phraseStartHandler() {
248  stateWithReadHandler("phraseStartHandler", [this](const char c) {
249  if (!isChar(c) || isCtl(c)) {
250  parseFailure("invalid first character " + std::string(1, c) +
251  " in HTTP phrase");
252  } else {
253  context_->phrase_.push_back(c);
255  }
256  });
257 }
258 
259 void
260 HttpResponseParser::phraseHandler() {
261  stateWithReadHandler("phraseHandler", [this](const char c) {
262  if (c == '\r') {
264 
265  } else if (!isChar(c) || isCtl(c)) {
266  parseFailure("invalid character " + std::string(1, c) +
267  " in HTTP phrase");
268 
269  } else {
270  context_->phrase_.push_back(c);
272  }
273  });
274 }
275 
276 void
277 HttpResponseParser::expectingNewLineHandler(const unsigned int next_state) {
278  stateWithReadHandler("expectingNewLineHandler", [this, next_state](const char c) {
279  // Only a new line character is allowed in this state.
280  if (c == '\n') {
281  // If next state is HTTP_PARSE_OK_ST it means that we're
282  // parsing 3rd new line in the HTTP request message. This
283  // terminates the HTTP request (if there is no body) or marks the
284  // beginning of the body.
285  if (next_state == HTTP_PARSE_OK_ST) {
286  // Whether there is a body in this message or not, we should
287  // parse the HTTP headers to validate it and to check if there
288  // is "Content-Length" specified. The "Content-Length" is
289  // required for parsing body.
290  response_.create();
291  try {
292  // This will throw exception if there is no Content-Length.
293  uint64_t content_length =
294  response_.getHeaderValueAsUint64("Content-Length");
295  if (content_length > 0) {
296  // There is body in this request, so let's parse it.
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 (response_.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 HttpResponseParser::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 parsed letter.
342  context_->headers_.push_back(HttpHeaderContext());
343  context_->headers_.back().name_.push_back(c);
345  }
346  });
347 }
348 
349 void
350 HttpResponseParser::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 HttpResponseParser::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 HttpResponseParser::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 HttpResponseParser::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 HttpResponseParser::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 = response_.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 
458 } // end of namespace isc::http
459 } // 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:194
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...
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
virtual void defineStates()
Defines states of the parser.
static const int HTTP_PHRASE_ST
Parsing HTTP status phrase.
static const int START_EVT
Event issued to start the model execution.
Definition: state_model.h:292
static const int SPACE_BEFORE_HEADER_VALUE_ST
Parsing space before header value.
void setState(unsigned int state)
Sets the current state to the given state value.
Definition: state_model.cc:281
static const int HEADER_NAME_ST
Parsing header name.
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
static const int HTTP_VERSION_SLASH_ST
Parsing slash character in "HTTP/Y.X".
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...
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
virtual void create()
Commits information held in the context into the response.
Definition: response.cc:69
static const int HTTP_VERSION_MAJOR_START_ST
Starting to parse major HTTP version number.
static const int HTTP_VERSION_MAJOR_ST
Parsing major HTTP version number.
Definition: edns.h:19
Represents HTTP response message.
Definition: response.h:98
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
static const int HTTP_VERSION_T2_ST
Parsing second occurrence of "T" in "HTTP".
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's next event.
Definition: state_model.cc:346
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
static const int HEADER_LWS_ST
Parsing LWS (Linear White Space), i.e.
static const int HTTP_VERSION_MINOR_ST
Parsing minor HTTP version number.
static const int HTTP_STATUS_CODE_ST
Parsing HTTP status code.
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:331
bool isSpecial(const char c) const
Checks if specified value is a special character.
static const int EXPECTING_NEW_LINE1_ST
Parsing first new line (after HTTP status phrase).
static const int HTTP_PHRASE_START_ST
Starting to parse HTTP status phrase.
Defines the logger used by the top-level component of kea-dhcp-ddns.
bool isCtl(const char c) const
Checks if specified value is a control value.
static const int HEADER_VALUE_ST
Parsing header value.
static const int RECEIVE_START_ST
State indicating a beginning of parsing.
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
bool isChar(const char c) const
Checks if specified value is a character.
static const int EXPECTING_NEW_LINE3_ST
Expecting second new line marking end of HTTP headers.
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
static const int HTTP_VERSION_P_ST
Parsing letter "P" in "HTTP".
Base class for the HTTP message parsers.
static const int HTTP_STATUS_CODE_START_ST
Starting to parse HTTP status code.
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:146
void initModel()
Initialize the state model for parsing.
static const int HEADER_LINE_START_ST
bool requiresBody() const
Checks if the body is required for the HTTP message.
Definition: http_message.cc:44
uint64_t getHeaderValueAsUint64(const std::string &header_name) const
Returns a value of the specified HTTP header as number.
Definition: http_message.cc:79
static const int HTTP_BODY_ST
Parsing body of a HTTP message.
static const int EXPECTING_NEW_LINE2_ST
Expecting new line after parsing header value.
static const int HTTP_VERSION_T1_ST
Parsing first occurrence of "T" in "HTTP".
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:304
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.