GCC Code Coverage Report


Directory: ./
File: libs/http_proto/src/rfc/detail/rules.cpp
Date: 2025-12-02 19:05:14
Exec Total Coverage
Lines: 158 166 95.2%
Functions: 10 10 100.0%
Branches: 91 102 89.2%

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 "src/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 "src/rfc/detail/rules.hpp"
26
27 namespace boost {
28 namespace http_proto {
29 namespace detail {
30
31 auto
32 23590 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 1269 times.
✓ Branch 1 taken 22321 times.
23590 if(it == end)
39 1269 return grammar::error::need_more;
40
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 22292 times.
22321 if(*it != '\r')
41 29 return grammar::error::mismatch;
42 22292 ++it;
43
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 22099 times.
22292 if(it == end)
44 193 return grammar::error::need_more;
45
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 22048 times.
22099 if(*it != '\n')
46 51 return grammar::error::mismatch;
47 22048 ++it;
48 22048 return {};
49 }
50
51 //------------------------------------------------
52
53 auto
54 13208 version_rule_t::
55 parse(
56 char const*& it,
57 char const* end) const noexcept ->
58 system::result<value_type>
59 {
60 13208 value_type v = 0;
61
2/2
✓ Branch 0 taken 887 times.
✓ Branch 1 taken 12321 times.
13208 if(it == end)
62 {
63 // expected "HTTP/"
64 887 BOOST_HTTP_PROTO_RETURN_EC(
65 grammar::error::need_more);
66 }
67
2/2
✓ Branch 0 taken 11649 times.
✓ Branch 1 taken 672 times.
12321 if(end - it >= 5)
68 {
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11649 times.
11649 if(std::memcmp(
70 it, "HTTP/", 5) != 0)
71 {
72 BOOST_HTTP_PROTO_RETURN_EC(
73 grammar::error::mismatch);
74 }
75 11649 it += 5;
76 }
77
2/2
✓ Branch 0 taken 123 times.
✓ Branch 1 taken 12198 times.
12321 if(it == end)
78 {
79 // expected DIGIT
80 123 BOOST_HTTP_PROTO_RETURN_EC(
81 grammar::error::need_more);
82 }
83
2/2
✓ Branch 1 taken 672 times.
✓ Branch 2 taken 11526 times.
12198 if(! grammar::digit_chars(*it))
84 {
85 // expected DIGIT
86 672 BOOST_HTTP_PROTO_RETURN_EC(
87 grammar::error::need_more);
88 }
89 11526 v = 10 * (*it++ - '0');
90
2/2
✓ Branch 0 taken 267 times.
✓ Branch 1 taken 11259 times.
11526 if(it == end)
91 {
92 // expected "."
93 267 BOOST_HTTP_PROTO_RETURN_EC(
94 grammar::error::need_more);
95 }
96
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11259 times.
11259 if(*it != '.')
97 {
98 // expected "."
99 BOOST_HTTP_PROTO_RETURN_EC(
100 grammar::error::need_more);
101 }
102 11259 ++it;
103
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 11138 times.
11259 if(it == end)
104 {
105 // expected DIGIT
106 121 BOOST_HTTP_PROTO_RETURN_EC(
107 grammar::error::need_more);
108 }
109
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 11138 times.
11138 if(! grammar::digit_chars(*it))
110 {
111 // expected DIGIT
112 BOOST_HTTP_PROTO_RETURN_EC(
113 grammar::error::need_more);
114 }
115 11138 v += *it++ - '0';
116 11138 return v;
117 }
118
119 //------------------------------------------------
120
121 auto
122 1034 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 2952 [](char c) -> int
130 {
131 2952 unsigned char uc(c - '0');
132
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2952 times.
2952 if(uc > 9)
133 return -1;
134 2952 return uc;
135 };
136
137
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 1008 times.
1034 if(it == end)
138 {
139 // end
140 26 BOOST_HTTP_PROTO_RETURN_EC(
141 grammar::error::need_more);
142 }
143 1008 auto it0 = it;
144 1008 int v = dig(*it);
145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1008 times.
1008 if(v == -1)
146 {
147 // expected DIGIT
148 BOOST_HTTP_PROTO_RETURN_EC(
149 grammar::error::mismatch);
150 }
151 1008 value_type t;
152 1008 t.v = 100 * v;
153 1008 ++it;
154
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 984 times.
1008 if(it == end)
155 {
156 // end
157 24 BOOST_HTTP_PROTO_RETURN_EC(
158 grammar::error::need_more);
159 }
160 984 v = dig(*it);
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 984 times.
984 if(v == -1)
162 {
163 // expected DIGIT
164 BOOST_HTTP_PROTO_RETURN_EC(
165 grammar::error::mismatch);
166 }
167 984 t.v = t.v + (10 * v);
168 984 ++it;
169
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 960 times.
984 if(it == end)
170 {
171 // end
172 24 BOOST_HTTP_PROTO_RETURN_EC(
173 grammar::error::need_more);
174 }
175 960 v = dig(*it);
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 960 times.
960 if(v == -1)
177 {
178 // expected DIGIT
179 BOOST_HTTP_PROTO_RETURN_EC(
180 grammar::error::need_more);
181 }
182 960 t.v = t.v + v;
183 960 ++it;
184
185 960 t.s = core::string_view(it0, it - it0);
186 960 t.st = int_to_status(t.v);
187 960 return t;
188 }
189
190 //------------------------------------------------
191
192 auto
193 934 reason_phrase_rule_t::
194 parse(
195 char const*& it,
196 char const* end) const noexcept ->
197 system::result<value_type>
198 {
199 934 auto begin = it;
200 934 it = grammar::find_if_not(it, end, ws_vchars);
201 934 return core::string_view(begin, it);
202 }
203
204 //------------------------------------------------
205
206 auto
207 14129 field_name_rule_t::
208 parse(
209 char const*& it,
210 char const* end) const noexcept ->
211 system::result<value_type>
212 {
213
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14128 times.
14129 if( it == end )
214 1 BOOST_HTTP_PROTO_RETURN_EC(
215 grammar::error::need_more);
216
217 14128 value_type v;
218
219 14128 auto begin = it;
220 14128 auto rv = grammar::parse(
221 it, end, token_rule);
222
6/6
✓ Branch 1 taken 14062 times.
✓ Branch 2 taken 66 times.
✓ Branch 3 taken 13123 times.
✓ Branch 4 taken 939 times.
✓ Branch 5 taken 13189 times.
✓ Branch 6 taken 939 times.
14128 if( rv.has_error() || (it != end) )
223 {
224
2/2
✓ Branch 0 taken 13123 times.
✓ Branch 1 taken 66 times.
13189 if( it != begin )
225 {
226 13123 v = core::string_view(begin, it - begin);
227 13123 return v;
228 }
229 66 return error::bad_field_name;
230 }
231
232 939 v = core::string_view(begin, end - begin);
233 939 return v;
234 }
235
236 auto
237 13388 field_value_rule_t::
238 parse(
239 char const*& it,
240 char const* end) const noexcept ->
241 system::result<value_type>
242 {
243 13388 value_type v;
244
2/2
✓ Branch 0 taken 239 times.
✓ Branch 1 taken 13149 times.
13388 if( it == end )
245 {
246 239 v.value = core::string_view(it, 0);
247 239 return v;
248 }
249
250 // field-line = field-name ":" OWS field-value OWS
251 // field-value = *field-content
252 // field-content = field-vchar
253 // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
254 // field-vchar = VCHAR / obs-text
255 // obs-text = %x80-FF
256 // VCHAR = %x21-7E
257 // ; visible (printing) characters
258
259 57600 auto is_field_vchar = [](unsigned char ch)
260 {
261
6/6
✓ Branch 0 taken 57566 times.
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 12 times.
✓ Branch 3 taken 57554 times.
✓ Branch 4 taken 12 times.
✓ Branch 5 taken 34 times.
57600 return (ch >= 0x21 && ch <= 0x7e) || ch >= 0x80;
262 };
263
264 13149 char const* s0 = nullptr;
265 13149 char const* s1 = nullptr;
266
267 13149 bool has_crlf = false;
268 13149 bool has_obs_fold = false;
269
270
2/2
✓ Branch 0 taken 85371 times.
✓ Branch 1 taken 1120 times.
86491 while( it < end )
271 {
272 85371 auto ch = *it;
273
2/2
✓ Branch 1 taken 15046 times.
✓ Branch 2 taken 70325 times.
85371 if( ws(ch) )
274 {
275 15046 ++it;
276 15046 continue;
277 }
278
279
2/2
✓ Branch 0 taken 12725 times.
✓ Branch 1 taken 57600 times.
70325 if( ch == '\r' )
280 {
281 // too short to know if we have a potential obs-fold
282 // occurrence
283
2/2
✓ Branch 0 taken 240 times.
✓ Branch 1 taken 12485 times.
12725 if( end - it < 2 )
284 240 BOOST_HTTP_PROTO_RETURN_EC(
285 grammar::error::need_more);
286
287
2/2
✓ Branch 0 taken 53 times.
✓ Branch 1 taken 12432 times.
12485 if( it[1] != '\n' )
288 53 goto done;
289
290
2/2
✓ Branch 0 taken 214 times.
✓ Branch 1 taken 12218 times.
12432 if( end - it < 3 )
291 214 BOOST_HTTP_PROTO_RETURN_EC(
292 grammar::error::need_more);
293
294
2/2
✓ Branch 1 taken 11488 times.
✓ Branch 2 taken 730 times.
12218 if(! ws(it[2]) )
295 {
296 11488 has_crlf = true;
297 11488 goto done;
298 }
299
300 730 has_obs_fold = true;
301 730 it = it + 3;
302 730 continue;
303 730 }
304
305
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 57566 times.
57600 if(! is_field_vchar(ch) )
306 {
307 34 goto done;
308 }
309
310
2/2
✓ Branch 0 taken 12480 times.
✓ Branch 1 taken 45086 times.
57566 if(! s0 )
311 12480 s0 = it;
312
313 57566 ++it;
314 57566 s1 = it;
315 }
316
317 1120 done:
318 // later routines wind up doing pointer
319 // subtraction using the .data() member
320 // of the value so we need a valid 0-len range
321
2/2
✓ Branch 0 taken 505 times.
✓ Branch 1 taken 12190 times.
12695 if(! s0 )
322 {
323 505 s0 = it;
324 505 s1 = s0;
325 }
326
327 12695 v.value = core::string_view(s0, s1 - s0);
328 12695 v.has_crlf = has_crlf;
329 12695 v.has_obs_fold = has_obs_fold;
330 12695 return v;
331 }
332
333 auto
334 24862 field_rule_t::
335 parse(
336 char const*& it,
337 char const* end) const noexcept ->
338 system::result<value_type>
339 {
340
2/2
✓ Branch 0 taken 229 times.
✓ Branch 1 taken 24633 times.
24862 if(it == end)
341 {
342 229 BOOST_HTTP_PROTO_RETURN_EC(
343 grammar::error::need_more);
344 }
345 // check for leading CRLF
346
2/2
✓ Branch 0 taken 10739 times.
✓ Branch 1 taken 13894 times.
24633 if(it[0] == '\r')
347 {
348 10739 ++it;
349
2/2
✓ Branch 0 taken 167 times.
✓ Branch 1 taken 10572 times.
10739 if(it == end)
350 {
351 167 BOOST_HTTP_PROTO_RETURN_EC(
352 grammar::error::need_more);
353 }
354
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 10551 times.
10572 if(*it != '\n')
355 {
356 21 BOOST_HTTP_PROTO_RETURN_EC(
357 grammar::error::mismatch);
358 }
359 // end of fields
360 10551 ++it;
361 10551 BOOST_HTTP_PROTO_RETURN_EC(
362 grammar::error::end_of_range);
363 }
364
365 13894 value_type v;
366 auto rv = grammar::parse(
367 13894 it, end, grammar::tuple_rule(
368 field_name_rule,
369 13894 grammar::delim_rule(':'),
370 field_value_rule,
371 13894 crlf_rule));
372
373
2/2
✓ Branch 1 taken 2422 times.
✓ Branch 2 taken 11472 times.
13894 if( rv.has_error() )
374 2422 return rv.error();
375
376 11472 auto val = rv.value();
377 11472 v.name = std::get<0>(val);
378 11472 v.value = std::get<2>(val).value;
379 11472 v.has_obs_fold = std::get<2>(val).has_obs_fold;
380
381 11472 return v;
382 }
383
384 //------------------------------------------------
385
386 void
387 231 remove_obs_fold(
388 char* it,
389 char const* const end) noexcept
390 {
391
2/2
✓ Branch 0 taken 2208 times.
✓ Branch 1 taken 21 times.
2229 while(it != end)
392 {
393
2/2
✓ Branch 0 taken 1624 times.
✓ Branch 1 taken 584 times.
2208 if(*it != '\r')
394 {
395 1624 ++it;
396 1624 continue;
397 }
398
2/2
✓ Branch 0 taken 210 times.
✓ Branch 1 taken 374 times.
584 if(end - it < 3)
399 210 break;
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 374 times.
374 BOOST_ASSERT(it[1] == '\n');
401
3/6
✓ Branch 0 taken 374 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 374 times.
✗ Branch 5 not taken.
748 if( it[1] == '\n' &&
402 374 ws(it[2]))
403 {
404 374 it[0] = ' ';
405 374 it[1] = ' ';
406 374 it += 3;
407 }
408 else
409 {
410 ++it;
411 }
412 }
413 231 }
414
415 } // detail
416 } // http_proto
417 } // boost
418