GCC Code Coverage Report


Directory: libs/http_proto/
File: src/detail/header.cpp
Date: 2025-06-15 05:10:38
Exec Total Coverage
Lines: 586 630 93.0%
Functions: 49 59 83.1%
Branches: 283 344 82.3%

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