1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
|
// Copyright (C) 2016-2018,2021 Internet Systems Consortium, Inc. ("ISC")
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
#ifndef HTTP_REQUEST_PARSER_H
#define HTTP_REQUEST_PARSER_H
#include <http/http_message_parser_base.h>
#include <http/request.h>
#include <boost/shared_ptr.hpp>
namespace isc {
namespace http {
class HttpRequestParser;
/// @brief Pointer to the @ref HttpRequestParser.
typedef boost::shared_ptr<HttpRequestParser> HttpRequestParserPtr;
/// @brief A generic parser for HTTP requests.
///
/// This class implements a parser for HTTP requests. The parser derives from
/// @ref HttpMessageParserBase class and implements its own state machine on
/// top of it. The states of the parser reflect various parts of the HTTP
/// message being parsed, e.g. parsing HTTP method, parsing URI, parsing
/// message body etc. The descriptions of all parser states are provided
/// below together with the constants defining these states.
///
/// The request parser validates the syntax of the received message as it
/// progresses with parsing the data. Though, it doesn't interpret the received
/// data until the whole message is parsed. In most cases we want to apply some
/// restrictions on the message content, e.g. Kea Control API requires that
/// commands are sent using HTTP POST, with a JSON command being carried in a
/// message body. The parser doesn't verify if the message meets these
/// restrictions until the whole message is parsed, i.e. stored in the
/// @ref HttpRequestContext object. This object is associated with a
/// @ref HttpRequest object (or its derivation). When the parsing is completed,
/// the @ref HttpRequest::create method is called to retrieve the data from
/// the @ref HttpRequestContext and interpret the data. In particular, the
/// @ref HttpRequest or its derivation checks if the received message meets
/// desired restrictions.
///
/// Kea Control API uses @ref PostHttpRequestJson class (which derives from the
/// @ref HttpRequest) to interpret received request. This class requires
/// that the HTTP request uses POST method and contains the following headers:
/// - Content-Type: application/json,
/// - Content-Length
///
/// If any of these restrictions is not met in the received message, an
/// exception will be thrown, thereby @ref HttpRequestParser will fail parsing
/// the message.
class HttpRequestParser : public HttpMessageParserBase {
public:
/// @name States supported by the HttpRequestParser.
///
//@{
/// @brief State indicating a beginning of parsing.
static const int RECEIVE_START_ST = SM_DERIVED_STATE_MIN + 1;
/// @brief Parsing HTTP method, e.g. GET, POST etc.
static const int HTTP_METHOD_ST = SM_DERIVED_STATE_MIN + 2;
/// @brief Parsing URI.
static const int HTTP_URI_ST = SM_DERIVED_STATE_MIN + 3;
/// @brief Parsing letter "H" of "HTTP".
static const int HTTP_VERSION_H_ST = SM_DERIVED_STATE_MIN + 4;
/// @brief Parsing first occurrence of "T" in "HTTP".
static const int HTTP_VERSION_T1_ST = SM_DERIVED_STATE_MIN + 5;
/// @brief Parsing second occurrence of "T" in "HTTP".
static const int HTTP_VERSION_T2_ST = SM_DERIVED_STATE_MIN + 6;
/// @brief Parsing letter "P" in "HTTP".
static const int HTTP_VERSION_P_ST = SM_DERIVED_STATE_MIN + 7;
/// @brief Parsing slash character in "HTTP/Y.X"
static const int HTTP_VERSION_SLASH_ST = SM_DERIVED_STATE_MIN + 8;
/// @brief Starting to parse major HTTP version number.
static const int HTTP_VERSION_MAJOR_START_ST = SM_DERIVED_STATE_MIN + 9;
/// @brief Parsing major HTTP version number.
static const int HTTP_VERSION_MAJOR_ST = SM_DERIVED_STATE_MIN + 10;
/// @brief Starting to parse minor HTTP version number.
static const int HTTP_VERSION_MINOR_START_ST = SM_DERIVED_STATE_MIN + 11;
/// @brief Parsing minor HTTP version number.
static const int HTTP_VERSION_MINOR_ST = SM_DERIVED_STATE_MIN + 12;
/// @brief Parsing first new line (after HTTP version number).
static const int EXPECTING_NEW_LINE1_ST = SM_DERIVED_STATE_MIN + 13;
/// @brief Starting to parse a header line.
static const int HEADER_LINE_START_ST = SM_DERIVED_STATE_MIN + 14;
/// @brief Parsing LWS (Linear White Space), i.e. new line with a space
/// or tab character while parsing a HTTP header.
static const int HEADER_LWS_ST = SM_DERIVED_STATE_MIN + 15;
/// @brief Parsing header name.
static const int HEADER_NAME_ST = SM_DERIVED_STATE_MIN + 16;
/// @brief Parsing space before header value.
static const int SPACE_BEFORE_HEADER_VALUE_ST = SM_DERIVED_STATE_MIN + 17;
/// @brief Parsing header value.
static const int HEADER_VALUE_ST = SM_DERIVED_STATE_MIN + 18;
/// @brief Expecting new line after parsing header value.
static const int EXPECTING_NEW_LINE2_ST = SM_DERIVED_STATE_MIN + 19;
/// @brief Expecting second new line marking end of HTTP headers.
static const int EXPECTING_NEW_LINE3_ST = SM_DERIVED_STATE_MIN + 20;
/// @brief Parsing body of a HTTP message.
static const int HTTP_BODY_ST = SM_DERIVED_STATE_MIN + 21;
//@}
/// @brief Constructor.
///
/// Creates new instance of the parser.
///
/// @param request Reference to the @ref HttpRequest object or its
/// derivation that should be used to validate the parsed request and
/// to be used as a container for the parsed request.
explicit HttpRequestParser(HttpRequest& request);
/// @brief Initialize the state model for parsing.
///
/// This method must be called before parsing the request, i.e. before
/// calling @ref HttpRequestParser::poll. It initializes dictionaries of
/// states and events, and sets the initial model state to RECEIVE_START_ST.
void initModel();
private:
/// @brief Defines states of the parser.
virtual void defineStates();
/// @name State handlers.
///
//@{
/// @brief Handler for RECEIVE_START_ST.
void receiveStartHandler();
/// @brief Handler for HTTP_METHOD_ST.
void httpMethodHandler();
/// @brief Handler for HTTP_URI_ST.
void uriHandler();
/// @brief Handler for states parsing "HTTP" string within the first line
/// of the HTTP request.
///
/// @param expected_letter One of the 'H', 'T', 'P'.
/// @param next_state A state to which the parser should transition after
/// parsing the character.
void versionHTTPHandler(const char expected_letter,
const unsigned int next_state);
/// @brief Handler for HTTP_VERSION_MAJOR_START_ST and
/// HTTP_VERSION_MINOR_START_ST.
///
/// This handler calculates version number using the following equation:
/// @code
/// storage = storage * 10 + c - '0';
/// @endcode
///
/// @param next_state State to which the parser should transition.
/// @param [out] storage Reference to a number holding current product of
/// parsing major or minor version number.
void versionNumberStartHandler(const unsigned int next_state,
unsigned int* storage);
/// @brief Handler for HTTP_VERSION_MAJOR_ST and HTTP_VERSION_MINOR_ST.
///
/// This handler calculates version number using the following equation:
/// @code
/// storage = storage * 10 + c - '0';
/// @endcode
///
/// @param following_character Character following the version number, i.e.
/// '.' for major version, \r for minor version.
/// @param next_state State to which the parser should transition.
/// @param [out] storage Pointer to a number holding current product of
/// parsing major or minor version number.
void versionNumberHandler(const char following_character,
const unsigned int next_state,
unsigned int* const storage);
/// @brief Handler for states related to new lines.
///
/// If the next_state is HTTP_PARSE_OK_ST it indicates that the parsed
/// value is a 3rd new line within request HTTP message. In this case the
/// handler calls @ref HttpRequest::create to validate the received message
/// (excluding body). The handler then reads the "Content-Length" header to
/// check if the request contains a body. If the "Content-Length" is greater
/// than zero, the parser transitions to HTTP_BODY_ST. If the
/// "Content-Length" doesn't exist the parser transitions to
/// HTTP_PARSE_OK_ST.
///
/// @param next_state A state to which parser should transition.
void expectingNewLineHandler(const unsigned int next_state);
/// @brief Handler for HEADER_LINE_START_ST.
void headerLineStartHandler();
/// @brief Handler for HEADER_LWS_ST.
void headerLwsHandler();
/// @brief Handler for HEADER_NAME_ST.
void headerNameHandler();
/// @brief Handler for SPACE_BEFORE_HEADER_VALUE_ST.
void spaceBeforeHeaderValueHandler();
/// @brief Handler for HEADER_VALUE_ST.
void headerValueHandler();
/// @brief Handler for HTTP_BODY_ST.
void bodyHandler();
//@}
/// @brief Reference to the request object specified in the constructor.
HttpRequest& request_;
/// @brief Pointer to the internal context of the @ref HttpRequest object.
HttpRequestContextPtr context_;
};
} // namespace http
} // namespace isc
#endif // HTTP_REQUEST_PARSER_H
|