Kea 2.7.6
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) {
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.
bool isSpecial(const signed char c) const
Checks if specified value is a special character.
void parseFailure(const std::string &error_msg)
Transition parser to failure state.
bool isChar(const signed 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 isCtl(const signed char c) const
Checks if specified value is a control value.
static const int HTTP_PARSE_OK_EVT
Parsing HTTP request successful.
virtual void defineStates() override
Defines states of the parser.
bool requiresBody() const
Checks if the body is required for the HTTP message.
uint64_t getHeaderValueAsUint64(const std::string &header_name) const
Returns a value of the specified HTTP header as number.
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.
void postNextEvent(unsigned int event)
Sets the next event to the given event value.
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.
unsigned int getNextEvent() const
Fetches the model's next event.
void transition(unsigned int state, unsigned int event)
Sets up the model to transition into given state with a given event.
static const int START_EVT
Event issued to start the model execution.
void setState(unsigned int state)
Sets the current state to the given state value.
unsigned int getCurrState() const
Fetches the model's current state.
Defines the logger used by the top-level component of kea-lfc.