GCC Code Coverage Report


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