summaryrefslogtreecommitdiffstats
path: root/src/lib/http/response_parser.h
blob: fb83b13a28ca8fa01d2a4fb8c1bb467ac52d7d5e (plain)
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
// Copyright (C) 2017-2023 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_RESPONSE_PARSER_H
#define HTTP_RESPONSE_PARSER_H

#include <http/http_message_parser_base.h>
#include <http/response.h>
#include <boost/shared_ptr.hpp>

namespace isc {
namespace http {

class HttpResponseParser;

/// @brief Pointer to the @ref HttpResponseParser.
typedef boost::shared_ptr<HttpResponseParser> HttpResponseParserPtr;

/// @brief A generic parser for HTTP responses.
///
/// This class implements a parser for HTTP responses. The parser derives
/// from the @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 version, status code,
/// message body etc. The descriptions of all parser states are provided
/// below together with the constants defining these states.
///
/// The response 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. responses to
/// control commands include JSON body. The parser doesn't verify if the
/// message meets such restrictions until the whole message is parsed,
/// i.e. stored in the @ref HttpResponseContext object. This object is
/// associated with @ref HttpResponse object (or its derivation). When
/// the parsing is completed, the @ref HttpResponse::create method is
/// called to retrieve and interpret the data from the context. In
/// particular, the @ref HttpResponse or its derivation checks if the
/// received message meets the desired restrictions.
class HttpResponseParser : public HttpMessageParserBase {
public:

    /// @name States supported by the HttpResponseParser.
    ///
    //@{

    /// @brief State indicating a beginning of parsing.
    static const int RECEIVE_START_ST = SM_DERIVED_STATE_MIN + 101;

    /// @brief Parsing letter "H" of "HTTP".
    static const int HTTP_VERSION_H_ST = SM_DERIVED_STATE_MIN + 102;

    /// @brief Parsing first occurrence of "T" in "HTTP".
    static const int HTTP_VERSION_T1_ST = SM_DERIVED_STATE_MIN + 103;

    /// @brief Parsing second occurrence of "T" in "HTTP".
    static const int HTTP_VERSION_T2_ST = SM_DERIVED_STATE_MIN + 104;

    /// @brief Parsing letter "P" in "HTTP".
    static const int HTTP_VERSION_P_ST = SM_DERIVED_STATE_MIN + 105;

    /// @brief Parsing slash character in "HTTP/Y.X"
    static const int HTTP_VERSION_SLASH_ST = SM_DERIVED_STATE_MIN + 106;

    /// @brief Starting to parse major HTTP version number.
    static const int HTTP_VERSION_MAJOR_START_ST = SM_DERIVED_STATE_MIN + 107;

    /// @brief Parsing major HTTP version number.
    static const int HTTP_VERSION_MAJOR_ST = SM_DERIVED_STATE_MIN + 108;

    /// @brief Starting to parse minor HTTP version number.
    static const int HTTP_VERSION_MINOR_START_ST = SM_DERIVED_STATE_MIN + 109;

    /// @brief Parsing minor HTTP version number.
    static const int HTTP_VERSION_MINOR_ST = SM_DERIVED_STATE_MIN + 110;

    /// @brief Starting to parse HTTP status code.
    static const int HTTP_STATUS_CODE_START_ST = SM_DERIVED_STATE_MIN + 111;

    /// @brief Parsing HTTP status code.
    static const int HTTP_STATUS_CODE_ST = SM_DERIVED_STATE_MIN + 112;

    /// @brief Starting to parse HTTP status phrase.
    static const int HTTP_PHRASE_START_ST = SM_DERIVED_STATE_MIN + 113;

    /// @brief Parsing HTTP status phrase.
    static const int HTTP_PHRASE_ST = SM_DERIVED_STATE_MIN + 114;

    /// @brief Parsing first new line (after HTTP status phrase).
    static const int EXPECTING_NEW_LINE1_ST = SM_DERIVED_STATE_MIN + 115;

    static const int HEADER_LINE_START_ST = SM_DERIVED_STATE_MIN + 116;

    /// @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 + 117;

    /// @brief Parsing header name.
    static const int HEADER_NAME_ST = SM_DERIVED_STATE_MIN + 118;

    /// @brief Parsing space before header value.
    static const int SPACE_BEFORE_HEADER_VALUE_ST = SM_DERIVED_STATE_MIN + 119;

    /// @brief Parsing header value.
    static const int HEADER_VALUE_ST = SM_DERIVED_STATE_MIN + 120;

    /// @brief Expecting new line after parsing header value.
    static const int EXPECTING_NEW_LINE2_ST = SM_DERIVED_STATE_MIN + 121;

    /// @brief Expecting second new line marking end of HTTP headers.
    static const int EXPECTING_NEW_LINE3_ST = SM_DERIVED_STATE_MIN + 122;

    /// @brief Parsing body of a HTTP message.
    static const int HTTP_BODY_ST = SM_DERIVED_STATE_MIN + 123;

    //@}

    /// @brief Constructor.
    ///
    /// Creates new instance of the parser.
    ///
    /// @param response Reference to the @ref HttpResponse object or its
    /// derivation that should be used to validate the parsed response and
    /// to be used as a container for the parsed response.
    explicit HttpResponseParser(HttpResponse& response);

    /// @brief Initialize the state model for parsing.
    ///
    /// This method must be called before parsing the response, i.e. before
    /// calling @ref HttpResponseParser::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 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 states in which parser begins to read numeric values.
    ///
    /// 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 number_name Name of the parsed numeric value, e.g. HTTP version or
    /// HTTP status code (used for error logging).
    /// @param [out] storage Reference to a number holding current product of
    /// parsing major or minor version number.
    void numberStartHandler(const unsigned int next_state,
                            const std::string& number_name,
                            unsigned int* const storage);

    /// @brief Handler for states in which parser reads numeric values.
    ///
    /// 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 number_name Name of the parsed numeric value, e.g. HTTP version or
    /// HTTP status code (used for error logging).
    /// @param [out] storage Pointer to a number holding current product of
    /// parsing major or minor version number.
    void numberHandler(const char following_character,
                       const unsigned int next_state,
                       const std::string& number_name,
                       unsigned int* const storage);

    /// @brief Handler for HTTP_PHRASE_START_ST.
    void phraseStartHandler();

    /// @brief Handler for HTTP_PHRASE_ST.
    void phraseHandler();

    /// @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 response object specified in the constructor.
    HttpResponse& response_;

    /// @brief Pointer to the internal context of the @ref HttpResponse object.
    HttpResponseContextPtr context_;
};

} // end of namespace isc::http
} // end of namespace isc

#endif