]> git.proxmox.com Git - ceph.git/blob - ceph/src/boost/libs/asio/example/cpp03/http/server4/request_parser.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / libs / asio / example / cpp03 / http / server4 / request_parser.cpp
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 <algorithm>
13 #include <cctype>
14 #include <boost/lexical_cast.hpp>
15 #include "request.hpp"
16
17 namespace http {
18 namespace server4 {
19
20 // Enable the pseudo-keywords reenter, yield and fork.
21 #include <boost/asio/yield.hpp>
22
23 std::string request_parser::content_length_name_ = "Content-Length";
24
25 boost::tribool request_parser::consume(request& req, char c)
26 {
27 reenter (this)
28 {
29 req.method.clear();
30 req.uri.clear();
31 req.http_version_major = 0;
32 req.http_version_minor = 0;
33 req.headers.clear();
34 req.content.clear();
35 content_length_ = 0;
36
37 // Request method.
38 while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ' ')
39 {
40 req.method.push_back(c);
41 yield return boost::indeterminate;
42 }
43 if (req.method.empty())
44 return false;
45
46 // Space.
47 if (c != ' ') return false;
48 yield return boost::indeterminate;
49
50 // URI.
51 while (!is_ctl(c) && c != ' ')
52 {
53 req.uri.push_back(c);
54 yield return boost::indeterminate;
55 }
56 if (req.uri.empty()) return false;
57
58 // Space.
59 if (c != ' ') return false;
60 yield return boost::indeterminate;
61
62 // HTTP protocol identifier.
63 if (c != 'H') return false;
64 yield return boost::indeterminate;
65 if (c != 'T') return false;
66 yield return boost::indeterminate;
67 if (c != 'T') return false;
68 yield return boost::indeterminate;
69 if (c != 'P') return false;
70 yield return boost::indeterminate;
71
72 // Slash.
73 if (c != '/') return false;
74 yield return boost::indeterminate;
75
76 // Major version number.
77 if (!is_digit(c)) return false;
78 while (is_digit(c))
79 {
80 req.http_version_major = req.http_version_major * 10 + c - '0';
81 yield return boost::indeterminate;
82 }
83
84 // Dot.
85 if (c != '.') return false;
86 yield return boost::indeterminate;
87
88 // Minor version number.
89 if (!is_digit(c)) return false;
90 while (is_digit(c))
91 {
92 req.http_version_minor = req.http_version_minor * 10 + c - '0';
93 yield return boost::indeterminate;
94 }
95
96 // CRLF.
97 if (c != '\r') return false;
98 yield return boost::indeterminate;
99 if (c != '\n') return false;
100 yield return boost::indeterminate;
101
102 // Headers.
103 while ((is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != '\r')
104 || (c == ' ' || c == '\t'))
105 {
106 if (c == ' ' || c == '\t')
107 {
108 // Leading whitespace. Must be continuation of previous header's value.
109 if (req.headers.empty()) return false;
110 while (c == ' ' || c == '\t')
111 yield return boost::indeterminate;
112 }
113 else
114 {
115 // Start the next header.
116 req.headers.push_back(header());
117
118 // Header name.
119 while (is_char(c) && !is_ctl(c) && !is_tspecial(c) && c != ':')
120 {
121 req.headers.back().name.push_back(c);
122 yield return boost::indeterminate;
123 }
124
125 // Colon and space separates the header name from the header value.
126 if (c != ':') return false;
127 yield return boost::indeterminate;
128 if (c != ' ') return false;
129 yield return boost::indeterminate;
130 }
131
132 // Header value.
133 while (is_char(c) && !is_ctl(c) && c != '\r')
134 {
135 req.headers.back().value.push_back(c);
136 yield return boost::indeterminate;
137 }
138
139 // CRLF.
140 if (c != '\r') return false;
141 yield return boost::indeterminate;
142 if (c != '\n') return false;
143 yield return boost::indeterminate;
144 }
145
146 // CRLF.
147 if (c != '\r') return false;
148 yield return boost::indeterminate;
149 if (c != '\n') return false;
150
151 // Check for optional Content-Length header.
152 for (std::size_t i = 0; i < req.headers.size(); ++i)
153 {
154 if (headers_equal(req.headers[i].name, content_length_name_))
155 {
156 try
157 {
158 content_length_ =
159 boost::lexical_cast<std::size_t>(req.headers[i].value);
160 }
161 catch (boost::bad_lexical_cast&)
162 {
163 return false;
164 }
165 }
166 }
167
168 // Content.
169 while (req.content.size() < content_length_)
170 {
171 yield return boost::indeterminate;
172 req.content.push_back(c);
173 }
174 }
175
176 return true;
177 }
178
179 // Disable the pseudo-keywords reenter, yield and fork.
180 #include <boost/asio/unyield.hpp>
181
182 bool request_parser::is_char(int c)
183 {
184 return c >= 0 && c <= 127;
185 }
186
187 bool request_parser::is_ctl(int c)
188 {
189 return (c >= 0 && c <= 31) || (c == 127);
190 }
191
192 bool request_parser::is_tspecial(int c)
193 {
194 switch (c)
195 {
196 case '(': case ')': case '<': case '>': case '@':
197 case ',': case ';': case ':': case '\\': case '"':
198 case '/': case '[': case ']': case '?': case '=':
199 case '{': case '}': case ' ': case '\t':
200 return true;
201 default:
202 return false;
203 }
204 }
205
206 bool request_parser::is_digit(int c)
207 {
208 return c >= '0' && c <= '9';
209 }
210
211 bool request_parser::tolower_compare(char a, char b)
212 {
213 return std::tolower(a) == std::tolower(b);
214 }
215
216 bool request_parser::headers_equal(const std::string& a, const std::string& b)
217 {
218 if (a.length() != b.length())
219 return false;
220
221 return std::equal(a.begin(), a.end(), b.begin(),
222 &request_parser::tolower_compare);
223 }
224
225 } // namespace server4
226 } // namespace http