Line data Source code
1 : //
2 : // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com)
3 : //
4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 : //
7 : // Official repository: https://github.com/cppalliance/http_proto
8 : //
9 :
10 : #include <boost/http_proto/rfc/detail/rules.hpp>
11 :
12 : #include <boost/http_proto/error.hpp>
13 : #include <boost/http_proto/detail/config.hpp>
14 : #include <boost/http_proto/rfc/token_rule.hpp>
15 :
16 : #include <boost/core/detail/string_view.hpp>
17 : #include <boost/url/grammar/delim_rule.hpp>
18 : #include <boost/url/grammar/digit_chars.hpp>
19 : #include <boost/url/grammar/error.hpp>
20 : #include <boost/url/grammar/hexdig_chars.hpp>
21 : #include <boost/url/grammar/lut_chars.hpp>
22 : #include <boost/url/grammar/parse.hpp>
23 : #include <boost/url/grammar/tuple_rule.hpp>
24 :
25 : #include "rules.hpp"
26 :
27 : namespace boost {
28 : namespace http_proto {
29 : namespace detail {
30 :
31 : auto
32 6287 : crlf_rule_t::
33 : parse(
34 : char const*& it,
35 : char const* end) const noexcept ->
36 : system::result<value_type>
37 : {
38 6287 : if(it == end)
39 1006 : return grammar::error::need_more;
40 5281 : if(*it != '\r')
41 31 : return grammar::error::mismatch;
42 5250 : ++it;
43 5250 : if(it == end)
44 163 : return grammar::error::need_more;
45 5087 : if(*it != '\n')
46 52 : return grammar::error::mismatch;
47 5035 : ++it;
48 5035 : return {};
49 : }
50 :
51 : //------------------------------------------------
52 :
53 : auto
54 3594 : version_rule_t::
55 : parse(
56 : char const*& it,
57 : char const* end) const noexcept ->
58 : system::result<value_type>
59 : {
60 3594 : value_type v = 0;
61 3594 : if(it == end)
62 : {
63 : // expected "HTTP/"
64 171 : BOOST_HTTP_PROTO_RETURN_EC(
65 : grammar::error::need_more);
66 : }
67 3423 : if(end - it >= 5)
68 : {
69 2883 : if(std::memcmp(
70 : it, "HTTP/", 5) != 0)
71 : {
72 0 : BOOST_HTTP_PROTO_RETURN_EC(
73 : grammar::error::mismatch);
74 : }
75 2883 : it += 5;
76 : }
77 3423 : if(it == end)
78 : {
79 : // expected DIGIT
80 90 : BOOST_HTTP_PROTO_RETURN_EC(
81 : grammar::error::need_more);
82 : }
83 3333 : if(! grammar::digit_chars(*it))
84 : {
85 : // expected DIGIT
86 540 : BOOST_HTTP_PROTO_RETURN_EC(
87 : grammar::error::need_more);
88 : }
89 2793 : v = 10 * (*it++ - '0');
90 2793 : if(it == end)
91 : {
92 : // expected "."
93 234 : BOOST_HTTP_PROTO_RETURN_EC(
94 : grammar::error::need_more);
95 : }
96 2559 : if(*it != '.')
97 : {
98 : // expected "."
99 0 : BOOST_HTTP_PROTO_RETURN_EC(
100 : grammar::error::need_more);
101 : }
102 2559 : ++it;
103 2559 : if(it == end)
104 : {
105 : // expected DIGIT
106 89 : BOOST_HTTP_PROTO_RETURN_EC(
107 : grammar::error::need_more);
108 : }
109 2470 : if(! grammar::digit_chars(*it))
110 : {
111 : // expected DIGIT
112 0 : BOOST_HTTP_PROTO_RETURN_EC(
113 : grammar::error::need_more);
114 : }
115 2470 : v += *it++ - '0';
116 2470 : return v;
117 : }
118 :
119 : //------------------------------------------------
120 :
121 : auto
122 608 : status_code_rule_t::
123 : parse(
124 : char const*& it,
125 : char const* end) const noexcept ->
126 : system::result<value_type>
127 : {
128 : auto const dig =
129 1773 : [](char c) -> int
130 : {
131 1773 : unsigned char uc(c - '0');
132 1773 : if(uc > 9)
133 0 : return -1;
134 1773 : return uc;
135 : };
136 :
137 608 : if(it == end)
138 : {
139 : // end
140 9 : BOOST_HTTP_PROTO_RETURN_EC(
141 : grammar::error::need_more);
142 : }
143 599 : auto it0 = it;
144 599 : int v = dig(*it);
145 599 : if(v == -1)
146 : {
147 : // expected DIGIT
148 0 : BOOST_HTTP_PROTO_RETURN_EC(
149 : grammar::error::mismatch);
150 : }
151 599 : value_type t;
152 599 : t.v = 100 * v;
153 599 : ++it;
154 599 : if(it == end)
155 : {
156 : // end
157 8 : BOOST_HTTP_PROTO_RETURN_EC(
158 : grammar::error::need_more);
159 : }
160 591 : v = dig(*it);
161 591 : if(v == -1)
162 : {
163 : // expected DIGIT
164 0 : BOOST_HTTP_PROTO_RETURN_EC(
165 : grammar::error::mismatch);
166 : }
167 591 : t.v = t.v + (10 * v);
168 591 : ++it;
169 591 : if(it == end)
170 : {
171 : // end
172 8 : BOOST_HTTP_PROTO_RETURN_EC(
173 : grammar::error::need_more);
174 : }
175 583 : v = dig(*it);
176 583 : if(v == -1)
177 : {
178 : // expected DIGIT
179 0 : BOOST_HTTP_PROTO_RETURN_EC(
180 : grammar::error::need_more);
181 : }
182 583 : t.v = t.v + v;
183 583 : ++it;
184 :
185 583 : t.s = core::string_view(it0, it - it0);
186 583 : t.st = int_to_status(t.v);
187 583 : return t;
188 : }
189 :
190 : //------------------------------------------------
191 :
192 : auto
193 4841 : field_name_rule_t::
194 : parse(
195 : char const*& it,
196 : char const* end) const noexcept ->
197 : system::result<value_type>
198 : {
199 4841 : if( it == end )
200 1 : BOOST_HTTP_PROTO_RETURN_EC(
201 : grammar::error::need_more);
202 :
203 4840 : value_type v;
204 :
205 4840 : auto begin = it;
206 4840 : auto rv = grammar::parse(
207 : it, end, token_rule);
208 4840 : if( rv.has_error() || (it != end) )
209 : {
210 4237 : if( it != begin )
211 : {
212 4172 : v = core::string_view(begin, it - begin);
213 4172 : return v;
214 : }
215 65 : return error::bad_field_name;
216 : }
217 :
218 603 : v = core::string_view(begin, end - begin);
219 603 : return v;
220 : }
221 :
222 : auto
223 4419 : field_value_rule_t::
224 : parse(
225 : char const*& it,
226 : char const* end) const noexcept ->
227 : system::result<value_type>
228 : {
229 4419 : value_type v;
230 4419 : if( it == end )
231 : {
232 199 : v.value = core::string_view(it, 0);
233 199 : return v;
234 : }
235 :
236 : // field-line = field-name ":" OWS field-value OWS
237 : // field-value = *field-content
238 : // field-content = field-vchar
239 : // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
240 : // field-vchar = VCHAR / obs-text
241 : // obs-text = %x80-FF
242 : // VCHAR = %x21-7E
243 : // ; visible (printing) characters
244 :
245 15877 : auto is_field_vchar = [](unsigned char ch)
246 : {
247 15877 : return (ch >= 0x21 && ch <= 0x7e) || ch >= 0x80;
248 : };
249 :
250 4220 : char const* s0 = nullptr;
251 4220 : char const* s1 = nullptr;
252 :
253 4220 : bool has_crlf = false;
254 4220 : bool has_obs_fold = false;
255 :
256 26910 : while( it < end )
257 : {
258 25952 : auto ch = *it;
259 25952 : if( ws(ch) )
260 : {
261 6131 : ++it;
262 6131 : continue;
263 : }
264 :
265 19821 : if( ch == '\r' )
266 : {
267 : // too short to know if we have a potential obs-fold
268 : // occurrence
269 3944 : if( end - it < 2 )
270 200 : BOOST_HTTP_PROTO_RETURN_EC(
271 : grammar::error::need_more);
272 :
273 3744 : if( it[1] != '\n' )
274 53 : goto done;
275 :
276 3691 : if( end - it < 3 )
277 171 : BOOST_HTTP_PROTO_RETURN_EC(
278 : grammar::error::need_more);
279 :
280 3520 : if(! ws(it[2]) )
281 : {
282 2804 : has_crlf = true;
283 2804 : goto done;
284 : }
285 :
286 716 : has_obs_fold = true;
287 716 : it = it + 3;
288 716 : continue;
289 716 : }
290 :
291 15877 : if(! is_field_vchar(ch) )
292 : {
293 34 : goto done;
294 : }
295 :
296 15843 : if(! s0 )
297 3595 : s0 = it;
298 :
299 15843 : ++it;
300 15843 : s1 = it;
301 : }
302 :
303 958 : done:
304 : // later routines wind up doing pointer
305 : // subtraction using the .data() member
306 : // of the value so we need a valid 0-len range
307 3849 : if(! s0 )
308 : {
309 462 : s0 = it;
310 462 : s1 = s0;
311 : }
312 :
313 3849 : v.value = core::string_view(s0, s1 - s0);
314 3849 : v.has_crlf = has_crlf;
315 3849 : v.has_obs_fold = has_obs_fold;
316 3849 : return v;
317 : }
318 :
319 : auto
320 6957 : field_rule_t::
321 : parse(
322 : char const*& it,
323 : char const* end) const noexcept ->
324 : system::result<value_type>
325 : {
326 6957 : if(it == end)
327 : {
328 197 : BOOST_HTTP_PROTO_RETURN_EC(
329 : grammar::error::need_more);
330 : }
331 : // check for leading CRLF
332 6760 : if(it[0] == '\r')
333 : {
334 2224 : ++it;
335 2224 : if(it == end)
336 : {
337 134 : BOOST_HTTP_PROTO_RETURN_EC(
338 : grammar::error::need_more);
339 : }
340 2090 : if(*it != '\n')
341 : {
342 21 : BOOST_HTTP_PROTO_RETURN_EC(
343 : grammar::error::mismatch);
344 : }
345 : // end of fields
346 2069 : ++it;
347 2069 : BOOST_HTTP_PROTO_RETURN_EC(
348 : grammar::error::end_of_range);
349 : }
350 :
351 4536 : value_type v;
352 : auto rv = grammar::parse(
353 4536 : it, end, grammar::tuple_rule(
354 : field_name_rule,
355 4536 : grammar::delim_rule(':'),
356 : field_value_rule,
357 4536 : crlf_rule));
358 :
359 4536 : if( rv.has_error() )
360 1739 : return rv.error();
361 :
362 2797 : auto val = rv.value();
363 2797 : v.name = std::get<0>(val);
364 2797 : v.value = std::get<2>(val).value;
365 2797 : v.has_obs_fold = std::get<2>(val).has_obs_fold;
366 :
367 2797 : return v;
368 : }
369 :
370 : //------------------------------------------------
371 :
372 : void
373 241 : remove_obs_fold(
374 : char* it,
375 : char const* const end) noexcept
376 : {
377 2247 : while(it != end)
378 : {
379 2224 : if(*it != '\r')
380 : {
381 1628 : ++it;
382 1628 : continue;
383 : }
384 596 : if(end - it < 3)
385 218 : break;
386 378 : BOOST_ASSERT(it[1] == '\n');
387 756 : if( it[1] == '\n' &&
388 378 : ws(it[2]))
389 : {
390 375 : it[0] = ' ';
391 375 : it[1] = ' ';
392 375 : it += 3;
393 : }
394 : else
395 : {
396 3 : ++it;
397 : }
398 : }
399 241 : }
400 :
401 : //------------------------------------------------
402 :
403 : system::result<hex_rule_t::value_type>
404 244 : hex_rule_t::
405 : parse(
406 : char const*& it,
407 : char const* end) const noexcept
408 : {
409 244 : value_type v;
410 :
411 : constexpr
412 244 : auto const max_size =
413 : (std::numeric_limits<std::size_t>::max)() >> 4;
414 :
415 244 : auto const begin = it;
416 497 : for( ; it < end; ++it )
417 : {
418 493 : auto n = grammar::hexdig_value(*it);
419 493 : if(n < 0)
420 : {
421 239 : if( it == begin )
422 84 : BOOST_HTTP_PROTO_RETURN_EC(
423 : grammar::error::mismatch);
424 :
425 155 : return v;
426 : }
427 :
428 : // we know that so long as we can shift the current
429 : // value up 4 bits, we'll be able to fit the hex
430 : // digit we've just parsed
431 254 : if( v.v > max_size )
432 1 : BOOST_HTTP_PROTO_RETURN_EC(
433 : grammar::error::invalid);
434 :
435 253 : v.v = (v.v << 4) | (std::size_t)n;
436 : }
437 4 : return v;
438 : }
439 :
440 : system::result<void>
441 0 : last_chunk_rule_t::
442 : parse(
443 : char const*& it,
444 : char const* end) const noexcept
445 : {
446 : constexpr
447 0 : std::size_t const last_chunk_len = 5; // 0\r\n\r\n
448 :
449 0 : if( static_cast<std::size_t>(end - it) <
450 : last_chunk_len )
451 0 : BOOST_HTTP_PROTO_RETURN_EC(
452 : grammar::error::need_more);
453 :
454 0 : core::string_view str(it, end - it);
455 0 : if( str != "0\r\n\r\n" )
456 0 : BOOST_HTTP_PROTO_RETURN_EC(grammar::error::invalid);
457 :
458 0 : it += last_chunk_len;
459 :
460 0 : return {};
461 : }
462 :
463 : } // detail
464 : } // http_proto
465 : } // boost
|