Kea 2.5.8
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
10#include <functional>
11
12using namespace isc::util;
13
14namespace isc {
15namespace http {
16
40
42 : HttpMessageParserBase(response), response_(response),
43 context_(response.context()) {
44}
45
46void
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
58void
59HttpResponseParser::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
156void
157HttpResponseParser::receiveStartHandler() {
158 std::string bytes;
159 getNextFromBuffer(bytes);
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
178void
179HttpResponseParser::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
203void
204HttpResponseParser::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
222void
223HttpResponseParser::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
246void
247HttpResponseParser::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
259void
260HttpResponseParser::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
276void
277HttpResponseParser::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
325void
326HttpResponseParser::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
351void
352HttpResponseParser::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
376void
377HttpResponseParser::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
395void
396HttpResponseParser::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
419void
420HttpResponseParser::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
439void
440HttpResponseParser::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) {
447 transition(HTTP_BODY_ST, DATA_READ_OK_EVT);
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
Base class for the HTTP message parsers.
void getNextFromBuffer(std::string &bytes, const size_t limit=1)
Retrieves next bytes of data from the buffer.
static const int DATA_READ_OK_EVT
Chunk of data successfully read and parsed.
static const int NEED_MORE_DATA_EVT
Unable to proceed with parsing until new data is provided.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
bool isCtl(const char c) const
Checks if specified value is a control value.
bool isChar(const char c) const
Checks if specified value is a character.
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...
void invalidEventError(const std::string &handler_name, const unsigned int event)
This method is called when invalid event occurred in a particular parser state.
static const int HTTP_PARSE_OK_ST
Parsing successfully completed.
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 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 defineStates()
Defines states of the parser.
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 EXPECTING_NEW_LINE1_ST
Parsing first new line (after HTTP status phrase).
HttpResponseParser(HttpResponse &response)
Constructor.
static const int HTTP_VERSION_T1_ST
Parsing first occurrence of "T" in "HTTP".
static const int HEADER_VALUE_ST
Parsing header value.
static const int HTTP_VERSION_MAJOR_START_ST
Starting to parse major HTTP version number.
static const int HTTP_BODY_ST
Parsing body of a HTTP message.
static const int HTTP_VERSION_T2_ST
Parsing second occurrence of "T" in "HTTP".
static const int HEADER_NAME_ST
Parsing header name.
void initModel()
Initialize the state model for parsing.
static const int HTTP_VERSION_H_ST
Parsing letter "H" of "HTTP".
static const int HTTP_STATUS_CODE_START_ST
Starting to parse HTTP status code.
static const int EXPECTING_NEW_LINE3_ST
Expecting second new line marking end of HTTP headers.
static const int RECEIVE_START_ST
State indicating a beginning of parsing.
static const int HTTP_VERSION_P_ST
Parsing letter "P" in "HTTP".
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.
static const int HEADER_LINE_START_ST
static const int HTTP_PHRASE_ST
Parsing HTTP status phrase.
static const int HEADER_LWS_ST
Parsing LWS (Linear White Space), i.e.
static const int SPACE_BEFORE_HEADER_VALUE_ST
Parsing space before header value.
static const int HTTP_VERSION_MAJOR_ST
Parsing major HTTP version number.
static const int HTTP_STATUS_CODE_ST
Parsing HTTP status code.
static const int EXPECTING_NEW_LINE2_ST
Expecting new line after parsing header value.
static const int HTTP_VERSION_MINOR_ST
Parsing minor HTTP version number.
static const int HTTP_VERSION_SLASH_ST
Parsing slash character in "HTTP/Y.X".
static const int HTTP_PHRASE_START_ST
Starting to parse HTTP status phrase.
Represents HTTP response message.
Definition: response.h:98
virtual void create()
Commits information held in the context into the response.
Definition: response.cc:69
void initDictionaries()
Initializes the event and state dictionaries.
Definition: state_model.cc:144
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
Definition: state_model.cc:320
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
unsigned int getNextEvent() const
Fetches the model's next event.
Definition: state_model.cc:373
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 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
unsigned int getCurrState() const
Fetches the model's current state.
Definition: state_model.cc:355
Defines the logger used by the top-level component of kea-lfc.