GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/rfc/detail/rules.cpp
Date: 2024-07-18 19:38:55
Exec Total Coverage
Lines: 169 185 91.4%
Functions: 10 11 90.9%
Branches: 101 114 88.6%

Line Branch Exec Source
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
2/2
✓ Branch 0 taken 1006 times.
✓ Branch 1 taken 5281 times.
6287 if(it == end)
39 1006 return grammar::error::need_more;
40
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 5250 times.
5281 if(*it != '\r')
41 31 return grammar::error::mismatch;
42 5250 ++it;
43
2/2
✓ Branch 0 taken 163 times.
✓ Branch 1 taken 5087 times.
5250 if(it == end)
44 163 return grammar::error::need_more;
45
2/2
✓ Branch 0 taken 52 times.
✓ Branch 1 taken 5035 times.
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
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 3423 times.
3594 if(it == end)
62 {
63 // expected "HTTP/"
64 171 BOOST_HTTP_PROTO_RETURN_EC(
65 grammar::error::need_more);
66 }
67
2/2
✓ Branch 0 taken 2883 times.
✓ Branch 1 taken 540 times.
3423 if(end - it >= 5)
68 {
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2883 times.
2883 if(std::memcmp(
70 it, "HTTP/", 5) != 0)
71 {
72 BOOST_HTTP_PROTO_RETURN_EC(
73 grammar::error::mismatch);
74 }
75 2883 it += 5;
76 }
77
2/2
✓ Branch 0 taken 90 times.
✓ Branch 1 taken 3333 times.
3423 if(it == end)
78 {
79 // expected DIGIT
80 90 BOOST_HTTP_PROTO_RETURN_EC(
81 grammar::error::need_more);
82 }
83
2/2
✓ Branch 1 taken 540 times.
✓ Branch 2 taken 2793 times.
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
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 2559 times.
2793 if(it == end)
91 {
92 // expected "."
93 234 BOOST_HTTP_PROTO_RETURN_EC(
94 grammar::error::need_more);
95 }
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2559 times.
2559 if(*it != '.')
97 {
98 // expected "."
99 BOOST_HTTP_PROTO_RETURN_EC(
100 grammar::error::need_more);
101 }
102 2559 ++it;
103
2/2
✓ Branch 0 taken 89 times.
✓ Branch 1 taken 2470 times.
2559 if(it == end)
104 {
105 // expected DIGIT
106 89 BOOST_HTTP_PROTO_RETURN_EC(
107 grammar::error::need_more);
108 }
109
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2470 times.
2470 if(! grammar::digit_chars(*it))
110 {
111 // expected DIGIT
112 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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1773 times.
1773 if(uc > 9)
133 return -1;
134 1773 return uc;
135 };
136
137
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 599 times.
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 599 times.
599 if(v == -1)
146 {
147 // expected DIGIT
148 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
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 591 times.
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 591 times.
591 if(v == -1)
162 {
163 // expected DIGIT
164 BOOST_HTTP_PROTO_RETURN_EC(
165 grammar::error::mismatch);
166 }
167 591 t.v = t.v + (10 * v);
168 591 ++it;
169
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 583 times.
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
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 583 times.
583 if(v == -1)
177 {
178 // expected DIGIT
179 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
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4840 times.
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
6/6
✓ Branch 1 taken 4775 times.
✓ Branch 2 taken 65 times.
✓ Branch 3 taken 4172 times.
✓ Branch 4 taken 603 times.
✓ Branch 5 taken 4237 times.
✓ Branch 6 taken 603 times.
4840 if( rv.has_error() || (it != end) )
209 {
210
2/2
✓ Branch 0 taken 4172 times.
✓ Branch 1 taken 65 times.
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
2/2
✓ Branch 0 taken 199 times.
✓ Branch 1 taken 4220 times.
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
6/6
✓ Branch 0 taken 15843 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 15831 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 34 times.
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
2/2
✓ Branch 0 taken 25952 times.
✓ Branch 1 taken 958 times.
26910 while( it < end )
257 {
258 25952 auto ch = *it;
259
2/2
✓ Branch 1 taken 6131 times.
✓ Branch 2 taken 19821 times.
25952 if( ws(ch) )
260 {
261 6131 ++it;
262 6131 continue;
263 }
264
265
2/2
✓ Branch 0 taken 3944 times.
✓ Branch 1 taken 15877 times.
19821 if( ch == '\r' )
266 {
267 // too short to know if we have a potential obs-fold
268 // occurrence
269
2/2
✓ Branch 0 taken 200 times.
✓ Branch 1 taken 3744 times.
3944 if( end - it < 2 )
270 200 BOOST_HTTP_PROTO_RETURN_EC(
271 grammar::error::need_more);
272
273
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 3691 times.
3744 if( it[1] != '\n' )
274 53 goto done;
275
276
2/2
✓ Branch 0 taken 171 times.
✓ Branch 1 taken 3520 times.
3691 if( end - it < 3 )
277 171 BOOST_HTTP_PROTO_RETURN_EC(
278 grammar::error::need_more);
279
280
2/2
✓ Branch 1 taken 2804 times.
✓ Branch 2 taken 716 times.
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
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 15843 times.
15877 if(! is_field_vchar(ch) )
292 {
293 34 goto done;
294 }
295
296
2/2
✓ Branch 0 taken 3595 times.
✓ Branch 1 taken 12248 times.
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
2/2
✓ Branch 0 taken 462 times.
✓ Branch 1 taken 3387 times.
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
2/2
✓ Branch 0 taken 197 times.
✓ Branch 1 taken 6760 times.
6957 if(it == end)
327 {
328 197 BOOST_HTTP_PROTO_RETURN_EC(
329 grammar::error::need_more);
330 }
331 // check for leading CRLF
332
2/2
✓ Branch 0 taken 2224 times.
✓ Branch 1 taken 4536 times.
6760 if(it[0] == '\r')
333 {
334 2224 ++it;
335
2/2
✓ Branch 0 taken 134 times.
✓ Branch 1 taken 2090 times.
2224 if(it == end)
336 {
337 134 BOOST_HTTP_PROTO_RETURN_EC(
338 grammar::error::need_more);
339 }
340
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 2069 times.
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
2/2
✓ Branch 1 taken 1739 times.
✓ Branch 2 taken 2797 times.
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
2/2
✓ Branch 0 taken 2224 times.
✓ Branch 1 taken 23 times.
2247 while(it != end)
378 {
379
2/2
✓ Branch 0 taken 1628 times.
✓ Branch 1 taken 596 times.
2224 if(*it != '\r')
380 {
381 1628 ++it;
382 1628 continue;
383 }
384
2/2
✓ Branch 0 taken 218 times.
✓ Branch 1 taken 378 times.
596 if(end - it < 3)
385 218 break;
386
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 378 times.
378 BOOST_ASSERT(it[1] == '\n');
387
5/6
✓ Branch 0 taken 378 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 375 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 375 times.
✓ Branch 5 taken 3 times.
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
2/2
✓ Branch 0 taken 493 times.
✓ Branch 1 taken 4 times.
497 for( ; it < end; ++it )
417 {
418 493 auto n = grammar::hexdig_value(*it);
419
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 254 times.
493 if(n < 0)
420 {
421
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 155 times.
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
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 253 times.
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 last_chunk_rule_t::
442 parse(
443 char const*& it,
444 char const* end) const noexcept
445 {
446 constexpr
447 std::size_t const last_chunk_len = 5; // 0\r\n\r\n
448
449 if( static_cast<std::size_t>(end - it) <
450 last_chunk_len )
451 BOOST_HTTP_PROTO_RETURN_EC(
452 grammar::error::need_more);
453
454 core::string_view str(it, end - it);
455 if( str != "0\r\n\r\n" )
456 BOOST_HTTP_PROTO_RETURN_EC(grammar::error::invalid);
457
458 it += last_chunk_len;
459
460 return {};
461 }
462
463 } // detail
464 } // http_proto
465 } // boost
466