Kea 2.7.1
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.
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 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.
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.