Kea  1.9.9-git
response_parser.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 
9 #include <http/response_parser.h>
10 #include <functional>
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  std::bind(&HttpResponseParser::receiveStartHandler, this));
66 
67  defineState(HTTP_VERSION_T1_ST, "HTTP_VERSION_T1_ST",
68  std::bind(&HttpResponseParser::versionHTTPHandler, this, 'T',
70 
71  defineState(HTTP_VERSION_T2_ST, "HTTP_VERSION_T2_ST",
72  std::bind(&HttpResponseParser::versionHTTPHandler, this, 'T',
74 
75  defineState(HTTP_VERSION_P_ST, "HTTP_VERSION_P_ST",
76  std::bind(&HttpResponseParser::versionHTTPHandler, this, 'P',
78 
79  defineState(HTTP_VERSION_SLASH_ST, "HTTP_VERSION_SLASH_ST",
80  std::bind(&HttpResponseParser::versionHTTPHandler, this, '/',
82 
83  defineState(HTTP_VERSION_MAJOR_START_ST, "HTTP_VERSION_MAJOR_START_ST",
84  std::bind(&HttpResponseParser::numberStartHandler, this,
86  "HTTP version",
87  &context_->http_version_major_));
88 
89  defineState(HTTP_VERSION_MAJOR_ST, "HTTP_VERSION_MAJOR_ST",
90  std::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  std::bind(&HttpResponseParser::numberStartHandler, this,
98  "HTTP version",
99  &context_->http_version_minor_));
100 
101  defineState(HTTP_VERSION_MINOR_ST, "HTTP_VERSION_MINOR_ST",
102  std::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  std::bind(&HttpResponseParser::numberStartHandler, this,
110  "HTTP status code",
111  &context_->status_code_));
112 
113  defineState(HTTP_STATUS_CODE_ST, "HTTP_STATUS_CODE_ST",
114  std::bind(&HttpResponseParser::numberHandler, this,
116  "HTTP status code",
117  &context_->status_code_));
118 
119  defineState(HTTP_PHRASE_START_ST, "HTTP_PHRASE_START_ST",
120  std::bind(&HttpResponseParser::phraseStartHandler, this));
121 
122  defineState(HTTP_PHRASE_ST, "HTTP_PHRASE_ST",
123  std::bind(&HttpResponseParser::phraseHandler, this));
124 
125  defineState(EXPECTING_NEW_LINE1_ST, "EXPECTING_NEW_LINE1_ST",
126  std::bind(&HttpResponseParser::expectingNewLineHandler, this,
128 
129  defineState(HEADER_LINE_START_ST, "HEADER_LINE_START_ST",
130  std::bind(&HttpResponseParser::headerLineStartHandler, this));
131 
132  defineState(HEADER_LWS_ST, "HEADER_LWS_ST",
133  std::bind(&HttpResponseParser::headerLwsHandler, this));
134 
135  defineState(HEADER_NAME_ST, "HEADER_NAME_ST",
136  std::bind(&HttpResponseParser::headerNameHandler, this));
137 
138  defineState(SPACE_BEFORE_HEADER_VALUE_ST, "SPACE_BEFORE_HEADER_VALUE_ST",
139  std::bind(&HttpResponseParser::spaceBeforeHeaderValueHandler, this));
140 
141  defineState(HEADER_VALUE_ST, "HEADER_VALUE_ST",
142  std::bind(&HttpResponseParser::headerValueHandler, this));
143 
144  defineState(EXPECTING_NEW_LINE2_ST, "EXPECTING_NEW_LINE2",
145  std::bind(&HttpResponseParser::expectingNewLineHandler, this,
147 
148  defineState(EXPECTING_NEW_LINE3_ST, "EXPECTING_NEW_LINE3_ST",
149  std::bind(&HttpResponseParser::expectingNewLineHandler, this,
151 
152  defineState(HTTP_BODY_ST, "HTTP_BODY_ST",
153  std::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  } else {
300  }
301  } catch (const std::exception& ex) {
302  // There is no body in this message. If the body is required
303  // parsing fails.
304  if (response_.requiresBody()) {
305  parseFailure("HTTP message lacks a body");
306 
307  } else {
308  // Body not required so simply terminate parsing.
310  }
311  }
312 
313  } else {
314  // This is 1st or 2nd new line, so let's transition to the
315  // next state required by this handler.
316  transition(next_state, DATA_READ_OK_EVT);
317  }
318  } else {
319  parseFailure("expecting new line after CR, found " +
320  std::string(1, c));
321  }
322  });
323 }
324 
325 void
326 HttpResponseParser::headerLineStartHandler() {
327  stateWithReadHandler("headerLineStartHandler", [this](const char c) {
328  // If we're parsing HTTP headers and we found CR it marks the
329  // end of headers section.
330  if (c == '\r') {
332 
333  } else if (!context_->headers_.empty() && ((c == ' ') || (c == '\t'))) {
334  // New line in headers section followed by space or tab is an LWS,
335  // a line break within header value.
337 
338  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
339  parseFailure("invalid character " + std::string(1, c) +
340  " in header name");
341 
342  } else {
343  // Update header name with the parsed letter.
344  context_->headers_.push_back(HttpHeaderContext());
345  context_->headers_.back().name_.push_back(c);
347  }
348  });
349 }
350 
351 void
352 HttpResponseParser::headerLwsHandler() {
353  stateWithReadHandler("headerLwsHandler", [this](const char c) {
354  if (c == '\r') {
355  // Found CR during parsing a header value. Next value
356  // should be new line.
358 
359  } else if ((c == ' ') || (c == '\t')) {
360  // Space and tab is used to mark LWS. Simply swallow
361  // this character.
363 
364  } else if (isCtl(c)) {
365  parseFailure("control character found in the HTTP header " +
366  context_->headers_.back().name_);
367 
368  } else {
369  // We're parsing header value, so let's update it.
370  context_->headers_.back().value_.push_back(c);
372  }
373  });
374 }
375 
376 void
377 HttpResponseParser::headerNameHandler() {
378  stateWithReadHandler("headerNameHandler", [this](const char c) {
379  // Colon follows header name and it has its own state.
380  if (c == ':') {
382 
383  } else if (!isChar(c) || isCtl(c) || isSpecial(c)) {
384  parseFailure("invalid character " + std::string(1, c) +
385  " found in the HTTP header name");
386 
387  } else {
388  // Parsing a header name, so update it.
389  context_->headers_.back().name_.push_back(c);
391  }
392  });
393 }
394 
395 void
396 HttpResponseParser::spaceBeforeHeaderValueHandler() {
397  stateWithReadHandler("spaceBeforeHeaderValueHandler", [this](const char c) {
398  if (c == ' ') {
399  // Remove leading whitespace from the header value.
401 
402  } else if (c == '\r') {
403  // If CR found during parsing header value, it marks the end
404  // of this value.
406 
407  } else if (isCtl(c)) {
408  parseFailure("control character found in the HTTP header "
409  + context_->headers_.back().name_);
410 
411  } else {
412  // Still parsing the value, so let's update it.
413  context_->headers_.back().value_.push_back(c);
415  }
416  });
417 }
418 
419 void
420 HttpResponseParser::headerValueHandler() {
421  stateWithReadHandler("headerValueHandler", [this](const char c) {
422  // If CR found during parsing header value, it marks the end
423  // of this value.
424  if (c == '\r') {
426 
427  } else if (isCtl(c)) {
428  parseFailure("control character found in the HTTP header "
429  + context_->headers_.back().name_);
430 
431  } else {
432  // Still parsing the value, so let's update it.
433  context_->headers_.back().value_.push_back(c);
435  }
436  });
437 }
438 
439 void
440 HttpResponseParser::bodyHandler() {
441  stateWithMultiReadHandler("bodyHandler", [this](const std::string& body) {
442  // We don't validate the body at this stage. Simply record the
443  // number of characters specified within "Content-Length".
444  context_->body_ += body;
445  size_t content_length = response_.getHeaderValueAsUint64("Content-Length");
446  if (context_->body_.length() < content_length) {
448 
449  } else {
450  // If there was some extraneous data, ignore it.
451  if (context_->body_.length() > content_length) {
452  context_->body_.resize(content_length);
453  }
455  }
456  });
457 }
458 
459 
460 } // end of namespace isc::http
461 } // 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 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:295
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:291
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".
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.
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.
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...
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.
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 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.
bool isChar(const char c) const
Checks if specified value is a 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.
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:264
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:144
void initModel()
Initialize the state model for parsing.
static const int HEADER_LINE_START_ST
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
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:320
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.
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