blob: 0ba3748a7bdca4ba495438881fe2b30d4c488ce4 [file] [log] [blame]
Alexander Afanasyevfa2f6622016-12-25 12:28:00 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2016, Regents of the University of California.
4 *
5 * This file is part of ChronoShare, a decentralized file sharing application over NDN.
6 *
7 * ChronoShare is free software: you can redistribute it and/or modify it under the terms
8 * of the GNU General Public License as published by the Free Software Foundation, either
9 * version 3 of the License, or (at your option) any later version.
10 *
11 * ChronoShare is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License along with
16 * ChronoShare, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * See AUTHORS.md for complete list of ChronoShare authors and contributors.
19 */
Zhenkai Zhua9a7e1d2013-02-25 18:29:07 -080020//
21// request_parser.cpp
22// ~~~~~~~~~~~~~~~~~~
23//
24// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
25//
26// Distributed under the Boost Software License, Version 1.0. (See accompanying
27// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
28//
29
30#include "request_parser.hpp"
31#include "request.hpp"
32
33namespace http {
34namespace server {
35
36request_parser::request_parser()
37 : state_(method_start)
38{
39}
40
41void request_parser::reset()
42{
43 state_ = method_start;
44}
45
46boost::tribool request_parser::consume(request& req, char input)
47{
48 switch (state_)
49 {
50 case method_start:
51 if (!is_char(input) || is_ctl(input) || is_tspecial(input))
52 {
53 return false;
54 }
55 else
56 {
57 state_ = method;
58 req.method.push_back(input);
59 return boost::indeterminate;
60 }
61 case method:
62 if (input == ' ')
63 {
64 state_ = uri;
65 return boost::indeterminate;
66 }
67 else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
68 {
69 return false;
70 }
71 else
72 {
73 req.method.push_back(input);
74 return boost::indeterminate;
75 }
76 case uri:
77 if (input == ' ')
78 {
79 state_ = http_version_h;
80 return boost::indeterminate;
81 }
82 else if (is_ctl(input))
83 {
84 return false;
85 }
86 else
87 {
88 req.uri.push_back(input);
89 return boost::indeterminate;
90 }
91 case http_version_h:
92 if (input == 'H')
93 {
94 state_ = http_version_t_1;
95 return boost::indeterminate;
96 }
97 else
98 {
99 return false;
100 }
101 case http_version_t_1:
102 if (input == 'T')
103 {
104 state_ = http_version_t_2;
105 return boost::indeterminate;
106 }
107 else
108 {
109 return false;
110 }
111 case http_version_t_2:
112 if (input == 'T')
113 {
114 state_ = http_version_p;
115 return boost::indeterminate;
116 }
117 else
118 {
119 return false;
120 }
121 case http_version_p:
122 if (input == 'P')
123 {
124 state_ = http_version_slash;
125 return boost::indeterminate;
126 }
127 else
128 {
129 return false;
130 }
131 case http_version_slash:
132 if (input == '/')
133 {
134 req.http_version_major = 0;
135 req.http_version_minor = 0;
136 state_ = http_version_major_start;
137 return boost::indeterminate;
138 }
139 else
140 {
141 return false;
142 }
143 case http_version_major_start:
144 if (is_digit(input))
145 {
146 req.http_version_major = req.http_version_major * 10 + input - '0';
147 state_ = http_version_major;
148 return boost::indeterminate;
149 }
150 else
151 {
152 return false;
153 }
154 case http_version_major:
155 if (input == '.')
156 {
157 state_ = http_version_minor_start;
158 return boost::indeterminate;
159 }
160 else if (is_digit(input))
161 {
162 req.http_version_major = req.http_version_major * 10 + input - '0';
163 return boost::indeterminate;
164 }
165 else
166 {
167 return false;
168 }
169 case http_version_minor_start:
170 if (is_digit(input))
171 {
172 req.http_version_minor = req.http_version_minor * 10 + input - '0';
173 state_ = http_version_minor;
174 return boost::indeterminate;
175 }
176 else
177 {
178 return false;
179 }
180 case http_version_minor:
181 if (input == '\r')
182 {
183 state_ = expecting_newline_1;
184 return boost::indeterminate;
185 }
186 else if (is_digit(input))
187 {
188 req.http_version_minor = req.http_version_minor * 10 + input - '0';
189 return boost::indeterminate;
190 }
191 else
192 {
193 return false;
194 }
195 case expecting_newline_1:
196 if (input == '\n')
197 {
198 state_ = header_line_start;
199 return boost::indeterminate;
200 }
201 else
202 {
203 return false;
204 }
205 case header_line_start:
206 if (input == '\r')
207 {
208 state_ = expecting_newline_3;
209 return boost::indeterminate;
210 }
211 else if (!req.headers.empty() && (input == ' ' || input == '\t'))
212 {
213 state_ = header_lws;
214 return boost::indeterminate;
215 }
216 else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
217 {
218 return false;
219 }
220 else
221 {
222 req.headers.push_back(header());
223 req.headers.back().name.push_back(input);
224 state_ = header_name;
225 return boost::indeterminate;
226 }
227 case header_lws:
228 if (input == '\r')
229 {
230 state_ = expecting_newline_2;
231 return boost::indeterminate;
232 }
233 else if (input == ' ' || input == '\t')
234 {
235 return boost::indeterminate;
236 }
237 else if (is_ctl(input))
238 {
239 return false;
240 }
241 else
242 {
243 state_ = header_value;
244 req.headers.back().value.push_back(input);
245 return boost::indeterminate;
246 }
247 case header_name:
248 if (input == ':')
249 {
250 state_ = space_before_header_value;
251 return boost::indeterminate;
252 }
253 else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
254 {
255 return false;
256 }
257 else
258 {
259 req.headers.back().name.push_back(input);
260 return boost::indeterminate;
261 }
262 case space_before_header_value:
263 if (input == ' ')
264 {
265 state_ = header_value;
266 return boost::indeterminate;
267 }
268 else
269 {
270 return false;
271 }
272 case header_value:
273 if (input == '\r')
274 {
275 state_ = expecting_newline_2;
276 return boost::indeterminate;
277 }
278 else if (is_ctl(input))
279 {
280 return false;
281 }
282 else
283 {
284 req.headers.back().value.push_back(input);
285 return boost::indeterminate;
286 }
287 case expecting_newline_2:
288 if (input == '\n')
289 {
290 state_ = header_line_start;
291 return boost::indeterminate;
292 }
293 else
294 {
295 return false;
296 }
297 case expecting_newline_3:
298 return (input == '\n');
299 default:
300 return false;
301 }
302}
303
304bool request_parser::is_char(int c)
305{
306 return c >= 0 && c <= 127;
307}
308
309bool request_parser::is_ctl(int c)
310{
311 return (c >= 0 && c <= 31) || (c == 127);
312}
313
314bool request_parser::is_tspecial(int c)
315{
316 switch (c)
317 {
318 case '(': case ')': case '<': case '>': case '@':
319 case ',': case ';': case ':': case '\\': case '"':
320 case '/': case '[': case ']': case '?': case '=':
321 case '{': case '}': case ' ': case '\t':
322 return true;
323 default:
324 return false;
325 }
326}
327
328bool request_parser::is_digit(int c)
329{
330 return c >= '0' && c <= '9';
331}
332
333} // namespace server
334} // namespace http