Kea 2.6.0
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
10#include <functional>
11#include <iostream>
12
13using namespace isc::util;
14
15namespace isc {
16namespace http {
17
39
41 : HttpMessageParserBase(request), request_(request),
42 context_(request_.context()) {
43}
44
45void
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
57void
58HttpRequestParser::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
143void
144HttpRequestParser::receiveStartHandler() {
145 std::string bytes;
146 getNextFromBuffer(bytes);
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
168void
169HttpRequestParser::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
189void
190HttpRequestParser::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
208void
209HttpRequestParser::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
233void
234HttpRequestParser::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
251void
252HttpRequestParser::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
274void
275HttpRequestParser::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
323void
324HttpRequestParser::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
349void
350HttpRequestParser::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
374void
375HttpRequestParser::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
393void
394HttpRequestParser::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
417void
418HttpRequestParser::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
437void
438HttpRequestParser::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) {
445 transition(HTTP_BODY_ST, DATA_READ_OK_EVT);
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
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.
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 version number).
static const int HTTP_URI_ST
Parsing URI.
static const int HTTP_VERSION_MAJOR_ST
Parsing major HTTP version number.
static const int HTTP_VERSION_SLASH_ST
Parsing slash character in "HTTP/Y.X".
static const int SPACE_BEFORE_HEADER_VALUE_ST
Parsing space before header value.
static const int HTTP_BODY_ST
Parsing body of a HTTP message.
static const int HTTP_VERSION_MAJOR_START_ST
Starting to parse major HTTP version number.
static const int EXPECTING_NEW_LINE3_ST
Expecting second new line marking end of HTTP headers.
static const int HEADER_NAME_ST
Parsing header name.
static const int RECEIVE_START_ST
State indicating a beginning of parsing.
void initModel()
Initialize the state model for parsing.
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 HEADER_LINE_START_ST
Starting to parse a header line.
static const int HTTP_VERSION_H_ST
Parsing letter "H" of "HTTP".
static const int HTTP_VERSION_MINOR_START_ST
Starting to parse minor HTTP version number.
static const int HTTP_METHOD_ST
Parsing HTTP method, e.g. GET, POST etc.
static const int HEADER_VALUE_ST
Parsing header value.
HttpRequestParser(HttpRequest &request)
Constructor.
static const int EXPECTING_NEW_LINE2_ST
Expecting new line after parsing header value.
static const int HTTP_VERSION_P_ST
Parsing letter "P" in "HTTP".
static const int HTTP_VERSION_T2_ST
Parsing second occurrence of "T" in "HTTP".
static const int HTTP_VERSION_T1_ST
Parsing first occurrence of "T" in "HTTP".
Represents HTTP request message.
Definition: request.h:57
virtual void create()
Commits information held in the context into the request.
Definition: request.cc:68
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.