]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // request_parser.cpp | |
3 | // ~~~~~~~~~~~~~~~~~~ | |
4 | // | |
5 | // Copyright (c) 2003-2016 Christopher M. Kohlhoff (chris at kohlhoff dot com) | |
6 | // | |
7 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
8 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
9 | // | |
10 | ||
11 | #include "request_parser.hpp" | |
12 | #include "request.hpp" | |
13 | ||
14 | namespace http { | |
15 | namespace server { | |
16 | ||
17 | request_parser::request_parser() | |
18 | : state_(method_start) | |
19 | { | |
20 | } | |
21 | ||
22 | void request_parser::reset() | |
23 | { | |
24 | state_ = method_start; | |
25 | } | |
26 | ||
27 | request_parser::result_type request_parser::consume(request& req, char input) | |
28 | { | |
29 | switch (state_) | |
30 | { | |
31 | case method_start: | |
32 | if (!is_char(input) || is_ctl(input) || is_tspecial(input)) | |
33 | { | |
34 | return bad; | |
35 | } | |
36 | else | |
37 | { | |
38 | state_ = method; | |
39 | req.method.push_back(input); | |
40 | return indeterminate; | |
41 | } | |
42 | case method: | |
43 | if (input == ' ') | |
44 | { | |
45 | state_ = uri; | |
46 | return indeterminate; | |
47 | } | |
48 | else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) | |
49 | { | |
50 | return bad; | |
51 | } | |
52 | else | |
53 | { | |
54 | req.method.push_back(input); | |
55 | return indeterminate; | |
56 | } | |
57 | case uri: | |
58 | if (input == ' ') | |
59 | { | |
60 | state_ = http_version_h; | |
61 | return indeterminate; | |
62 | } | |
63 | else if (is_ctl(input)) | |
64 | { | |
65 | return bad; | |
66 | } | |
67 | else | |
68 | { | |
69 | req.uri.push_back(input); | |
70 | return indeterminate; | |
71 | } | |
72 | case http_version_h: | |
73 | if (input == 'H') | |
74 | { | |
75 | state_ = http_version_t_1; | |
76 | return indeterminate; | |
77 | } | |
78 | else | |
79 | { | |
80 | return bad; | |
81 | } | |
82 | case http_version_t_1: | |
83 | if (input == 'T') | |
84 | { | |
85 | state_ = http_version_t_2; | |
86 | return indeterminate; | |
87 | } | |
88 | else | |
89 | { | |
90 | return bad; | |
91 | } | |
92 | case http_version_t_2: | |
93 | if (input == 'T') | |
94 | { | |
95 | state_ = http_version_p; | |
96 | return indeterminate; | |
97 | } | |
98 | else | |
99 | { | |
100 | return bad; | |
101 | } | |
102 | case http_version_p: | |
103 | if (input == 'P') | |
104 | { | |
105 | state_ = http_version_slash; | |
106 | return indeterminate; | |
107 | } | |
108 | else | |
109 | { | |
110 | return bad; | |
111 | } | |
112 | case http_version_slash: | |
113 | if (input == '/') | |
114 | { | |
115 | req.http_version_major = 0; | |
116 | req.http_version_minor = 0; | |
117 | state_ = http_version_major_start; | |
118 | return indeterminate; | |
119 | } | |
120 | else | |
121 | { | |
122 | return bad; | |
123 | } | |
124 | case http_version_major_start: | |
125 | if (is_digit(input)) | |
126 | { | |
127 | req.http_version_major = req.http_version_major * 10 + input - '0'; | |
128 | state_ = http_version_major; | |
129 | return indeterminate; | |
130 | } | |
131 | else | |
132 | { | |
133 | return bad; | |
134 | } | |
135 | case http_version_major: | |
136 | if (input == '.') | |
137 | { | |
138 | state_ = http_version_minor_start; | |
139 | return indeterminate; | |
140 | } | |
141 | else if (is_digit(input)) | |
142 | { | |
143 | req.http_version_major = req.http_version_major * 10 + input - '0'; | |
144 | return indeterminate; | |
145 | } | |
146 | else | |
147 | { | |
148 | return bad; | |
149 | } | |
150 | case http_version_minor_start: | |
151 | if (is_digit(input)) | |
152 | { | |
153 | req.http_version_minor = req.http_version_minor * 10 + input - '0'; | |
154 | state_ = http_version_minor; | |
155 | return indeterminate; | |
156 | } | |
157 | else | |
158 | { | |
159 | return bad; | |
160 | } | |
161 | case http_version_minor: | |
162 | if (input == '\r') | |
163 | { | |
164 | state_ = expecting_newline_1; | |
165 | return indeterminate; | |
166 | } | |
167 | else if (is_digit(input)) | |
168 | { | |
169 | req.http_version_minor = req.http_version_minor * 10 + input - '0'; | |
170 | return indeterminate; | |
171 | } | |
172 | else | |
173 | { | |
174 | return bad; | |
175 | } | |
176 | case expecting_newline_1: | |
177 | if (input == '\n') | |
178 | { | |
179 | state_ = header_line_start; | |
180 | return indeterminate; | |
181 | } | |
182 | else | |
183 | { | |
184 | return bad; | |
185 | } | |
186 | case header_line_start: | |
187 | if (input == '\r') | |
188 | { | |
189 | state_ = expecting_newline_3; | |
190 | return indeterminate; | |
191 | } | |
192 | else if (!req.headers.empty() && (input == ' ' || input == '\t')) | |
193 | { | |
194 | state_ = header_lws; | |
195 | return indeterminate; | |
196 | } | |
197 | else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) | |
198 | { | |
199 | return bad; | |
200 | } | |
201 | else | |
202 | { | |
203 | req.headers.push_back(header()); | |
204 | req.headers.back().name.push_back(input); | |
205 | state_ = header_name; | |
206 | return indeterminate; | |
207 | } | |
208 | case header_lws: | |
209 | if (input == '\r') | |
210 | { | |
211 | state_ = expecting_newline_2; | |
212 | return indeterminate; | |
213 | } | |
214 | else if (input == ' ' || input == '\t') | |
215 | { | |
216 | return indeterminate; | |
217 | } | |
218 | else if (is_ctl(input)) | |
219 | { | |
220 | return bad; | |
221 | } | |
222 | else | |
223 | { | |
224 | state_ = header_value; | |
225 | req.headers.back().value.push_back(input); | |
226 | return indeterminate; | |
227 | } | |
228 | case header_name: | |
229 | if (input == ':') | |
230 | { | |
231 | state_ = space_before_header_value; | |
232 | return indeterminate; | |
233 | } | |
234 | else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) | |
235 | { | |
236 | return bad; | |
237 | } | |
238 | else | |
239 | { | |
240 | req.headers.back().name.push_back(input); | |
241 | return indeterminate; | |
242 | } | |
243 | case space_before_header_value: | |
244 | if (input == ' ') | |
245 | { | |
246 | state_ = header_value; | |
247 | return indeterminate; | |
248 | } | |
249 | else | |
250 | { | |
251 | return bad; | |
252 | } | |
253 | case header_value: | |
254 | if (input == '\r') | |
255 | { | |
256 | state_ = expecting_newline_2; | |
257 | return indeterminate; | |
258 | } | |
259 | else if (is_ctl(input)) | |
260 | { | |
261 | return bad; | |
262 | } | |
263 | else | |
264 | { | |
265 | req.headers.back().value.push_back(input); | |
266 | return indeterminate; | |
267 | } | |
268 | case expecting_newline_2: | |
269 | if (input == '\n') | |
270 | { | |
271 | state_ = header_line_start; | |
272 | return indeterminate; | |
273 | } | |
274 | else | |
275 | { | |
276 | return bad; | |
277 | } | |
278 | case expecting_newline_3: | |
279 | return (input == '\n') ? good : bad; | |
280 | default: | |
281 | return bad; | |
282 | } | |
283 | } | |
284 | ||
285 | bool request_parser::is_char(int c) | |
286 | { | |
287 | return c >= 0 && c <= 127; | |
288 | } | |
289 | ||
290 | bool request_parser::is_ctl(int c) | |
291 | { | |
292 | return (c >= 0 && c <= 31) || (c == 127); | |
293 | } | |
294 | ||
295 | bool request_parser::is_tspecial(int c) | |
296 | { | |
297 | switch (c) | |
298 | { | |
299 | case '(': case ')': case '<': case '>': case '@': | |
300 | case ',': case ';': case ':': case '\\': case '"': | |
301 | case '/': case '[': case ']': case '?': case '=': | |
302 | case '{': case '}': case ' ': case '\t': | |
303 | return true; | |
304 | default: | |
305 | return false; | |
306 | } | |
307 | } | |
308 | ||
309 | bool request_parser::is_digit(int c) | |
310 | { | |
311 | return c >= '0' && c <= '9'; | |
312 | } | |
313 | ||
314 | } // namespace server | |
315 | } // namespace http |