GCC Code Coverage Report


Directory: libs/http_proto/
File: src/detail/header.cpp
Date: 2025-10-12 23:51:57
Exec Total Coverage
Lines: 564 611 92.3%
Functions: 47 57 82.5%
Branches: 280 342 81.9%

Line Branch Exec Source
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/http_proto
9 //
10
11 #include "src/rfc/detail/rules.hpp"
12 #include "src/rfc/detail/transfer_coding_rule.hpp"
13
14 #include <boost/http_proto/detail/header.hpp>
15 #include <boost/http_proto/field.hpp>
16 #include <boost/http_proto/header_limits.hpp>
17 #include <boost/http_proto/rfc/list_rule.hpp>
18 #include <boost/http_proto/rfc/token_rule.hpp>
19 #include <boost/http_proto/rfc/upgrade_rule.hpp>
20 #include <boost/assert.hpp>
21 #include <boost/assert/source_location.hpp>
22 #include <boost/static_assert.hpp>
23 #include <boost/url/grammar/ci_string.hpp>
24 #include <boost/url/grammar/parse.hpp>
25 #include <boost/url/grammar/range_rule.hpp>
26 #include <boost/url/grammar/recycled.hpp>
27 #include <boost/url/grammar/unsigned_rule.hpp>
28
29 #include <utility>
30
31 namespace boost {
32 namespace http_proto {
33 namespace detail {
34
35 //------------------------------------------------
36
37 auto
38 87 header::
39 entry::
40 operator+(
41 std::size_t dv) const noexcept ->
42 entry
43 {
44 return {
45 static_cast<
46 87 offset_type>(np + dv),
47 87 nn,
48 static_cast<
49 87 offset_type>(vp + dv),
50 87 vn,
51 87 id };
52 }
53
54 auto
55 101 header::
56 entry::
57 operator-(
58 std::size_t dv) const noexcept ->
59 entry
60 {
61 return {
62 static_cast<
63 101 offset_type>(np - dv),
64 101 nn,
65 static_cast<
66 101 offset_type>(vp - dv),
67 101 vn,
68 101 id };
69 }
70
71 //------------------------------------------------
72
73 constexpr field header::unknown_field;
74
75 //------------------------------------------------
76
77 constexpr
78 header::
79 header(fields_tag) noexcept
80 : kind(detail::kind::fields)
81 , cbuf("\r\n")
82 , size(2)
83 , fld{}
84 {
85 }
86
87 constexpr
88 header::
89 header(request_tag) noexcept
90 : kind(detail::kind::request)
91 , cbuf("GET / HTTP/1.1\r\n\r\n")
92 , size(18)
93 , prefix(16)
94 , req{ 3, 1,
95 http_proto::method::get }
96 {
97 }
98
99 constexpr
100 header::
101 header(response_tag) noexcept
102 : kind(detail::kind::response)
103 , cbuf("HTTP/1.1 200 OK\r\n\r\n")
104 , size(19)
105 , prefix(17)
106 , res{ 200,
107 http_proto::status::ok }
108 {
109 }
110
111 //------------------------------------------------
112
113 header const*
114 2360 header::
115 get_default(detail::kind k) noexcept
116 {
117 static constexpr header h[3] = {
118 fields_tag{},
119 request_tag{},
120 response_tag{}};
121 2360 return &h[k];
122 }
123
124 11902 header::
125 11902 header(empty v) noexcept
126 11902 : kind(v.param)
127 {
128 11902 }
129
130 1289 header::
131 1289 header(detail::kind k) noexcept
132 1289 : header(*get_default(k))
133 {
134 1289 }
135
136 void
137 78 header::
138 swap(header& h) noexcept
139 {
140 78 std::swap(cbuf, h.cbuf);
141 78 std::swap(buf, h.buf);
142 78 std::swap(cap, h.cap);
143 78 std::swap(size, h.size);
144 78 std::swap(count, h.count);
145 78 std::swap(prefix, h.prefix);
146 78 std::swap(version, h.version);
147 78 std::swap(md, h.md);
148
3/3
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 55 times.
✓ Branch 2 taken 8 times.
78 switch(kind)
149 {
150 15 default:
151 case detail::kind::fields:
152 15 break;
153 55 case detail::kind::request:
154 55 std::swap(
155 55 req.method_len, h.req.method_len);
156 55 std::swap(
157 55 req.target_len, h.req.target_len);
158 55 std::swap(req.method, h.req.method);
159 55 break;
160 8 case detail::kind::response:
161 8 std::swap(
162 8 res.status_int, h.res.status_int);
163 8 std::swap(res.status, h.res.status);
164 8 break;
165 }
166 78 }
167
168 /* References:
169
170 6.3. Persistence
171 https://datatracker.ietf.org/doc/html/rfc7230#section-6.3
172 */
173 bool
174 22 header::
175 keep_alive() const noexcept
176 {
177
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 21 times.
22 if(md.payload == payload::error)
178 1 return false;
179
2/2
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 8 times.
21 if( version ==
180 http_proto::version::http_1_1)
181 {
182
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 10 times.
13 if(md.connection.close)
183 3 return false;
184 }
185 else
186 {
187
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
8 if(! md.connection.keep_alive)
188 4 return false;
189 }
190 // can't use to_eof in requests
191
3/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8 times.
14 BOOST_ASSERT(
192 kind != detail::kind::request ||
193 md.payload != payload::to_eof);
194
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 if(md.payload == payload::to_eof)
195 3 return false;
196 11 return true;
197 }
198
199 //------------------------------------------------
200
201 // return total bytes needed
202 // to store message of `size`
203 // bytes and `count` fields.
204 std::size_t
205 1994 header::
206 bytes_needed(
207 std::size_t size,
208 std::size_t count) noexcept
209 {
210 // make sure `size` is big enough
211 // to hold the largest default buffer:
212 // "HTTP/1.1 200 OK\r\n\r\n"
213
2/2
✓ Branch 0 taken 1241 times.
✓ Branch 1 taken 753 times.
1994 if(size < 19)
214 1241 size = 19;
215
216 // align size up to alignof(entry)
217 1994 size = (size + alignof(entry) - 1) & ~(alignof(entry) - 1);
218
219 1994 return size + count * sizeof(entry);
220 }
221
222 std::size_t
223 10006 header::
224 table_space(
225 std::size_t count) noexcept
226 {
227 return count *
228 10006 sizeof(header::entry);
229 }
230
231 std::size_t
232 10006 header::
233 table_space() const noexcept
234 {
235 10006 return table_space(count);
236 }
237
238 auto
239 2599 header::
240 tab() const noexcept ->
241 table
242 {
243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2599 times.
2599 BOOST_ASSERT(cap > 0);
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2599 times.
2599 BOOST_ASSERT(buf != nullptr);
245 2599 return table(buf + cap);
246 }
247
248 auto
249 702 header::
250 tab_() const noexcept ->
251 entry*
252 {
253 return reinterpret_cast<
254 702 entry*>(buf + cap);
255 }
256
257 // return true if header cbuf is a default
258 bool
259 41 header::
260 is_default() const noexcept
261 {
262 41 return buf == nullptr;
263 }
264
265 std::size_t
266 136 header::
267 find(
268 field id) const noexcept
269 {
270
2/2
✓ Branch 0 taken 59 times.
✓ Branch 1 taken 77 times.
136 if(count == 0)
271 59 return 0;
272 77 std::size_t i = 0;
273 77 auto const* p = &tab()[0];
274
2/2
✓ Branch 0 taken 91 times.
✓ Branch 1 taken 29 times.
120 while(i < count)
275 {
276
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 43 times.
91 if(p->id == id)
277 48 break;
278 43 ++i;
279 43 --p;
280 }
281 77 return i;
282 }
283
284 std::size_t
285 42 header::
286 find(
287 core::string_view name) const noexcept
288 {
289
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 36 times.
42 if(count == 0)
290 6 return 0;
291 36 std::size_t i = 0;
292 36 auto const* p = &tab()[0];
293
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 3 times.
57 while(i < count)
294 {
295 core::string_view s(
296 54 cbuf + prefix + p->np,
297 54 p->nn);
298
2/2
✓ Branch 1 taken 33 times.
✓ Branch 2 taken 21 times.
54 if(grammar::ci_is_equal(s, name))
299 33 break;
300 21 ++i;
301 21 --p;
302 }
303 36 return i;
304 }
305
306 void
307 1116 header::
308 copy_table(
309 void* dest,
310 std::size_t n) const noexcept
311 {
312 // When `n == 0`, cbuf + cap may have incorrect
313 // alignment, which can trigger UB sanitizer.
314
2/2
✓ Branch 0 taken 1096 times.
✓ Branch 1 taken 20 times.
1116 if(n == 0)
315 1096 return;
316
317 20 std::memcpy(
318 reinterpret_cast<
319 20 entry*>(dest) - n,
320 reinterpret_cast<
321 entry const*>(
322 20 cbuf + cap) - n,
323 n * sizeof(entry));
324 }
325
326 void
327 1116 header::
328 copy_table(
329 void* dest) const noexcept
330 {
331 1116 copy_table(dest, count);
332 1116 }
333
334 // assign all the members but
335 // preserve the allocated memory
336 void
337 1095 header::
338 assign_to(
339 header& dest) const noexcept
340 {
341 1095 auto const buf_ = dest.buf;
342 1095 auto const cbuf_ = dest.cbuf;
343 1095 auto const cap_ = dest.cap;
344 1095 dest = *this;
345 1095 dest.buf = buf_;
346 1095 dest.cbuf = cbuf_;
347 1095 dest.cap = cap_;
348 1095 }
349
350 //------------------------------------------------
351 //
352 // Metadata
353 //
354 //------------------------------------------------
355
356 std::size_t
357 header::
358 maybe_count(
359 field id) const noexcept
360 {
361 if(kind == detail::kind::fields)
362 return std::size_t(-1);
363 switch(id)
364 {
365 case field::connection:
366 return md.connection.count;
367 case field::content_encoding:
368 return md.content_encoding.count;
369 case field::content_length:
370 return md.content_length.count;
371 case field::expect:
372 return md.expect.count;
373 case field::transfer_encoding:
374 return md.transfer_encoding.count;
375 case field::upgrade:
376 return md.upgrade.count;
377 default:
378 break;
379 }
380 return std::size_t(-1);
381 }
382
383 bool
384 24 header::
385 is_special(
386 field id) const noexcept
387 {
388
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 19 times.
24 if(kind == detail::kind::fields)
389 5 return false;
390
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 10 times.
19 switch(id)
391 {
392 9 case field::connection:
393 case field::content_encoding:
394 case field::content_length:
395 case field::expect:
396 case field::transfer_encoding:
397 case field::upgrade:
398 9 return true;
399 10 default:
400 10 break;
401 }
402 10 return false;
403 }
404
405 //------------------------------------------------
406
407 // called when the start-line changes
408 void
409 10610 header::
410 on_start_line()
411 {
412 // items in both the request-line
413 // and the status-line can affect
414 // the payload, for example whether
415 // or not EOF marks the end of the
416 // payload.
417
418 10610 update_payload();
419 10610 }
420
421 // called after a field is inserted
422 void
423 11780 header::
424 on_insert(
425 field id,
426 core::string_view v)
427 {
428
2/2
✓ Branch 0 taken 483 times.
✓ Branch 1 taken 11297 times.
11780 if(kind == detail::kind::fields)
429 483 return;
430
7/7
✓ Branch 0 taken 129 times.
✓ Branch 1 taken 4877 times.
✓ Branch 2 taken 136 times.
✓ Branch 3 taken 47 times.
✓ Branch 4 taken 4229 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 1855 times.
11297 switch(id)
431 {
432 129 case field::content_encoding:
433 129 return on_insert_content_encoding(v);
434 4877 case field::content_length:
435 4877 return on_insert_content_length(v);
436 136 case field::connection:
437 136 return on_insert_connection(v);
438 47 case field::expect:
439 47 return on_insert_expect(v);
440 4229 case field::transfer_encoding:
441 4229 return on_insert_transfer_encoding(v);
442 24 case field::upgrade:
443 24 return on_insert_upgrade(v);
444 1855 default:
445 1855 break;
446 }
447 }
448
449 // called when one field is erased
450 void
451 39 header::
452 on_erase(field id)
453 {
454
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 36 times.
39 if(kind == detail::kind::fields)
455 3 return;
456
6/7
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 10 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 5 times.
36 switch(id)
457 {
458 9 case field::connection:
459 9 return on_erase_connection();
460 case field::content_encoding:
461 return on_erase_content_encoding();
462 4 case field::content_length:
463 4 return on_erase_content_length();
464 10 case field::expect:
465 10 return on_erase_expect();
466 4 case field::transfer_encoding:
467 4 return on_erase_transfer_encoding();
468 4 case field::upgrade:
469 4 return on_erase_upgrade();
470 5 default:
471 5 break;
472 }
473 }
474
475 //------------------------------------------------
476
477 /*
478 https://datatracker.ietf.org/doc/html/rfc7230#section-6.1
479 */
480 void
481 143 header::
482 on_insert_connection(
483 core::string_view v)
484 {
485 143 ++md.connection.count;
486
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 142 times.
143 if(md.connection.ec.failed())
487 5 return;
488 auto rv = grammar::parse(
489
1/2
✓ Branch 2 taken 142 times.
✗ Branch 3 not taken.
142 v, list_rule(token_rule, 1));
490
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 138 times.
142 if(! rv)
491 {
492 4 md.connection.ec =
493 8 BOOST_HTTP_PROTO_ERR(
494 error::bad_connection);
495 4 return;
496 }
497 138 md.connection.ec = {};
498
2/2
✓ Branch 6 taken 149 times.
✓ Branch 7 taken 138 times.
287 for(auto t : *rv)
499 {
500
2/2
✓ Branch 2 taken 96 times.
✓ Branch 3 taken 53 times.
149 if(grammar::ci_is_equal(
501 t, "close"))
502 96 md.connection.close = true;
503
2/2
✓ Branch 2 taken 26 times.
✓ Branch 3 taken 27 times.
53 else if(grammar::ci_is_equal(
504 t, "keep-alive"))
505 26 md.connection.keep_alive = true;
506
2/2
✓ Branch 2 taken 20 times.
✓ Branch 3 taken 7 times.
27 else if(grammar::ci_is_equal(
507 t, "upgrade"))
508 20 md.connection.upgrade = true;
509 }
510
2/2
✓ Branch 1 taken 138 times.
✓ Branch 2 taken 4 times.
142 }
511
512 void
513 4878 header::
514 on_insert_content_length(
515 core::string_view v)
516 {
517 static
518 constexpr
519 grammar::unsigned_rule<
520 std::uint64_t> num_rule{};
521
522 4878 ++md.content_length.count;
523
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4876 times.
4878 if(md.content_length.ec.failed())
524 4695 return;
525 auto rv =
526 4876 grammar::parse(v, num_rule);
527
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 4871 times.
4876 if(! rv)
528 {
529 // parse failure
530 5 md.content_length.ec =
531 10 BOOST_HTTP_PROTO_ERR(
532 error::bad_content_length);
533 5 md.content_length.value = 0;
534 5 update_payload();
535 5 return;
536 }
537
2/2
✓ Branch 0 taken 4681 times.
✓ Branch 1 taken 190 times.
4871 if(md.content_length.count == 1)
538 {
539 // one value
540 4681 md.content_length.ec = {};
541 4681 md.content_length.value = *rv;
542 4681 update_payload();
543 4681 return;
544 }
545
2/2
✓ Branch 1 taken 7 times.
✓ Branch 2 taken 183 times.
190 if(*rv == md.content_length.value)
546 {
547 // ok: duplicate value
548 7 return;
549 }
550 // bad: different values
551 183 md.content_length.ec =
552 366 BOOST_HTTP_PROTO_ERR(
553 error::multiple_content_length);
554 183 md.content_length.value = 0;
555 183 update_payload();
556 }
557
558 void
559 53 header::
560 on_insert_expect(
561 core::string_view v)
562 {
563 53 ++md.expect.count;
564
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 45 times.
53 if(kind != detail::kind::request)
565 8 return;
566
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 41 times.
45 if(md.expect.ec.failed())
567 4 return;
568 // VFALCO Should we allow duplicate
569 // Expect fields that have 100-continue?
570
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 9 times.
73 if( md.expect.count > 1 ||
571
4/4
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 22 times.
✓ Branch 4 taken 19 times.
✓ Branch 5 taken 22 times.
73 ! grammar::ci_is_equal(v,
572 "100-continue"))
573 {
574 19 md.expect.ec =
575 38 BOOST_HTTP_PROTO_ERR(
576 error::bad_expect);
577 19 md.expect.is_100_continue = false;
578 19 return;
579 }
580 22 md.expect.is_100_continue = true;
581 }
582
583 void
584 4231 header::
585 on_insert_transfer_encoding(
586 core::string_view v)
587 {
588 4231 ++md.transfer_encoding.count;
589
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4230 times.
4231 if(md.transfer_encoding.ec.failed())
590 4223 return;
591
592 auto rv = grammar::parse(
593
1/2
✓ Branch 2 taken 4230 times.
✗ Branch 3 not taken.
4230 v, list_rule(transfer_coding_rule, 1));
594
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4226 times.
4230 if(! rv)
595 {
596 // parse error
597 4 goto error;
598 }
599
2/2
✓ Branch 7 taken 4233 times.
✓ Branch 8 taken 4222 times.
8455 for(auto t : *rv)
600 {
601
2/2
✓ Branch 0 taken 4229 times.
✓ Branch 1 taken 4 times.
4233 if(! md.transfer_encoding.is_chunked)
602 {
603
2/2
✓ Branch 0 taken 4210 times.
✓ Branch 1 taken 19 times.
4229 if(t.id == transfer_coding_rule_t::chunked)
604 4210 md.transfer_encoding.is_chunked = true;
605 4229 continue;
606 }
607
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(t.id == transfer_coding_rule_t::chunked)
608 {
609 // chunked appears twice
610 2 goto error;
611 }
612 // chunked must be last
613 2 goto error;
614
6/6
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 4229 times.
✓ Branch 4 taken 4222 times.
✓ Branch 5 taken 4 times.
✓ Branch 7 taken 4222 times.
✓ Branch 8 taken 4 times.
8463 }
615 4222 update_payload();
616 4222 return;
617
618 8 error:
619 8 md.transfer_encoding.ec =
620 16 BOOST_HTTP_PROTO_ERR(
621 error::bad_transfer_encoding);
622 8 md.transfer_encoding.is_chunked = false;
623 8 update_payload();
624
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4222 times.
4230 }
625
626 void
627 129 header::
628 on_insert_content_encoding(
629 core::string_view v)
630 {
631 129 ++md.content_encoding.count;
632
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 129 times.
129 if(md.content_encoding.ec.failed())
633 3 return;
634
635 auto rv = grammar::parse(
636
1/2
✓ Branch 2 taken 129 times.
✗ Branch 3 not taken.
129 v, list_rule(token_rule, 1));
637
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 128 times.
129 if(!rv)
638 {
639 1 md.content_encoding.ec =
640 2 BOOST_HTTP_PROTO_ERR(
641 error::bad_content_encoding);
642 1 md.content_encoding.coding =
643 content_coding::unknown;
644 1 return;
645 }
646
647
6/6
✓ Branch 2 taken 127 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 126 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 126 times.
128 if(rv->size() > 1 || md.content_encoding.count > 1)
648 {
649 2 md.content_encoding.coding =
650 content_coding::unknown;
651 2 return;
652 }
653
654 252 if(grammar::ci_is_equal(
655
2/2
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 64 times.
252 *rv->begin(), "deflate"))
656 {
657 62 md.content_encoding.coding =
658 content_coding::deflate;
659 }
660 128 else if(grammar::ci_is_equal(
661
1/2
✓ Branch 3 taken 64 times.
✗ Branch 4 not taken.
128 *rv->begin(), "gzip"))
662 {
663 64 md.content_encoding.coding =
664 content_coding::gzip;
665 }
666 else if(grammar::ci_is_equal(
667 *rv->begin(), "br"))
668 {
669 md.content_encoding.coding =
670 content_coding::br;
671 }
672 else
673 {
674 md.content_encoding.coding =
675 content_coding::unknown;
676 }
677
2/2
✓ Branch 1 taken 126 times.
✓ Branch 2 taken 3 times.
129 }
678
679 void
680 26 header::
681 on_insert_upgrade(
682 core::string_view v)
683 {
684 26 ++md.upgrade.count;
685
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 25 times.
26 if(md.upgrade.ec.failed())
686 5 return;
687
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24 times.
25 if( version !=
688 http_proto::version::http_1_1)
689 {
690 1 md.upgrade.ec =
691 2 BOOST_HTTP_PROTO_ERR(
692 error::bad_upgrade);
693 1 md.upgrade.websocket = false;
694 1 return;
695 }
696 auto rv = grammar::parse(
697
1/2
✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
24 v, upgrade_rule);
698
2/2
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
24 if(! rv)
699 {
700 3 md.upgrade.ec =
701 6 BOOST_HTTP_PROTO_ERR(
702 error::bad_upgrade);
703 3 md.upgrade.websocket = false;
704 3 return;
705 }
706
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 5 times.
21 if(! md.upgrade.websocket)
707 {
708
2/2
✓ Branch 6 taken 16 times.
✓ Branch 7 taken 7 times.
23 for(auto t : *rv)
709 {
710 16 if( grammar::ci_is_equal(
711
6/6
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 9 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
✓ Branch 5 taken 7 times.
26 t.name, "websocket") &&
712 10 t.version.empty())
713 {
714 9 md.upgrade.websocket = true;
715 9 break;
716 }
717 }
718 }
719
2/2
✓ Branch 1 taken 21 times.
✓ Branch 2 taken 3 times.
24 }
720
721 //------------------------------------------------
722
723 void
724 9 header::
725 on_erase_connection()
726 {
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(
728 md.connection.count > 0);
729 // reset and re-insert
730 9 auto n = md.connection.count - 1;
731 9 auto const p = cbuf + prefix;
732 9 auto const* e = &tab()[0];
733 9 md.connection = {};
734
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 while(n > 0)
735 {
736
2/2
✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1 times.
8 if(e->id == field::connection)
737 {
738
1/2
✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
7 on_insert_connection(
739 core::string_view(
740 7 p + e->vp, e->vn));
741 7 --n;
742 }
743 8 --e;
744 }
745 9 }
746
747 void
748 4 header::
749 on_erase_content_length()
750 {
751
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
752 md.content_length.count > 0);
753 4 --md.content_length.count;
754
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(md.content_length.count == 0)
755 {
756 // no Content-Length
757 1 md.content_length = {};
758 1 update_payload();
759 1 return;
760 }
761
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(! md.content_length.ec.failed())
762 {
763 // removing a duplicate value
764 2 return;
765 }
766 // reset and re-insert
767 1 auto n = md.content_length.count;
768 1 auto const p = cbuf + prefix;
769 1 auto const* e = &tab()[0];
770 1 md.content_length = {};
771
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 while(n > 0)
772 {
773
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(e->id == field::content_length)
774 {
775
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 on_insert_content_length(
776 core::string_view(
777 1 p + e->vp, e->vn));
778 1 --n;
779 }
780 1 --e;
781 }
782 1 update_payload();
783 }
784
785 void
786 10 header::
787 on_erase_expect()
788 {
789
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
10 BOOST_ASSERT(
790 md.expect.count > 0);
791 10 --md.expect.count;
792
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
10 if(kind != detail::kind::request)
793 1 return;
794
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
9 if(md.expect.count == 0)
795 {
796 // no Expect
797 3 md.expect = {};
798 3 return;
799 }
800 // VFALCO This should be uncommented
801 // if we want to allow multiple Expect
802 // fields with the value 100-continue
803 /*
804 if(! md.expect.ec.failed())
805 return;
806 */
807 // reset and re-insert
808 6 auto n = md.expect.count;
809 6 auto const p = cbuf + prefix;
810 6 auto const* e = &tab()[0];
811 6 md.expect = {};
812
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 6 times.
18 while(n > 0)
813 {
814
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
12 if(e->id == field::expect)
815 {
816 6 on_insert_expect(
817 core::string_view(
818 6 p + e->vp, e->vn));
819 6 --n;
820 }
821 12 --e;
822 }
823 }
824
825 void
826 4 header::
827 on_erase_transfer_encoding()
828 {
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
830 md.transfer_encoding.count > 0);
831 // reset and re-insert
832 4 auto n = md.transfer_encoding.count - 1;
833 4 auto const p = cbuf + prefix;
834 4 auto const* e = &tab()[0];
835 4 md.transfer_encoding = {};
836
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
7 while(n > 0)
837 {
838
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 if(e->id == field::transfer_encoding)
839 {
840
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 on_insert_transfer_encoding(
841 core::string_view(
842 2 p + e->vp, e->vn));
843 2 --n;
844 }
845 3 --e;
846 }
847 4 }
848
849 void
850 header::
851 on_erase_content_encoding()
852 {
853 BOOST_ASSERT(
854 md.content_encoding.count > 0);
855 --md.content_encoding.count;
856 if(md.content_encoding.count == 0)
857 {
858 // no Content-Encoding
859 md.content_encoding = {};
860 return;
861 }
862 // re-insert everything
863 --md.content_encoding.count;
864 // TODO
865 // on_insert_content_encoding();
866 }
867
868 // called when Upgrade is erased
869 void
870 4 header::
871 on_erase_upgrade()
872 {
873
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(
874 md.upgrade.count > 0);
875 4 --md.upgrade.count;
876
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 if(md.upgrade.count == 0)
877 {
878 // no Upgrade
879 2 md.upgrade = {};
880 2 return;
881 }
882 // reset and re-insert
883 2 auto n = md.upgrade.count;
884 2 auto const p = cbuf + prefix;
885 2 auto const* e = &tab()[0];
886 2 md.upgrade = {};
887
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 while(n > 0)
888 {
889
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if(e->id == field::upgrade)
890
1/2
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
2 on_insert_upgrade(
891 core::string_view(
892 2 p + e->vp, e->vn));
893 2 --n;
894 2 --e;
895 }
896 }
897
898 //------------------------------------------------
899
900 // called when all fields with id are removed
901 void
902 72 header::
903 on_erase_all(
904 field id)
905 {
906
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 51 times.
72 if(kind == detail::kind::fields)
907 21 return;
908
6/6
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 39 times.
51 switch(id)
909 {
910 3 case field::connection:
911 3 md.connection = {};
912 3 return;
913
914 2 case field::content_length:
915 2 md.content_length = {};
916 2 update_payload();
917 2 return;
918
919 5 case field::expect:
920 5 md.expect = {};
921 5 update_payload();
922 5 return;
923
924 1 case field::transfer_encoding:
925 1 md.transfer_encoding = {};
926 1 update_payload();
927 1 return;
928
929 1 case field::upgrade:
930 1 md.upgrade = {};
931 1 return;
932
933 39 default:
934 39 break;
935 }
936 }
937
938 //------------------------------------------------
939
940 /* References:
941
942 3.3. Message Body
943 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3
944
945 3.3.1. Transfer-Encoding
946 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.1
947
948 3.3.2. Content-Length
949 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
950 */
951 void
952 19719 header::
953 update_payload() noexcept
954 {
955
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19719 times.
19719 BOOST_ASSERT(kind !=
956 detail::kind::fields);
957
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19719 times.
19719 if(md.payload_override)
958 {
959 // e.g. response to
960 // a HEAD request
961 return;
962 }
963
964 /* If there is an error in either Content-Length
965 or Transfer-Encoding, then the payload is
966 undefined. Clients should probably close the
967 connection. Servers can send a Bad Request
968 and avoid reading any payload bytes.
969 */
970
2/2
✓ Branch 1 taken 188 times.
✓ Branch 2 taken 19531 times.
19719 if(md.content_length.ec.failed())
971 {
972 // invalid Content-Length
973 188 md.payload = payload::error;
974 188 md.payload_size = 0;
975 188 return;
976 }
977
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 19523 times.
19531 if(md.transfer_encoding.ec.failed())
978 {
979 // invalid Transfer-Encoding
980 8 md.payload = payload::error;
981 8 md.payload_size = 0;
982 8 return;
983 }
984
985 /* A sender MUST NOT send a Content-Length
986 header field in any message that contains
987 a Transfer-Encoding header field.
988 https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
989 */
990
2/2
✓ Branch 0 taken 4685 times.
✓ Branch 1 taken 14838 times.
19523 if( md.content_length.count > 0 &&
991
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4682 times.
4685 md.transfer_encoding.count > 0)
992 {
993 3 md.payload = payload::error;
994 3 md.payload_size = 0;
995 3 return;
996 }
997
998
2/2
✓ Branch 0 taken 1310 times.
✓ Branch 1 taken 18210 times.
19520 if(kind == detail::kind::response)
999 1310 goto do_response;
1000
1001 //--------------------------------------------
1002
1003 /* The presence of a message body in a
1004 request is signaled by a Content-Length
1005 or Transfer-Encoding header field. Request
1006 message framing is independent of method
1007 semantics, even if the method does not
1008 define any use for a message body.
1009 */
1010
2/2
✓ Branch 0 taken 4420 times.
✓ Branch 1 taken 13790 times.
18210 if(md.content_length.count > 0)
1011 {
1012
2/2
✓ Branch 0 taken 4393 times.
✓ Branch 1 taken 27 times.
4420 if(md.content_length.value > 0)
1013 {
1014 // non-zero Content-Length
1015 4393 md.payload = payload::size;
1016 4393 md.payload_size = md.content_length.value;
1017 4393 return;
1018 }
1019 // Content-Length: 0
1020 27 md.payload = payload::none;
1021 27 md.payload_size = 0;
1022 27 return;
1023 }
1024
2/2
✓ Branch 0 taken 4012 times.
✓ Branch 1 taken 9778 times.
13790 if(md.transfer_encoding.is_chunked)
1025 {
1026 // chunked
1027 4012 md.payload = payload::chunked;
1028 4012 md.payload_size = 0;
1029 4012 return;
1030 }
1031 // no payload
1032 9778 md.payload = payload::none;
1033 9778 md.payload_size = 0;
1034 9778 return;
1035
1036 //--------------------------------------------
1037 1310 do_response:
1038
1039
2/2
✓ Branch 0 taken 1299 times.
✓ Branch 1 taken 11 times.
1310 if( res.status_int / 100 == 1 || // 1xx e.g. Continue
1040
2/2
✓ Branch 0 taken 1297 times.
✓ Branch 1 taken 2 times.
1299 res.status_int == 204 || // No Content
1041
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1295 times.
1297 res.status_int == 304) // Not Modified
1042 {
1043 /* The correctness of any Content-Length
1044 here is defined by the particular
1045 resource, and cannot be determined
1046 here. In any case there is no payload.
1047 */
1048 15 md.payload = payload::none;
1049 15 md.payload_size = 0;
1050 15 return;
1051 }
1052
2/2
✓ Branch 0 taken 259 times.
✓ Branch 1 taken 1036 times.
1295 if(md.content_length.count > 0)
1053 {
1054
2/2
✓ Branch 0 taken 237 times.
✓ Branch 1 taken 22 times.
259 if(md.content_length.value > 0)
1055 {
1056 // Content-Length > 0
1057 237 md.payload = payload::size;
1058 237 md.payload_size = md.content_length.value;
1059 237 return;
1060 }
1061 // Content-Length: 0
1062 22 md.payload = payload::none;
1063 22 md.payload_size = 0;
1064 22 return;
1065 }
1066
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 843 times.
1036 if(md.transfer_encoding.is_chunked)
1067 {
1068 // chunked
1069 193 md.payload = payload::chunked;
1070 193 md.payload_size = 0;
1071 193 return;
1072 }
1073
1074 // eof needed
1075 843 md.payload = payload::to_eof;
1076 843 md.payload_size = 0;
1077 }
1078
1079 //------------------------------------------------
1080
1081 std::size_t
1082 546 header::
1083 count_crlf(
1084 core::string_view s) noexcept
1085 {
1086 546 auto it = s.data();
1087 546 auto len = s.size();
1088 546 std::size_t n = 0;
1089
2/2
✓ Branch 0 taken 18395 times.
✓ Branch 1 taken 546 times.
18941 while(len >= 2)
1090 {
1091
2/2
✓ Branch 0 taken 1737 times.
✓ Branch 1 taken 16658 times.
18395 if( it[0] == '\r' &&
1092
1/2
✓ Branch 0 taken 1737 times.
✗ Branch 1 not taken.
1737 it[1] != '\r')
1093 {
1094
1/2
✓ Branch 0 taken 1737 times.
✗ Branch 1 not taken.
1737 if(it[1] == '\n')
1095 1737 n++;
1096 1737 it += 2;
1097 1737 len -= 2;
1098 }
1099 else
1100 {
1101 16658 it++;
1102 16658 len--;
1103 }
1104 }
1105 546 return n;
1106 }
1107
1108 static
1109 void
1110 14379 parse_start_line(
1111 header& h,
1112 header_limits const& lim,
1113 std::size_t new_size,
1114 system::error_code& ec) noexcept
1115 {
1116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14379 times.
14379 BOOST_ASSERT(h.size == 0);
1117
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14379 times.
14379 BOOST_ASSERT(h.prefix == 0);
1118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14379 times.
14379 BOOST_ASSERT(h.cbuf != nullptr);
1119
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14379 times.
14379 BOOST_ASSERT(
1120 h.kind != detail::kind::fields);
1121
1122 14379 auto const it0 = h.cbuf;
1123 14379 auto const end = it0 + new_size;
1124 14379 char const* it = it0;
1125
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 14343 times.
14379 if( new_size > lim.max_start_line)
1126 36 new_size = lim.max_start_line;
1127
2/2
✓ Branch 0 taken 12432 times.
✓ Branch 1 taken 1947 times.
14379 if(h.kind == detail::kind::request)
1128 {
1129 auto rv = grammar::parse(
1130 12432 it, end, request_line_rule);
1131
2/2
✓ Branch 1 taken 2693 times.
✓ Branch 2 taken 9739 times.
12432 if(! rv)
1132 {
1133 2693 ec = rv.error();
1134
2/4
✓ Branch 2 taken 2693 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 2693 times.
5386 if( ec == grammar::error::need_more &&
1135
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2693 times.
2693 new_size == lim.max_start_line)
1136 ec = BOOST_HTTP_PROTO_ERR(
1137 error::start_line_limit);
1138 2693 return;
1139 }
1140 // method
1141 9739 auto sm = std::get<0>(*rv);
1142 9739 h.req.method = string_to_method(sm);
1143 9739 h.req.method_len =
1144 9739 static_cast<header::offset_type>(sm.size());
1145 // target
1146 9739 auto st = std::get<1>(*rv);
1147 9739 h.req.target_len =
1148 9739 static_cast<header::offset_type>(st.size());
1149 // version
1150
2/3
✓ Branch 2 taken 25 times.
✓ Branch 3 taken 9714 times.
✗ Branch 4 not taken.
9739 switch(std::get<2>(*rv))
1151 {
1152 25 case 10:
1153 25 h.version =
1154 http_proto::version::http_1_0;
1155 25 break;
1156 9714 case 11:
1157 9714 h.version =
1158 http_proto::version::http_1_1;
1159 9714 break;
1160 default:
1161 {
1162 ec = BOOST_HTTP_PROTO_ERR(
1163 error::bad_version);
1164 return;
1165 }
1166 }
1167 }
1168 else
1169 {
1170 auto rv = grammar::parse(
1171 1947 it, end, status_line_rule);
1172
2/2
✓ Branch 1 taken 1112 times.
✓ Branch 2 taken 835 times.
1947 if(! rv)
1173 {
1174 1112 ec = rv.error();
1175
2/4
✓ Branch 2 taken 1112 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 1112 times.
2224 if( ec == grammar::error::need_more &&
1176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1112 times.
1112 new_size == lim.max_start_line)
1177 ec = BOOST_HTTP_PROTO_ERR(
1178 error::start_line_limit);
1179 1112 return;
1180 }
1181 // version
1182
2/3
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 831 times.
✗ Branch 4 not taken.
835 switch(std::get<0>(*rv))
1183 {
1184 4 case 10:
1185 4 h.version =
1186 http_proto::version::http_1_0;
1187 4 break;
1188 831 case 11:
1189 831 h.version =
1190 http_proto::version::http_1_1;
1191 831 break;
1192 default:
1193 {
1194 ec = BOOST_HTTP_PROTO_ERR(
1195 error::bad_version);
1196 return;
1197 }
1198 }
1199 // status-code
1200 835 h.res.status_int =
1201 static_cast<unsigned short>(
1202 835 std::get<1>(*rv).v);
1203 835 h.res.status = std::get<1>(*rv).st;
1204 }
1205 10574 h.prefix = static_cast<header::offset_type>(it - it0);
1206 10574 h.size = h.prefix;
1207 10574 h.on_start_line();
1208 }
1209
1210 // returns: true if we added a field
1211 static
1212 void
1213 24856 parse_field(
1214 header& h,
1215 header_limits const& lim,
1216 std::size_t new_size,
1217 system::error_code& ec) noexcept
1218 {
1219
2/2
✓ Branch 0 taken 96 times.
✓ Branch 1 taken 24760 times.
24856 if( new_size > lim.max_field)
1220 96 new_size = lim.max_field;
1221 24856 auto const it0 = h.cbuf + h.size;
1222 24856 auto const end = h.cbuf + new_size;
1223 24856 char const* it = it0;
1224 24856 auto rv = grammar::parse(
1225 it, end, field_rule);
1226
2/2
✓ Branch 1 taken 13388 times.
✓ Branch 2 taken 11468 times.
24856 if(rv.has_error())
1227 {
1228 13388 ec = rv.error();
1229
2/2
✓ Branch 2 taken 10549 times.
✓ Branch 3 taken 2839 times.
13388 if(ec == grammar::error::end_of_range)
1230 {
1231 // final CRLF
1232 10549 h.size = static_cast<
1233 10549 header::offset_type>(it - h.cbuf);
1234 13388 return;
1235 }
1236
3/4
✓ Branch 2 taken 2580 times.
✓ Branch 3 taken 259 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 2839 times.
5419 if( ec == grammar::error::need_more &&
1237
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2580 times.
2580 new_size == lim.max_field)
1238 {
1239 ec = BOOST_HTTP_PROTO_ERR(
1240 error::field_size_limit);
1241 }
1242 2839 return;
1243 }
1244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11468 times.
11468 if(h.count >= lim.max_fields)
1245 {
1246 ec = BOOST_HTTP_PROTO_ERR(
1247 error::fields_limit);
1248 return;
1249 }
1250
2/2
✓ Branch 1 taken 210 times.
✓ Branch 2 taken 11258 times.
11468 if(rv->has_obs_fold)
1251 {
1252 // obs fold not allowed in test views
1253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
210 BOOST_ASSERT(h.buf != nullptr);
1254 210 remove_obs_fold(h.buf + h.size, it);
1255 }
1256 11468 auto id = string_to_field(rv->name)
1257 11468 .value_or(header::unknown_field);
1258 11468 h.size = static_cast<header::offset_type>(it - h.cbuf);
1259
1260 // add field table entry
1261
1/2
✓ Branch 0 taken 11468 times.
✗ Branch 1 not taken.
11468 if(h.buf != nullptr)
1262 {
1263 22936 auto& e = header::table(
1264 11468 h.buf + h.cap)[h.count];
1265 11468 auto const base =
1266 11468 h.buf + h.prefix;
1267 11468 e.np = static_cast<header::offset_type>(
1268 11468 rv->name.data() - base);
1269 11468 e.nn = static_cast<header::offset_type>(
1270 11468 rv->name.size());
1271 11468 e.vp = static_cast<header::offset_type>(
1272 11468 rv->value.data() - base);
1273 11468 e.vn = static_cast<header::offset_type>(
1274 11468 rv->value.size());
1275 11468 e.id = id;
1276 }
1277 11468 ++h.count;
1278 11468 h.on_insert(id, rv->value);
1279 11468 ec = {};
1280 }
1281
1282 void
1283 17193 header::
1284 parse(
1285 std::size_t new_size,
1286 header_limits const& lim,
1287 system::error_code& ec) noexcept
1288 {
1289
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 17157 times.
17193 if( new_size > lim.max_size)
1290 36 new_size = lim.max_size;
1291
2/2
✓ Branch 0 taken 2580 times.
✓ Branch 1 taken 14613 times.
17193 if( this->prefix == 0 &&
1292
2/2
✓ Branch 0 taken 234 times.
✓ Branch 1 taken 14379 times.
14613 this->kind !=
1293 detail::kind::fields)
1294 {
1295 14379 parse_start_line(
1296 *this, lim, new_size, ec);
1297
2/2
✓ Branch 1 taken 10574 times.
✓ Branch 2 taken 3805 times.
14379 if(ec.failed())
1298 {
1299
2/4
✓ Branch 2 taken 3805 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 3805 times.
7610 if( ec == grammar::error::need_more &&
1300
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3805 times.
3805 new_size == lim.max_fields)
1301 {
1302 ec = BOOST_HTTP_PROTO_ERR(
1303 error::headers_limit);
1304 }
1305 3805 return;
1306 }
1307 }
1308 for(;;)
1309 {
1310 24856 parse_field(
1311 *this, lim, new_size, ec);
1312
2/2
✓ Branch 1 taken 13388 times.
✓ Branch 2 taken 11468 times.
24856 if(ec.failed())
1313 {
1314
3/4
✓ Branch 2 taken 2580 times.
✓ Branch 3 taken 10808 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13388 times.
15968 if( ec == grammar::error::need_more &&
1315
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2580 times.
2580 new_size == lim.max_size)
1316 {
1317 ec = BOOST_HTTP_PROTO_ERR(
1318 error::headers_limit);
1319 return;
1320 }
1321 13388 break;
1322 }
1323 11468 }
1324
2/2
✓ Branch 2 taken 10549 times.
✓ Branch 3 taken 2839 times.
13388 if(ec == grammar::error::end_of_range)
1325 10549 ec = {};
1326 }
1327
1328 } // detail
1329 } // http_proto
1330 } // boost
1331