OpenVPN 3 Core Library
Loading...
Searching...
No Matches
reply.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 reply
19
20#ifndef OPENVPN_HTTP_REPLY_H
21#define OPENVPN_HTTP_REPLY_H
22
25
26namespace openvpn::HTTP {
27
28struct Reply
29{
34
35 void reset()
36 {
39 status_code = 0;
40 status_text = "";
41 headers.clear();
42 }
43
44 std::string to_string() const
45 {
46 std::ostringstream out;
47 out << "HTTP Reply" << std::endl;
48 out << "version=" << http_version_major << '/' << http_version_minor << std::endl;
49 out << "status_code=" << status_code << std::endl;
50 out << "status_text=" << status_text << std::endl;
52 return out.str();
53 }
54
58 std::string status_text;
60};
61
63{
88
89 public:
90 enum status
91 {
95 };
96
99 {
100 }
101
102 // Reset to initial parser state.
103 void reset()
104 {
106 }
107
108 // Parse some HTTP reply data.
109 status consume(Reply &req, const unsigned char input)
110 {
111 switch (state_)
112 {
113 case http_version_h:
114 if (input == 'H')
115 {
117 return pending;
118 }
119 else
120 {
121 return fail;
122 }
123 case http_version_t_1:
124 if (input == 'T')
125 {
127 return pending;
128 }
129 else
130 {
131 return fail;
132 }
133 case http_version_t_2:
134 if (input == 'T')
135 {
137 return pending;
138 }
139 else
140 {
141 return fail;
142 }
143 case http_version_p:
144 if (input == 'P')
145 {
147 return pending;
148 }
149 else
150 {
151 return fail;
152 }
154 if (input == '/')
155 {
156 req.http_version_major = 0;
157 req.http_version_minor = 0;
159 return pending;
160 }
161 else
162 {
163 return fail;
164 }
167 {
168 req.http_version_major = req.http_version_major * 10 + input - '0';
170 return pending;
171 }
172 else
173 {
174 return fail;
175 }
177 if (input == '.')
178 {
180 return pending;
181 }
182 else if (Util::is_digit(input))
183 {
184 req.http_version_major = req.http_version_major * 10 + input - '0';
185 return pending;
186 }
187 else
188 {
189 return fail;
190 }
193 {
194 req.http_version_minor = req.http_version_minor * 10 + input - '0';
196 return pending;
197 }
198 else
199 {
200 return fail;
201 }
203 if (input == ' ')
204 {
206 return pending;
207 }
208 else if (Util::is_digit(input))
209 {
210 req.http_version_minor = req.http_version_minor * 10 + input - '0';
211 return pending;
212 }
213 else
214 {
215 return fail;
216 }
219 {
220 req.status_code = req.status_code * 10 + input - '0';
222 return pending;
223 }
224 else
225 {
226 return fail;
227 }
228 case status_code:
229 if (input == ' ')
230 {
232 return pending;
233 }
234 else if (Util::is_digit(input))
235 {
236 req.status_code = req.status_code * 10 + input - '0';
237 return pending;
238 }
239 else
240 {
241 return fail;
242 }
244 if (input == '\r')
245 {
247 return pending;
248 }
250 {
251 return fail;
252 }
253 else
254 {
256 req.status_text.push_back(input);
257 return pending;
258 }
259 case status_text:
260 if (input == '\r')
261 {
263 return pending;
264 }
265 else if (!Util::is_char(input) || Util::is_ctl(input))
266 {
267 return fail;
268 }
269 else
270 {
271 req.status_text.push_back(input);
272 return pending;
273 }
275 if (input == '\n')
276 {
278 return pending;
279 }
280 else
281 {
282 return fail;
283 }
285 if (input == '\r')
286 {
288 return pending;
289 }
290 else if (!req.headers.empty() && (input == ' ' || input == '\t'))
291 {
293 return pending;
294 }
296 {
297 return fail;
298 }
299 else
300 {
301 req.headers.push_back(Header());
302 req.headers.back().name.push_back(input);
304 return pending;
305 }
306 case header_lws:
307 if (input == '\r')
308 {
310 return pending;
311 }
312 else if (input == ' ' || input == '\t')
313 {
314 return pending;
315 }
316 else if (Util::is_ctl(input))
317 {
318 return fail;
319 }
320 else
321 {
323 req.headers.back().value.push_back(input);
324 return pending;
325 }
326 case header_name:
327 if (input == ':')
328 {
330 return pending;
331 }
333 {
334 return fail;
335 }
336 else
337 {
338 req.headers.back().name.push_back(input);
339 return pending;
340 }
342 if (input == ' ')
343 {
345 return pending;
346 }
347 else
348 {
349 return fail;
350 }
351 case header_value:
352 if (input == '\r')
353 {
355 return pending;
356 }
357 else if (Util::is_ctl(input))
358 {
359 return fail;
360 }
361 else
362 {
363 req.headers.back().value.push_back(input);
364 return pending;
365 }
367 if (input == '\n')
368 {
370 return pending;
371 }
372 else
373 {
374 return fail;
375 }
377 if (input == '\n')
378 return success;
379 else
380 return fail;
381 default:
382 return fail;
383 }
384 }
385
386 private:
387 // The current state of the parser.
389};
390
392{
393 typedef Reply State;
395};
396} // namespace openvpn::HTTP
397
398#endif
status consume(Reply &req, const unsigned char input)
Definition reply.hpp:109
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 reply.hpp:44
std::string status_text
Definition reply.hpp:58
HeaderList headers
Definition reply.hpp:59
static const char * input[]
static std::stringstream out
Definition test_path.cpp:10