OpenVPN 3 Core Library
Loading...
Searching...
No Matches
request.hpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11//
12// Source: pkg:github/chriskohlhoff/asio@asio-1-8-0#asio/src/examples/http
13// Adapted from code Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
14//
15// Distributed under the Boost Software License, Version 1.0. (See accompanying
16// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
17
18// Parse an HTTP request
19
20#ifndef OPENVPN_HTTP_REQUEST_H
21#define OPENVPN_HTTP_REQUEST_H
22
25
26namespace openvpn::HTTP {
27
28struct Request
29{
34
35 void reset()
36 {
37 method = "";
38 uri = "";
41 headers.clear();
42 }
43
44 std::string to_string() const
45 {
46 std::ostringstream out;
47 out << "HTTP Request" << std::endl;
48 out << "method=" << method << std::endl;
49 out << "uri=" << uri << std::endl;
50 out << "version=" << http_version_major << '/' << http_version_minor << std::endl;
52 return out.str();
53 }
54
55 std::string to_string_compact() const
56 {
57 std::ostringstream out;
58 out << method << ' ' << uri << " HTTP/" << http_version_major << '.' << http_version_minor;
59 return out.str();
60 }
61
62 bool at_least_http_1_1() const
63 {
64 return http_version_major > 1 || (http_version_major == 1 && http_version_minor >= 1);
65 }
66
67 std::string method;
68 std::string uri;
72};
73
75{
99
100 public:
107
110 {
111 }
112
113 // Reset to initial parser state.
114 void reset()
115 {
117 }
118
119 // Parse some HTTP request data.
120 status consume(Request &req, const unsigned char input)
121 {
122 switch (state_)
123 {
124 case method_start:
126 {
127 return fail;
128 }
129 else
130 {
131 state_ = method;
132 req.method.push_back(input);
133 return pending;
134 }
135 case method:
136 if (input == ' ')
137 {
138 state_ = uri;
139 return pending;
140 }
142 {
143 return fail;
144 }
145 else
146 {
147 req.method.push_back(input);
148 return pending;
149 }
150 case uri:
151 if (input == ' ')
152 {
154 return pending;
155 }
156 else if (Util::is_ctl(input))
157 {
158 return fail;
159 }
160 else
161 {
162 req.uri.push_back(input);
163 return pending;
164 }
165 case http_version_h:
166 if (input == 'H')
167 {
169 return pending;
170 }
171 else
172 {
173 return fail;
174 }
175 case http_version_t_1:
176 if (input == 'T')
177 {
179 return pending;
180 }
181 else
182 {
183 return fail;
184 }
185 case http_version_t_2:
186 if (input == 'T')
187 {
189 return pending;
190 }
191 else
192 {
193 return fail;
194 }
195 case http_version_p:
196 if (input == 'P')
197 {
199 return pending;
200 }
201 else
202 {
203 return fail;
204 }
206 if (input == '/')
207 {
208 req.http_version_major = 0;
209 req.http_version_minor = 0;
211 return pending;
212 }
213 else
214 {
215 return fail;
216 }
219 {
220 req.http_version_major = req.http_version_major * 10 + input - '0';
222 return pending;
223 }
224 else
225 {
226 return fail;
227 }
229 if (input == '.')
230 {
232 return pending;
233 }
234 else if (Util::is_digit(input))
235 {
236 req.http_version_major = req.http_version_major * 10 + input - '0';
237 return pending;
238 }
239 else
240 {
241 return fail;
242 }
245 {
246 req.http_version_minor = req.http_version_minor * 10 + input - '0';
248 return pending;
249 }
250 else
251 {
252 return fail;
253 }
255 if (input == '\r')
256 {
258 return pending;
259 }
260 else if (Util::is_digit(input))
261 {
262 req.http_version_minor = req.http_version_minor * 10 + input - '0';
263 return pending;
264 }
265 else
266 {
267 return fail;
268 }
270 if (input == '\n')
271 {
273 return pending;
274 }
275 else
276 {
277 return fail;
278 }
280 if (input == '\r')
281 {
283 return pending;
284 }
285 else if (!req.headers.empty() && (input == ' ' || input == '\t'))
286 {
288 return pending;
289 }
291 {
292 return fail;
293 }
294 else
295 {
296 req.headers.push_back(Header());
297 req.headers.back().name.push_back(input);
299 return pending;
300 }
301 case header_lws:
302 if (input == '\r')
303 {
305 return pending;
306 }
307 else if (input == ' ' || input == '\t')
308 {
309 return pending;
310 }
311 else if (Util::is_ctl(input))
312 {
313 return fail;
314 }
315 else
316 {
318 req.headers.back().value.push_back(input);
319 return pending;
320 }
321 case header_name:
322 if (input == ':')
323 {
325 return pending;
326 }
328 {
329 return fail;
330 }
331 else
332 {
333 req.headers.back().name.push_back(input);
334 return pending;
335 }
337 if (input == ' ')
338 {
340 return pending;
341 }
342 else
343 {
344 return fail;
345 }
346 case header_value:
347 if (input == '\r')
348 {
350 return pending;
351 }
352 else if (Util::is_ctl(input))
353 {
354 return fail;
355 }
356 else
357 {
358 req.headers.back().value.push_back(input);
359 return pending;
360 }
362 if (input == '\n')
363 {
365 return pending;
366 }
367 else
368 {
369 return fail;
370 }
372 if (input == '\n')
373 return success;
374 else
375 return fail;
376 default:
377 return fail;
378 }
379 }
380
381 private:
382 // The current state of the parser.
384};
385
387{
388 typedef Request State;
390};
391} // namespace openvpn::HTTP
392
393#endif
status consume(Request &req, const unsigned char input)
Definition request.hpp:120
bool is_char(const unsigned char c)
Definition parseutil.hpp:26
bool is_digit(const unsigned char c)
Definition parseutil.hpp:68
bool is_tspecial(const unsigned char c)
Definition parseutil.hpp:38
bool is_ctl(const unsigned char c)
Definition parseutil.hpp:32
std::string to_string() const
Definition header.hpp:94
std::string to_string() const
Definition request.hpp:44
bool at_least_http_1_1() const
Definition request.hpp:62
std::string to_string_compact() const
Definition request.hpp:55
static const char * input[]
static std::stringstream out
Definition test_path.cpp:10