GCC Code Coverage Report


Directory: ./
File: libs/http_proto/src/parser.cpp
Date: 2025-12-18 06:34:37
Exec Total Coverage
Lines: 757 886 85.4%
Functions: 84 98 85.7%
Branches: 372 497 74.8%

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/except.hpp>
12 #include <boost/http_proto/error.hpp>
13 #include <boost/http_proto/parser.hpp>
14
15 #include <boost/assert.hpp>
16 #include <boost/buffers/circular_buffer.hpp>
17 #include <boost/buffers/copy.hpp>
18 #include <boost/buffers/flat_buffer.hpp>
19 #include <boost/buffers/front.hpp>
20 #include <boost/buffers/slice.hpp>
21 #include <boost/capy/brotli/decode.hpp>
22 #include <boost/capy/polystore.hpp>
23 #include <boost/capy/zlib/error.hpp>
24 #include <boost/capy/zlib/inflate.hpp>
25 #include <boost/url/grammar/ci_string.hpp>
26 #include <boost/url/grammar/error.hpp>
27 #include <boost/url/grammar/hexdig_chars.hpp>
28
29 #include "src/detail/brotli_filter_base.hpp"
30 #include "src/detail/buffer_utils.hpp"
31 #include "src/detail/zlib_filter_base.hpp"
32
33 namespace boost {
34 namespace http_proto {
35
36 /*
37 Principles for fixed-size buffer design
38
39 axiom 1:
40 To read data you must have a buffer.
41
42 axiom 2:
43 The size of the HTTP header is not
44 known in advance.
45
46 conclusion 3:
47 A single I/O can produce a complete
48 HTTP header and additional payload
49 data.
50
51 conclusion 4:
52 A single I/O can produce multiple
53 complete HTTP headers, complete
54 payloads, and a partial header or
55 payload.
56
57 axiom 5:
58 A process is in one of two states:
59 1. at or below capacity
60 2. above capacity
61
62 axiom 6:
63 A program which can allocate an
64 unbounded number of resources can
65 go above capacity.
66
67 conclusion 7:
68 A program can guarantee never going
69 above capacity if all resources are
70 provisioned at program startup.
71
72 corollary 8:
73 `parser` and `serializer` should each
74 allocate a single buffer of calculated
75 size, and never resize it.
76
77 axiom #:
78 A parser and a serializer are always
79 used in pairs.
80
81 Buffer Usage
82
83 | | begin
84 | H | p | | f | read headers
85 | H | p | | T | f | set T body
86 | H | p | | C | T | f | make codec C
87 | H | p | b | C | T | f | decode p into b
88 | H | p | b | C | T | f | read/parse loop
89 | H | | T | f | destroy codec
90 | H | | T | f | finished
91
92 H headers
93 C codec
94 T body
95 f table
96 p partial payload
97 b body data
98
99 "payload" is the bytes coming in from
100 the stream.
101
102 "body" is the logical body, after transfer
103 encoding is removed. This can be the
104 same as the payload.
105
106 A "plain payload" is when the payload and
107 body are identical (no transfer encodings).
108
109 A "buffered payload" is any payload which is
110 not plain. A second buffer is required
111 for reading.
112
113 "overread" is additional data received past
114 the end of the headers when reading headers,
115 or additional data received past the end of
116 the message payload.
117 */
118
119 namespace {
120
121 class chained_sequence
122 {
123 char const* pos_;
124 char const* end_;
125 char const* begin_b_;
126 char const* end_b_;
127
128 public:
129 120197 chained_sequence(buffers::const_buffer_pair const& cbp)
130 120197 : pos_(static_cast<char const*>(cbp[0].data()))
131 120197 , end_(pos_ + cbp[0].size())
132 120197 , begin_b_(static_cast<char const*>(cbp[1].data()))
133 120197 , end_b_(begin_b_ + cbp[1].size())
134 {
135 120197 }
136
137 char const*
138 618747 next() noexcept
139 {
140 618747 ++pos_;
141 // most frequently taken branch
142
2/2
✓ Branch 0 taken 597426 times.
✓ Branch 1 taken 21321 times.
618747 if(pos_ < end_)
143 597426 return pos_;
144
145 // bring the second range
146
2/2
✓ Branch 0 taken 38 times.
✓ Branch 1 taken 21283 times.
21321 if(begin_b_ != end_b_)
147 {
148 38 pos_ = begin_b_;
149 38 end_ = end_b_;
150 38 begin_b_ = end_b_;
151 38 return pos_;
152 }
153
154 // undo the increament
155 21283 pos_ = end_;
156 21283 return nullptr;
157 }
158
159 bool
160 410999 is_empty() const noexcept
161 {
162 410999 return pos_ == end_;
163 }
164
165 char
166 604240 value() const noexcept
167 {
168 604240 return *pos_;
169 }
170
171 std::size_t
172 424974 size() const noexcept
173 {
174 424974 return (end_ - pos_) + (end_b_ - begin_b_);
175 }
176 };
177
178 std::uint64_t
179 115957 parse_hex(
180 chained_sequence& cs,
181 system::error_code& ec) noexcept
182 {
183 115957 std::uint64_t v = 0;
184 115957 std::size_t init_size = cs.size();
185
2/2
✓ Branch 1 taken 283496 times.
✓ Branch 2 taken 19282 times.
302778 while(!cs.is_empty())
186 {
187 283496 auto n = grammar::hexdig_value(cs.value());
188
2/2
✓ Branch 0 taken 96674 times.
✓ Branch 1 taken 186822 times.
283496 if(n < 0)
189 {
190
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 96673 times.
96674 if(init_size == cs.size())
191 {
192 2 ec = BOOST_HTTP_PROTO_ERR(
193 error::bad_payload);
194 1 return 0;
195 }
196 96673 return v;
197 }
198
199 // at least 4 significant bits are free
200
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 186821 times.
186822 if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
201 {
202 2 ec = BOOST_HTTP_PROTO_ERR(
203 error::bad_payload);
204 1 return 0;
205 }
206
207 186821 v = (v << 4) | static_cast<std::uint64_t>(n);
208 186821 cs.next();
209 }
210 38564 ec = BOOST_HTTP_PROTO_ERR(
211 error::need_data);
212 19282 return 0;
213 }
214
215 void
216 97025 find_eol(
217 chained_sequence& cs,
218 system::error_code& ec) noexcept
219 {
220
2/2
✓ Branch 1 taken 103626 times.
✓ Branch 2 taken 88 times.
103714 while(!cs.is_empty())
221 {
222
2/2
✓ Branch 1 taken 96937 times.
✓ Branch 2 taken 6689 times.
103626 if(cs.value() == '\r')
223 {
224
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 96927 times.
96937 if(!cs.next())
225 10 break;
226
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 96925 times.
96927 if(cs.value() != '\n')
227 {
228 4 ec = BOOST_HTTP_PROTO_ERR(
229 error::bad_payload);
230 2 return;
231 }
232 96925 cs.next();
233 96925 return;
234 }
235 6689 cs.next();
236 }
237 196 ec = BOOST_HTTP_PROTO_ERR(
238 error::need_data);
239 }
240
241 void
242 111557 parse_eol(
243 chained_sequence& cs,
244 system::error_code& ec) noexcept
245 {
246
2/2
✓ Branch 1 taken 111543 times.
✓ Branch 2 taken 14 times.
111557 if(cs.size() >= 2)
247 {
248 // we are sure size is at least 2
249
6/6
✓ Branch 1 taken 111541 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 111540 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 111540 times.
✓ Branch 7 taken 3 times.
111543 if(cs.value() == '\r' && *cs.next() == '\n')
250 {
251 111540 cs.next();
252 111540 return;
253 }
254 6 ec = BOOST_HTTP_PROTO_ERR(
255 error::bad_payload);
256 3 return;
257 }
258 28 ec = BOOST_HTTP_PROTO_ERR(
259 error::need_data);
260 }
261
262 void
263 4223 skip_trailer_headers(
264 chained_sequence& cs,
265 system::error_code& ec) noexcept
266 {
267
2/2
✓ Branch 1 taken 4501 times.
✓ Branch 2 taken 6 times.
4507 while(!cs.is_empty())
268 {
269
2/2
✓ Branch 1 taken 4149 times.
✓ Branch 2 taken 352 times.
4501 if(cs.value() == '\r')
270 {
271
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4147 times.
4149 if(!cs.next())
272 2 break;
273
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4145 times.
4147 if(cs.value() != '\n')
274 {
275 4 ec = BOOST_HTTP_PROTO_ERR(
276 error::bad_payload);
277 2 return;
278 }
279 4145 cs.next();
280 4145 return;
281 }
282 // skip to the end of field
283 352 find_eol(cs, ec);
284
2/2
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 284 times.
352 if(ec)
285 68 return;
286 }
287 16 ec = BOOST_HTTP_PROTO_ERR(
288 error::need_data);
289 }
290
291 template<class UInt>
292 std::size_t
293 360930 clamp(
294 UInt x,
295 std::size_t limit = (std::numeric_limits<
296 std::size_t>::max)()) noexcept
297 {
298
2/2
✓ Branch 0 taken 101553 times.
✓ Branch 1 taken 259377 times.
360930 if(x >= limit)
299 101553 return limit;
300 259377 return static_cast<std::size_t>(x);
301 }
302
303 class zlib_filter
304 : public detail::zlib_filter_base
305 {
306 capy::zlib::inflate_service& svc_;
307
308 public:
309 72 zlib_filter(
310 const capy::polystore& ctx,
311 http_proto::detail::workspace& ws,
312 int window_bits)
313 72 : zlib_filter_base(ws)
314 72 , svc_(ctx.get<capy::zlib::inflate_service>())
315 {
316 system::error_code ec = static_cast<capy::zlib::error>(
317
1/1
✓ Branch 1 taken 72 times.
72 svc_.init2(strm_, window_bits));
318
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 72 times.
72 if(ec != capy::zlib::error::ok)
319 detail::throw_system_error(ec);
320 72 }
321
322 private:
323 virtual
324 results
325 56581 do_process(
326 buffers::mutable_buffer out,
327 buffers::const_buffer in,
328 bool more) noexcept override
329 {
330 56581 strm_.next_out = static_cast<unsigned char*>(out.data());
331 56581 strm_.avail_out = saturate_cast(out.size());
332 56581 strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
333 56581 strm_.avail_in = saturate_cast(in.size());
334
335 auto rs = static_cast<capy::zlib::error>(
336
2/2
✓ Branch 0 taken 56489 times.
✓ Branch 1 taken 92 times.
56581 svc_.inflate(
337 56581 strm_,
338 more ? capy::zlib::no_flush : capy::zlib::finish));
339
340 56581 results rv;
341 56581 rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
342 56581 rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
343 56581 rv.finished = (rs == capy::zlib::error::stream_end);
344
345
3/4
✓ Branch 0 taken 1636 times.
✓ Branch 1 taken 54945 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1636 times.
56581 if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err)
346 rv.ec = rs;
347
348 56581 return rv;
349 }
350 };
351
352 class brotli_filter
353 : public detail::brotli_filter_base
354 {
355 capy::brotli::decode_service& svc_;
356 capy::brotli::decoder_state* state_;
357
358 public:
359 brotli_filter(
360 const capy::polystore& ctx,
361 http_proto::detail::workspace&)
362 : svc_(ctx.get<capy::brotli::decode_service>())
363 {
364 // TODO: use custom allocator
365 state_ = svc_.create_instance(nullptr, nullptr, nullptr);
366
367 if(!state_)
368 detail::throw_bad_alloc();
369 }
370
371 ~brotli_filter()
372 {
373 svc_.destroy_instance(state_);
374 }
375
376 private:
377 virtual
378 results
379 do_process(
380 buffers::mutable_buffer out,
381 buffers::const_buffer in,
382 bool more) noexcept override
383 {
384 auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
385 auto available_in = in.size();
386 auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
387 auto available_out = out.size();
388
389 auto rs = svc_.decompress_stream(
390 state_,
391 &available_in,
392 &next_in,
393 &available_out,
394 &next_out,
395 nullptr);
396
397 results rv;
398 rv.in_bytes = in.size() - available_in;
399 rv.out_bytes = out.size() - available_out;
400 rv.finished = svc_.is_finished(state_);
401
402 if(!more && rs == capy::brotli::decoder_result::needs_more_input)
403 rv.ec = BOOST_HTTP_PROTO_ERR(error::bad_payload);
404
405 if(rs == capy::brotli::decoder_result::error)
406 rv.ec = BOOST_HTTP_PROTO_ERR(
407 svc_.get_error_code(state_));
408
409 return rv;
410 }
411 };
412
413 class parser_service
414 {
415 public:
416 parser::config_base cfg;
417 std::size_t space_needed = 0;
418 std::size_t max_codec = 0;
419
420 43 parser_service(
421 parser::config_base const& cfg_)
422 43 : cfg(cfg_)
423 {
424 /*
425 | fb | cb0 | cb1 | C | T | f |
426
427 fb flat_buffer headers.max_size
428 cb0 circular_buffer min_buffer
429 cb1 circular_buffer min_buffer
430 C codec max_codec
431 T body max_type_erase
432 f table max_table_space
433
434 */
435 // validate
436 //if(cfg.min_prepare > cfg.max_prepare)
437 //detail::throw_invalid_argument();
438
439
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 if(cfg.max_prepare < 1)
440 detail::throw_invalid_argument();
441
442 // VFALCO TODO OVERFLOW CHECING
443 {
444 //fb_.size() - h_.size +
445 //svc_.cfg.min_buffer +
446 //svc_.cfg.min_buffer +
447 //svc_.max_codec;
448 }
449
450 // VFALCO OVERFLOW CHECKING ON THIS
451 43 space_needed +=
452 43 cfg.headers.valid_space_needed();
453
454 // cb0_, cb1_
455 // VFALCO OVERFLOW CHECKING ON THIS
456 43 space_needed +=
457 43 cfg.min_buffer +
458 cfg.min_buffer;
459
460 // T
461 43 space_needed += cfg.max_type_erase;
462
463 // max_codec
464
3/4
✓ Branch 0 taken 42 times.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 42 times.
43 if(cfg.apply_deflate_decoder || cfg.apply_gzip_decoder)
465 {
466 // TODO: Account for the number of allocations and
467 // their overhead in the workspace.
468
469 // https://www.zlib.net/zlib_tech.html
470 std::size_t n =
471 1 (1 << cfg.zlib_window_bits) +
472 (7 * 1024) +
473 #ifdef __s390x__
474 5768 +
475 #endif
476 detail::workspace::space_needed<
477 1 zlib_filter>();
478
479
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(max_codec < n)
480 1 max_codec = n;
481 }
482 43 space_needed += max_codec;
483
484 // round up to alignof(detail::header::entry)
485 43 auto const al = alignof(
486 detail::header::entry);
487 43 space_needed = al * ((
488 43 space_needed + al - 1) / al);
489 43 }
490
491 std::size_t
492 55255 max_overread() const noexcept
493 {
494 return
495 55255 cfg.headers.max_size +
496 55255 cfg.min_buffer;
497 }
498 };
499
500 } // namespace
501
502 //------------------------------------------------
503
504 void
505 43 install_parser_service(
506 capy::polystore& ctx,
507 parser::config_base const& cfg)
508 {
509 43 ctx.emplace<parser_service>(cfg);
510 43 }
511
512 //------------------------------------------------
513
514 class parser::impl
515 {
516 enum class state
517 {
518 reset,
519 start,
520 header,
521 header_done,
522 body,
523 set_body,
524 complete_in_place,
525 complete
526 };
527
528 enum class style
529 {
530 in_place,
531 sink,
532 elastic,
533 };
534
535 const capy::polystore& ctx_;
536 parser_service& svc_;
537
538 detail::workspace ws_;
539 detail::header h_;
540 std::uint64_t body_limit_;
541 std::uint64_t body_total_;
542 std::uint64_t payload_remain_;
543 std::uint64_t chunk_remain_;
544 std::size_t body_avail_;
545 std::size_t nprepare_;
546
547 buffers::flat_buffer fb_;
548 buffers::circular_buffer cb0_;
549 buffers::circular_buffer cb1_;
550
551 buffers::mutable_buffer_pair mbp_;
552 buffers::const_buffer_pair cbp_;
553
554 detail::filter* filter_;
555 buffers::any_dynamic_buffer* eb_;
556 sink* sink_;
557
558 state state_;
559 style style_;
560 bool got_header_;
561 bool got_eof_;
562 bool head_response_;
563 bool needs_chunk_close_;
564 bool trailer_headers_;
565 bool chunked_body_ended;
566
567 public:
568 1056 impl(const capy::polystore& ctx, detail::kind k)
569 1056 : ctx_(ctx)
570 1056 , svc_(ctx.get<parser_service>())
571 1056 , ws_(svc_.space_needed)
572 1056 , h_(detail::empty{ k })
573 1056 , state_(state::reset)
574 1056 , got_header_(false)
575 {
576 1056 }
577
578 bool
579 11938 got_header() const noexcept
580 {
581 11938 return got_header_;
582 }
583
584 bool
585 51344 is_complete() const noexcept
586 {
587 51344 return state_ >= state::complete_in_place;
588 }
589
590 detail::header const&
591 316 safe_get_header() const
592 {
593 // headers must be received
594
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 if(! got_header_)
595 detail::throw_logic_error();
596
597 316 return h_;
598 }
599
600 bool
601 755 is_body_set() const noexcept
602 {
603 755 return style_ != style::in_place;
604 }
605
606 void
607 2480 reset() noexcept
608 {
609 2480 ws_.clear();
610 2480 state_ = state::start;
611 2480 got_header_ = false;
612 2480 got_eof_ = false;
613 2480 }
614
615 void
616 10307 start(
617 bool head_response)
618 {
619 10307 std::size_t leftover = 0;
620
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2255 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8015 times.
✓ Branch 5 taken 32 times.
10307 switch(state_)
621 {
622 1 default:
623 case state::reset:
624 // reset must be called first
625 1 detail::throw_logic_error();
626
627 2255 case state::start:
628 // reset required on eof
629
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2255 times.
2255 if(got_eof_)
630 detail::throw_logic_error();
631 2255 break;
632
633 3 case state::header:
634
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(fb_.size() == 0)
635 {
636 // start() called twice
637 2 detail::throw_logic_error();
638 }
639 BOOST_FALLTHROUGH;
640
641 case state::header_done:
642 case state::body:
643 case state::set_body:
644 // current message is incomplete
645 2 detail::throw_logic_error();
646
647 8015 case state::complete_in_place:
648 // remove available body.
649
2/2
✓ Branch 1 taken 4000 times.
✓ Branch 2 taken 4015 times.
8015 if(is_plain())
650 4000 cb0_.consume(body_avail_);
651 BOOST_FALLTHROUGH;
652
653 case state::complete:
654 {
655 // move leftovers to front
656
657 8047 ws_.clear();
658 8047 leftover = cb0_.size();
659
660 8047 auto* dest = reinterpret_cast<char*>(ws_.data());
661 8047 auto cbp = cb0_.data();
662 8047 auto* a = static_cast<char const*>(cbp[0].data());
663 8047 auto* b = static_cast<char const*>(cbp[1].data());
664 8047 auto an = cbp[0].size();
665 8047 auto bn = cbp[1].size();
666
667
2/2
✓ Branch 0 taken 7609 times.
✓ Branch 1 taken 438 times.
8047 if(bn == 0)
668 {
669 7609 std::memmove(dest, a, an);
670 }
671 else
672 {
673 // if `a` can fit between `dest` and `b`, shift `b` to the left
674 // and copy `a` to its position. if `a` fits perfectly, the
675 // shift will be of size 0.
676 // if `a` requires more space, shift `b` to the right and
677 // copy `a` to its position. this process may require multiple
678 // iterations and should be done chunk by chunk to prevent `b`
679 // from overlapping with `a`.
680 do
681 {
682 // clamp right shifts to prevent overlap with `a`
683 438 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
684 438 b = static_cast<char const*>(std::memmove(bp, b, bn));
685
686 // a chunk or all of `a` based on available space
687 438 auto chunk_a = static_cast<std::size_t>(b - dest);
688 438 std::memcpy(dest, a, chunk_a); // never overlap
689 438 an -= chunk_a;
690 438 dest += chunk_a;
691 438 a += chunk_a;
692
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 } while(an);
693 }
694
695 8047 break;
696 }
697 }
698
699 10302 ws_.clear();
700
701 20604 fb_ = {
702 10302 ws_.data(),
703
1/1
✓ Branch 1 taken 10302 times.
10302 svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
704 leftover };
705
706
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10302 times.
10302 BOOST_ASSERT(
707 fb_.capacity() == svc_.max_overread() - leftover);
708
709
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10302 BOOST_ASSERT(
710 head_response == false ||
711 h_.kind == detail::kind::response);
712
713 10302 h_ = detail::header(detail::empty{h_.kind});
714 10302 h_.buf = reinterpret_cast<char*>(ws_.data());
715 10302 h_.cbuf = h_.buf;
716 10302 h_.cap = ws_.size();
717
718 10302 state_ = state::header;
719 10302 style_ = style::in_place;
720
721 // reset to the configured default
722 10302 body_limit_ = svc_.cfg.body_limit;
723
724 10302 body_total_ = 0;
725 10302 payload_remain_ = 0;
726 10302 chunk_remain_ = 0;
727 10302 body_avail_ = 0;
728 10302 nprepare_ = 0;
729
730 10302 filter_ = nullptr;
731 10302 eb_ = nullptr;
732 10302 sink_ = nullptr;
733
734 10302 got_header_ = false;
735 10302 head_response_ = head_response;
736 10302 needs_chunk_close_ = false;
737 10302 trailer_headers_ = false;
738 10302 chunked_body_ended = false;
739 10302 }
740
741 auto
742 51003 prepare() ->
743 mutable_buffers_type
744 {
745 51003 nprepare_ = 0;
746
747
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40626 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
51003 switch(state_)
748 {
749 1 default:
750 case state::reset:
751 // reset must be called first
752 1 detail::throw_logic_error();
753
754 1 case state::start:
755 // start must be called first
756 1 detail::throw_logic_error();
757
758 10374 case state::header:
759 {
760
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10374 times.
10374 BOOST_ASSERT(
761 h_.size < svc_.cfg.headers.max_size);
762 10374 std::size_t n = fb_.capacity();
763
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10374 times.
10374 BOOST_ASSERT(n <= svc_.max_overread());
764 10374 n = clamp(n, svc_.cfg.max_prepare);
765 10374 mbp_[0] = fb_.prepare(n);
766 10374 nprepare_ = n;
767 10374 return mutable_buffers_type(&mbp_[0], 1);
768 }
769
770 case state::header_done:
771 // forgot to call parse()
772 detail::throw_logic_error();
773
774 40626 case state::body:
775 {
776
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40626 times.
40626 if(got_eof_)
777 {
778 // forgot to call parse()
779 detail::throw_logic_error();
780 }
781
782
2/2
✓ Branch 1 taken 21575 times.
✓ Branch 2 taken 19051 times.
40626 if(! is_plain())
783 {
784 // buffered payload
785 21575 std::size_t n = cb0_.capacity();
786 21575 n = clamp(n, svc_.cfg.max_prepare);
787 21575 nprepare_ = n;
788 21575 mbp_ = cb0_.prepare(n);
789 21575 return detail::make_span(mbp_);
790 }
791 else
792 {
793
2/2
✓ Branch 0 taken 19030 times.
✓ Branch 1 taken 21 times.
19051 switch(style_)
794 {
795 19030 default:
796 case style::in_place:
797 case style::sink:
798 {
799 19030 std::size_t n = cb0_.capacity();
800 19030 n = clamp(n, svc_.cfg.max_prepare);
801
802
2/2
✓ Branch 0 taken 19005 times.
✓ Branch 1 taken 25 times.
19030 if(h_.md.payload == payload::size)
803 {
804
2/2
✓ Branch 0 taken 17798 times.
✓ Branch 1 taken 1207 times.
19005 if(n > payload_remain_)
805 {
806 17798 std::size_t overread =
807 17798 n - static_cast<std::size_t>(payload_remain_);
808
2/2
✓ Branch 1 taken 7878 times.
✓ Branch 2 taken 9920 times.
17798 if(overread > svc_.max_overread())
809 7878 n = static_cast<std::size_t>(payload_remain_) +
810 7878 svc_.max_overread();
811 }
812 }
813 else
814 {
815
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 BOOST_ASSERT(
816 h_.md.payload == payload::to_eof);
817 // No more messages can be pipelined, so
818 // limit the output buffer to the remaining
819 // body limit plus one byte to detect
820 // exhaustion.
821 25 std::uint64_t r = body_limit_remain();
822
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 if(r != std::uint64_t(-1))
823 25 r += 1;
824 25 n = clamp(r, n);
825 }
826
827 19030 nprepare_ = n;
828 19030 mbp_ = cb0_.prepare(n);
829 19030 return detail::make_span(mbp_);
830 }
831 21 case style::elastic:
832 {
833
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
21 BOOST_ASSERT(cb0_.size() == 0);
834
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(body_avail_ == 0);
835
836 21 std::size_t n = svc_.cfg.min_buffer;
837
838
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
21 if(h_.md.payload == payload::size)
839 {
840 // Overreads are not allowed, or
841 // else the caller will see extra
842 // unrelated data.
843 6 n = clamp(payload_remain_, n);
844 }
845 else
846 {
847
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 BOOST_ASSERT(
848 h_.md.payload == payload::to_eof);
849 // No more messages can be pipelined, so
850 // limit the output buffer to the remaining
851 // body limit plus one byte to detect
852 // exhaustion.
853 15 std::uint64_t r = body_limit_remain();
854
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if(r != std::uint64_t(-1))
855 15 r += 1;
856 15 n = clamp(r, n);
857 15 n = clamp(n, eb_->max_size() - eb_->size());
858 // fill capacity first to avoid an allocation
859 std::size_t avail =
860 15 eb_->capacity() - eb_->size();
861
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if(avail != 0)
862 15 n = clamp(n, avail);
863
864
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if(n == 0)
865 {
866 // dynamic buffer is full
867 // attempt a 1 byte read so
868 // we can detect overflow
869 1 nprepare_ = 1;
870 1 mbp_ = cb0_.prepare(1);
871 1 return detail::make_span(mbp_);
872 }
873 }
874
875 20 n = clamp(n, svc_.cfg.max_prepare);
876
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 BOOST_ASSERT(n != 0);
877 20 nprepare_ = n;
878 20 return eb_->prepare(n);
879 }
880 }
881 }
882 }
883
884 case state::set_body:
885 // forgot to call parse()
886 detail::throw_logic_error();
887
888 1 case state::complete_in_place:
889 case state::complete:
890 // already complete
891 1 detail::throw_logic_error();
892 }
893 }
894
895 void
896 51000 commit(
897 std::size_t n)
898 {
899
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40623 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
51000 switch(state_)
900 {
901 1 default:
902 case state::reset:
903 {
904 // reset must be called first
905 1 detail::throw_logic_error();
906 }
907
908 1 case state::start:
909 {
910 // forgot to call start()
911 1 detail::throw_logic_error();
912 }
913
914 10374 case state::header:
915 {
916
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10373 times.
10374 if(n > nprepare_)
917 {
918 // n can't be greater than size of
919 // the buffers returned by prepare()
920 1 detail::throw_invalid_argument();
921 }
922
923
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10372 times.
10373 if(got_eof_)
924 {
925 // can't commit after EOF
926 1 detail::throw_logic_error();
927 }
928
929 10372 nprepare_ = 0; // invalidate
930 10372 fb_.commit(n);
931 10372 break;
932 }
933
934 case state::header_done:
935 {
936 // forgot to call parse()
937 detail::throw_logic_error();
938 }
939
940 40623 case state::body:
941 {
942
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40621 times.
40623 if(n > nprepare_)
943 {
944 // n can't be greater than size of
945 // the buffers returned by prepare()
946 2 detail::throw_invalid_argument();
947 }
948
949
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40621 times.
40621 if(got_eof_)
950 {
951 // can't commit after EOF
952 detail::throw_logic_error();
953 }
954
955 40621 nprepare_ = 0; // invalidate
956
6/6
✓ Branch 1 taken 19046 times.
✓ Branch 2 taken 21575 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 19026 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 40601 times.
40621 if(is_plain() && style_ == style::elastic)
957 {
958
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
20 if(eb_->max_size() == eb_->size())
959 {
960 // borrowed 1 byte from
961 // cb0_ in prepare()
962
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
963 1 cb0_.commit(n);
964 }
965 else
966 {
967 19 eb_->commit(n);
968 19 payload_remain_ -= n;
969 19 body_total_ += n;
970 }
971 }
972 else
973 {
974 40601 cb0_.commit(n);
975 }
976 40621 break;
977 }
978
979 case state::set_body:
980 {
981 // forgot to call parse()
982 detail::throw_logic_error();
983 }
984
985 1 case state::complete_in_place:
986 case state::complete:
987 {
988 // already complete
989 1 detail::throw_logic_error();
990 }
991 }
992 50993 }
993
994 void
995 401 commit_eof()
996 {
997 401 nprepare_ = 0; // invalidate
998
999
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 30 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 368 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
401 switch(state_)
1000 {
1001 1 default:
1002 case state::reset:
1003 // reset must be called first
1004 1 detail::throw_logic_error();
1005
1006 1 case state::start:
1007 // forgot to call start()
1008 1 detail::throw_logic_error();
1009
1010 30 case state::header:
1011 30 got_eof_ = true;
1012 30 break;
1013
1014 case state::header_done:
1015 // forgot to call parse()
1016 detail::throw_logic_error();
1017
1018 368 case state::body:
1019 368 got_eof_ = true;
1020 368 break;
1021
1022 case state::set_body:
1023 // forgot to call parse()
1024 detail::throw_logic_error();
1025
1026 1 case state::complete_in_place:
1027 case state::complete:
1028 // can't commit eof when complete
1029 1 detail::throw_logic_error();
1030 }
1031 398 }
1032
1033 void
1034 69827 parse(
1035 system::error_code& ec)
1036 {
1037 69827 ec = {};
1038
7/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 16650 times.
✓ Branch 3 taken 9083 times.
✓ Branch 4 taken 41299 times.
✓ Branch 5 taken 2333 times.
✓ Branch 6 taken 460 times.
69827 switch(state_)
1039 {
1040 1 default:
1041 case state::reset:
1042 // reset must be called first
1043 1 detail::throw_logic_error();
1044
1045 1 case state::start:
1046 // start must be called first
1047 1 detail::throw_logic_error();
1048
1049 16650 case state::header:
1050 {
1051
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16650 times.
16650 BOOST_ASSERT(h_.buf == static_cast<
1052 void const*>(ws_.data()));
1053
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16650 times.
16650 BOOST_ASSERT(h_.cbuf == static_cast<
1054 void const*>(ws_.data()));
1055
1056 16650 h_.parse(fb_.size(), svc_.cfg.headers, ec);
1057
1058
2/2
✓ Branch 2 taken 6385 times.
✓ Branch 3 taken 10265 times.
16650 if(ec == condition::need_more_input)
1059 {
1060
2/2
✓ Branch 0 taken 6358 times.
✓ Branch 1 taken 27 times.
6385 if(! got_eof_)
1061 {
1062 // headers incomplete
1063 6358 return;
1064 }
1065
1066
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 15 times.
27 if(fb_.size() == 0)
1067 {
1068 // stream closed cleanly
1069 12 state_ = state::reset;
1070 24 ec = BOOST_HTTP_PROTO_ERR(
1071 error::end_of_stream);
1072 12 return;
1073 }
1074
1075 // stream closed with a
1076 // partial message received
1077 15 state_ = state::reset;
1078 30 ec = BOOST_HTTP_PROTO_ERR(
1079 error::incomplete);
1080 15 return;
1081 }
1082
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 10006 times.
10265 else if(ec.failed())
1083 {
1084 // other error,
1085 //
1086 // VFALCO map this to a bad
1087 // request or bad response error?
1088 //
1089 259 state_ = state::reset; // unrecoverable
1090 259 return;
1091 }
1092
1093 10006 got_header_ = true;
1094
1095 // reserve headers + table
1096 10006 ws_.reserve_front(h_.size);
1097 10006 ws_.reserve_back(h_.table_space());
1098
1099 // no payload
1100
2/2
✓ Branch 0 taken 9084 times.
✓ Branch 1 taken 922 times.
10006 if(h_.md.payload == payload::none ||
1101
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9084 times.
9084 head_response_)
1102 {
1103 // octets of the next message
1104 922 auto overread = fb_.size() - h_.size;
1105
1/1
✓ Branch 2 taken 922 times.
922 cb0_ = { ws_.data(), overread, overread };
1106 922 ws_.reserve_front(overread);
1107 922 state_ = state::complete_in_place;
1108 922 return;
1109 }
1110
1111 9084 state_ = state::header_done;
1112 9084 break;
1113 }
1114
1115 9083 case state::header_done:
1116 {
1117 // metadata error
1118
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 8903 times.
9083 if(h_.md.payload == payload::error)
1119 {
1120 // VFALCO This needs looking at
1121 360 ec = BOOST_HTTP_PROTO_ERR(
1122 error::bad_payload);
1123 180 state_ = state::reset; // unrecoverable
1124 180 return;
1125 }
1126
1127 // overread currently includes any and all octets that
1128 // extend beyond the current end of the header
1129 // this can include associated body octets for the
1130 // current message or octets of the next message in the
1131 // stream, e.g. pipelining is being used
1132 8903 auto const overread = fb_.size() - h_.size;
1133
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8903 times.
8903 BOOST_ASSERT(overread <= svc_.max_overread());
1134
1135 8903 auto cap = fb_.capacity() + overread +
1136 8903 svc_.cfg.min_buffer;
1137
1138 // reserve body buffers first, as the decoder
1139 // must be installed after them.
1140 8903 auto const p = ws_.reserve_front(cap);
1141
1142
3/4
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 8831 times.
8903 switch(h_.md.content_encoding.coding)
1143 {
1144 36 case content_coding::deflate:
1145
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if(!svc_.cfg.apply_deflate_decoder)
1146 goto no_filter;
1147 72 filter_ = &ws_.emplace<zlib_filter>(
1148 36 ctx_, ws_, svc_.cfg.zlib_window_bits);
1149 36 break;
1150
1151 36 case content_coding::gzip:
1152
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if(!svc_.cfg.apply_gzip_decoder)
1153 goto no_filter;
1154 72 filter_ = &ws_.emplace<zlib_filter>(
1155
1/1
✓ Branch 1 taken 36 times.
36 ctx_, ws_, svc_.cfg.zlib_window_bits + 16);
1156 36 break;
1157
1158 case content_coding::br:
1159 if(!svc_.cfg.apply_brotli_decoder)
1160 goto no_filter;
1161 filter_ = &ws_.emplace<brotli_filter>(
1162 ctx_, ws_);
1163 break;
1164
1165 no_filter:
1166 8831 default:
1167 8831 cap += svc_.max_codec;
1168 8831 ws_.reserve_front(svc_.max_codec);
1169 8831 break;
1170 }
1171
1172
6/6
✓ Branch 1 taken 4205 times.
✓ Branch 2 taken 4698 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 4181 times.
✓ Branch 5 taken 4722 times.
✓ Branch 6 taken 4181 times.
8903 if(is_plain() || style_ == style::elastic)
1173 {
1174
1/1
✓ Branch 1 taken 4722 times.
4722 cb0_ = { p, cap, overread };
1175 4722 cb1_ = {};
1176 }
1177 else
1178 {
1179 // buffered payload
1180 8362 std::size_t n0 = (overread > svc_.cfg.min_buffer)
1181
2/2
✓ Branch 0 taken 4157 times.
✓ Branch 1 taken 24 times.
4181 ? overread
1182 4157 : svc_.cfg.min_buffer;
1183 4181 std::size_t n1 = svc_.cfg.min_buffer;
1184
1185
1/1
✓ Branch 1 taken 4181 times.
4181 cb0_ = { p , n0, overread };
1186 4181 cb1_ = { p + n0 , n1 };
1187 }
1188
1189
2/2
✓ Branch 0 taken 4374 times.
✓ Branch 1 taken 4529 times.
8903 if(h_.md.payload == payload::size)
1190 {
1191
2/2
✓ Branch 0 taken 4350 times.
✓ Branch 1 taken 24 times.
4374 if(!filter_ &&
1192
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4349 times.
4350 body_limit_ < h_.md.payload_size)
1193 {
1194 2 ec = BOOST_HTTP_PROTO_ERR(
1195 error::body_too_large);
1196 1 state_ = state::reset;
1197 1 return;
1198 }
1199 4373 payload_remain_ = h_.md.payload_size;
1200 }
1201
1202 8902 state_ = state::body;
1203 BOOST_FALLTHROUGH;
1204 }
1205
1206 case state::body:
1207 {
1208 50201 do_body:
1209
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50201 times.
50201 BOOST_ASSERT(state_ == state::body);
1210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50201 times.
50201 BOOST_ASSERT(h_.md.payload != payload::none);
1211
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50201 times.
50201 BOOST_ASSERT(h_.md.payload != payload::error);
1212
1213 8797 auto set_state_to_complete = [&]()
1214 {
1215
2/2
✓ Branch 0 taken 8337 times.
✓ Branch 1 taken 460 times.
8797 if(style_ == style::in_place)
1216 {
1217 8337 state_ = state::complete_in_place;
1218 8337 return;
1219 }
1220 460 state_ = state::complete;
1221 50201 };
1222
1223
2/2
✓ Branch 0 taken 24436 times.
✓ Branch 1 taken 25765 times.
50201 if(h_.md.payload == payload::chunked)
1224 {
1225 for(;;)
1226 {
1227
2/2
✓ Branch 0 taken 124342 times.
✓ Branch 1 taken 1153 times.
125495 if(chunk_remain_ == 0
1228
2/2
✓ Branch 0 taken 120197 times.
✓ Branch 1 taken 4145 times.
124342 && !chunked_body_ended)
1229 {
1230 120197 auto cs = chained_sequence(cb0_.data());
1231 19411 auto check_ec = [&]()
1232 {
1233
4/6
✓ Branch 2 taken 19402 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19402 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19411 times.
19411 if(ec == condition::need_more_input && got_eof_)
1234 {
1235 ec = BOOST_HTTP_PROTO_ERR(error::incomplete);
1236 state_ = state::reset;
1237 }
1238 139608 };
1239
1240
2/2
✓ Branch 0 taken 111557 times.
✓ Branch 1 taken 8640 times.
120197 if(needs_chunk_close_)
1241 {
1242 111557 parse_eol(cs, ec);
1243
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 111540 times.
111557 if(ec)
1244 {
1245 17 check_ec();
1246 19411 return;
1247 }
1248 }
1249
2/2
✓ Branch 0 taken 4223 times.
✓ Branch 1 taken 4417 times.
8640 else if(trailer_headers_)
1250 {
1251 4223 skip_trailer_headers(cs, ec);
1252
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 4145 times.
4223 if(ec)
1253 {
1254 78 check_ec();
1255 78 return;
1256 }
1257 4145 cb0_.consume(cb0_.size() - cs.size());
1258 4145 chunked_body_ended = true;
1259 8292 continue;
1260 }
1261
1262 115957 auto chunk_size = parse_hex(cs, ec);
1263
2/2
✓ Branch 1 taken 19284 times.
✓ Branch 2 taken 96673 times.
115957 if(ec)
1264 {
1265 19284 check_ec();
1266 19284 return;
1267 }
1268
1269 // skip chunk extensions
1270 96673 find_eol(cs, ec);
1271
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 96641 times.
96673 if(ec)
1272 {
1273 32 check_ec();
1274 32 return;
1275 }
1276
1277 96641 cb0_.consume(cb0_.size() - cs.size());
1278 96641 chunk_remain_ = chunk_size;
1279
1280 96641 needs_chunk_close_ = true;
1281
2/2
✓ Branch 0 taken 4147 times.
✓ Branch 1 taken 92494 times.
96641 if(chunk_remain_ == 0)
1282 {
1283 4147 needs_chunk_close_ = false;
1284 4147 trailer_headers_ = true;
1285 4147 continue;
1286 }
1287 }
1288
1289
6/6
✓ Branch 1 taken 2485 times.
✓ Branch 2 taken 95307 times.
✓ Branch 3 taken 340 times.
✓ Branch 4 taken 2145 times.
✓ Branch 5 taken 340 times.
✓ Branch 6 taken 97452 times.
97792 if(cb0_.size() == 0 && !chunked_body_ended)
1290 {
1291
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 339 times.
340 if(got_eof_)
1292 {
1293 2 ec = BOOST_HTTP_PROTO_ERR(
1294 error::incomplete);
1295 1 state_ = state::reset;
1296 1 return;
1297 }
1298
1299 678 ec = BOOST_HTTP_PROTO_ERR(
1300 error::need_data);
1301 339 return;
1302 }
1303
1304
2/2
✓ Branch 0 taken 51070 times.
✓ Branch 1 taken 46382 times.
97452 if(filter_)
1305 {
1306
1/1
✓ Branch 2 taken 51070 times.
51070 chunk_remain_ -= apply_filter(
1307 ec,
1308 clamp(chunk_remain_, cb0_.size()),
1309 51070 !chunked_body_ended);
1310
1311
6/6
✓ Branch 1 taken 50530 times.
✓ Branch 2 taken 540 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 50506 times.
✓ Branch 5 taken 564 times.
✓ Branch 6 taken 50506 times.
51070 if(ec || chunked_body_ended)
1312 564 return;
1313 }
1314 else
1315 {
1316 const std::size_t chunk_avail =
1317 46382 clamp(chunk_remain_, cb0_.size());
1318 const auto chunk =
1319 46382 buffers::prefix(cb0_.data(), chunk_avail);
1320
1321
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 46382 times.
46382 if(body_limit_remain() < chunk_avail)
1322 {
1323 ec = BOOST_HTTP_PROTO_ERR(
1324 error::body_too_large);
1325 state_ = state::reset;
1326 4121 return;
1327 }
1328
1329
1/4
✓ Branch 0 taken 46382 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46382 switch(style_)
1330 {
1331 46382 case style::in_place:
1332 {
1333 46382 auto copied = buffers::copy(
1334
1/1
✓ Branch 2 taken 46382 times.
46382 cb1_.prepare(cb1_.capacity()),
1335 chunk);
1336 46382 chunk_remain_ -= copied;
1337 46382 body_avail_ += copied;
1338 46382 body_total_ += copied;
1339 46382 cb0_.consume(copied);
1340 46382 cb1_.commit(copied);
1341 46382 if(cb1_.capacity() == 0
1342
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 46382 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 46382 times.
46382 && !chunked_body_ended)
1343 {
1344 ec = BOOST_HTTP_PROTO_ERR(
1345 error::in_place_overflow);
1346 return;
1347 }
1348 46382 break;
1349 }
1350 case style::sink:
1351 {
1352 auto sink_rs = sink_->write(
1353 chunk, !chunked_body_ended);
1354 chunk_remain_ -= sink_rs.bytes;
1355 body_total_ += sink_rs.bytes;
1356 cb0_.consume(sink_rs.bytes);
1357 if(sink_rs.ec.failed())
1358 {
1359 body_avail_ +=
1360 chunk_avail - sink_rs.bytes;
1361 ec = sink_rs.ec;
1362 state_ = state::reset;
1363 return;
1364 }
1365 break;
1366 }
1367 case style::elastic:
1368 {
1369 if(eb_->max_size() - eb_->size()
1370 < chunk_avail)
1371 {
1372 ec = BOOST_HTTP_PROTO_ERR(
1373 error::buffer_overflow);
1374 state_ = state::reset;
1375 return;
1376 }
1377 buffers::copy(
1378 eb_->prepare(chunk_avail),
1379 chunk);
1380 chunk_remain_ -= chunk_avail;
1381 body_total_ += chunk_avail;
1382 cb0_.consume(chunk_avail);
1383 eb_->commit(chunk_avail);
1384 break;
1385 }
1386 }
1387
1388
2/2
✓ Branch 0 taken 4121 times.
✓ Branch 1 taken 42261 times.
46382 if(chunked_body_ended)
1389 {
1390 4121 set_state_to_complete();
1391 4121 return;
1392 }
1393 }
1394 101059 }
1395 }
1396 else
1397 {
1398 // non-chunked payload
1399
1400 77295 const std::size_t payload_avail = [&]()
1401 {
1402 25765 auto ret = cb0_.size();
1403
2/2
✓ Branch 0 taken 24121 times.
✓ Branch 1 taken 1644 times.
25765 if(!filter_)
1404 24121 ret -= body_avail_;
1405
2/2
✓ Branch 0 taken 24159 times.
✓ Branch 1 taken 1606 times.
25765 if(h_.md.payload == payload::size)
1406 24159 return clamp(payload_remain_, ret);
1407 // payload::eof
1408 1606 return ret;
1409 25765 }();
1410
1411 77295 const bool is_complete = [&]()
1412 {
1413
2/2
✓ Branch 0 taken 24159 times.
✓ Branch 1 taken 1606 times.
25765 if(h_.md.payload == payload::size)
1414 24159 return payload_avail == payload_remain_;
1415 // payload::eof
1416 1606 return got_eof_;
1417 25765 }();
1418
1419
2/2
✓ Branch 0 taken 1644 times.
✓ Branch 1 taken 24121 times.
25765 if(filter_)
1420 {
1421 3288 payload_remain_ -= apply_filter(
1422
1/1
✓ Branch 1 taken 1644 times.
1644 ec, payload_avail, !is_complete);
1423
6/6
✓ Branch 1 taken 564 times.
✓ Branch 2 taken 1080 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 516 times.
✓ Branch 5 taken 1128 times.
✓ Branch 6 taken 516 times.
1644 if(ec || is_complete)
1424 1128 return;
1425 }
1426 else
1427 {
1428 // plain body
1429
1430
2/2
✓ Branch 0 taken 764 times.
✓ Branch 1 taken 23357 times.
24121 if(h_.md.payload == payload::to_eof)
1431 {
1432
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 763 times.
764 if(body_limit_remain() < payload_avail)
1433 {
1434 2 ec = BOOST_HTTP_PROTO_ERR(
1435 error::body_too_large);
1436 1 state_ = state::reset;
1437 1 return;
1438 }
1439 }
1440
1441
3/4
✓ Branch 0 taken 23363 times.
✓ Branch 1 taken 371 times.
✓ Branch 2 taken 386 times.
✗ Branch 3 not taken.
24120 switch(style_)
1442 {
1443 23363 case style::in_place:
1444 {
1445 23363 payload_remain_ -= payload_avail;
1446 23363 body_avail_ += payload_avail;
1447 23363 body_total_ += payload_avail;
1448
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23362 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 23362 times.
23363 if(cb0_.capacity() == 0 && !is_complete)
1449 {
1450 2 ec = BOOST_HTTP_PROTO_ERR(
1451 error::in_place_overflow);
1452 1 return;
1453 }
1454 23362 break;
1455 }
1456 371 case style::sink:
1457 {
1458 371 payload_remain_ -= payload_avail;
1459 371 body_total_ += payload_avail;
1460
1/1
✓ Branch 1 taken 371 times.
371 auto sink_rs = sink_->write(
1461 371 buffers::prefix(cb0_.data(), payload_avail),
1462 371 !is_complete);
1463 371 cb0_.consume(sink_rs.bytes);
1464
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 371 times.
371 if(sink_rs.ec.failed())
1465 {
1466 body_avail_ +=
1467 payload_avail - sink_rs.bytes;
1468 ec = sink_rs.ec;
1469 state_ = state::reset;
1470 return;
1471 }
1472 371 break;
1473 }
1474 386 case style::elastic:
1475 {
1476 // payload_remain_ and body_total_
1477 // are already updated in commit()
1478
1479 // cb0_ contains data
1480
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 193 times.
386 if(payload_avail != 0)
1481 {
1482
2/2
✓ Branch 1 taken 193 times.
✓ Branch 4 taken 193 times.
193 if(eb_->max_size() - eb_->size()
1483
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 192 times.
193 < payload_avail)
1484 {
1485 2 ec = BOOST_HTTP_PROTO_ERR(
1486 error::buffer_overflow);
1487 1 state_ = state::reset;
1488 1 return;
1489 }
1490 // only happens when an elastic body
1491 // is attached in header_done state
1492 192 buffers::copy(
1493
1/1
✓ Branch 1 taken 192 times.
192 eb_->prepare(payload_avail),
1494 192 cb0_.data());
1495 192 cb0_.consume(payload_avail);
1496
1/1
✓ Branch 1 taken 192 times.
192 eb_->commit(payload_avail);
1497 192 payload_remain_ -= payload_avail;
1498 192 body_total_ += payload_avail;
1499 }
1500 385 break;
1501 }
1502 }
1503
1504
2/2
✓ Branch 0 taken 4676 times.
✓ Branch 1 taken 19442 times.
24118 if(is_complete)
1505 {
1506 4676 set_state_to_complete();
1507 4676 return;
1508 }
1509 }
1510
1511
4/4
✓ Branch 0 taken 19257 times.
✓ Branch 1 taken 701 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19256 times.
19958 if(h_.md.payload == payload::size && got_eof_)
1512 {
1513 2 ec = BOOST_HTTP_PROTO_ERR(
1514 error::incomplete);
1515 1 state_ = state::reset;
1516 1 return;
1517 }
1518
1519 39914 ec = BOOST_HTTP_PROTO_ERR(
1520 error::need_data);
1521 19957 return;
1522 }
1523
1524 break;
1525 }
1526
1527 2333 case state::set_body:
1528 case state::complete_in_place:
1529 {
1530
2/2
✓ Branch 1 taken 426 times.
✓ Branch 2 taken 1907 times.
2333 auto& body_buf = is_plain() ? cb0_ : cb1_;
1531
1532
3/4
✓ Branch 0 taken 2216 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
2333 switch(style_)
1533 {
1534 2216 case style::in_place:
1535 2216 return; // no-op
1536 58 case style::sink:
1537 {
1538
1/1
✓ Branch 1 taken 58 times.
58 auto rs = sink_->write(
1539 58 buffers::prefix(body_buf.data(), body_avail_),
1540 58 state_ == state::set_body);
1541 58 body_buf.consume(rs.bytes);
1542 58 body_avail_ -= rs.bytes;
1543
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
58 if(rs.ec.failed())
1544 {
1545 ec = rs.ec;
1546 state_ = state::reset;
1547 return;
1548 }
1549 58 break;
1550 }
1551 59 case style::elastic:
1552 {
1553 59 if(eb_->max_size() - eb_->size()
1554
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 < body_avail_)
1555 {
1556 ec = BOOST_HTTP_PROTO_ERR(
1557 error::buffer_overflow);
1558 return;
1559 }
1560 59 buffers::copy(
1561
1/1
✓ Branch 1 taken 59 times.
59 eb_->prepare(body_avail_),
1562 59 body_buf.data());
1563 59 body_buf.consume(body_avail_);
1564 59 eb_->commit(body_avail_);
1565 59 body_avail_ = 0;
1566 // TODO: expand cb0_ when possible?
1567 59 break;
1568 }
1569 }
1570
1571
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if(state_ == state::set_body)
1572 {
1573 state_ = state::body;
1574 goto do_body;
1575 }
1576
1577 117 state_ = state::complete;
1578 117 break;
1579 }
1580
1581 460 case state::complete:
1582 460 break;
1583 }
1584 }
1585
1586 auto
1587 41250 pull_body() ->
1588 const_buffers_type
1589 {
1590
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 41250 times.
✗ Branch 2 not taken.
41250 switch(state_)
1591 {
1592 case state::header_done:
1593 return {};
1594 41250 case state::body:
1595 case state::complete_in_place:
1596 41250 cbp_ = buffers::prefix(
1597
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 22269 times.
41250 (is_plain() ? cb0_ : cb1_).data(),
1598 body_avail_);
1599 41250 return detail::make_span(cbp_);
1600 default:
1601 detail::throw_logic_error();
1602 }
1603 }
1604
1605 void
1606 39606 consume_body(std::size_t n)
1607 {
1608
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 39606 times.
✗ Branch 2 not taken.
39606 switch(state_)
1609 {
1610 case state::header_done:
1611 return;
1612 39606 case state::body:
1613 case state::complete_in_place:
1614 39606 n = clamp(n, body_avail_);
1615
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 20625 times.
39606 (is_plain() ? cb0_ : cb1_).consume(n);
1616 39606 body_avail_ -= n;
1617 39606 return;
1618 default:
1619 detail::throw_logic_error();
1620 }
1621 }
1622
1623 core::string_view
1624 700 body() const
1625 {
1626 // Precondition violation
1627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 if(state_ != state::complete_in_place)
1628 detail::throw_logic_error();
1629
1630 // Precondition violation
1631
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 if(body_avail_ != body_total_)
1632 detail::throw_logic_error();
1633
1634
2/2
✓ Branch 1 taken 579 times.
✓ Branch 2 taken 121 times.
700 auto cbp = (is_plain() ? cb0_ : cb1_).data();
1635
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 700 times.
700 BOOST_ASSERT(cbp[1].size() == 0);
1636
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 700 times.
700 BOOST_ASSERT(cbp[0].size() == body_avail_);
1637 700 return core::string_view(
1638 700 static_cast<char const*>(cbp[0].data()),
1639 1400 body_avail_);
1640 }
1641
1642 void
1643 77 set_body_limit(std::uint64_t n)
1644 {
1645
3/3
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
77 switch(state_)
1646 {
1647 73 case state::header:
1648 case state::header_done:
1649 73 body_limit_ = n;
1650 73 break;
1651 2 case state::complete_in_place:
1652 // only allowed for empty bodies
1653
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(body_total_ == 0)
1654 1 break;
1655 BOOST_FALLTHROUGH;
1656 default:
1657 // set body_limit before parsing the body
1658 3 detail::throw_logic_error();
1659 }
1660 74 }
1661
1662 void
1663 383 set_body(
1664 buffers::any_dynamic_buffer& eb) noexcept
1665 {
1666 383 eb_ = &eb;
1667 383 style_ = style::elastic;
1668 383 nprepare_ = 0; // invalidate
1669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 383 times.
383 if(state_ == state::body)
1670 state_ = state::set_body;
1671 383 }
1672
1673 void
1674 372 set_body(sink& s) noexcept
1675 {
1676 372 sink_ = &s;
1677 372 style_ = style::sink;
1678 372 nprepare_ = 0; // invalidate
1679
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 if(state_ == state::body)
1680 state_ = state::set_body;
1681 372 }
1682
1683 detail::workspace&
1684 755 ws() noexcept
1685 {
1686 755 return ws_;
1687 }
1688
1689 private:
1690 bool
1691 182054 is_plain() const noexcept
1692 {
1693
2/2
✓ Branch 0 taken 172346 times.
✓ Branch 1 taken 9708 times.
354400 return ! filter_ &&
1694
2/2
✓ Branch 0 taken 85762 times.
✓ Branch 1 taken 86584 times.
354400 h_.md.payload != payload::chunked;
1695 }
1696
1697 std::uint64_t
1698 157918 body_limit_remain() const noexcept
1699 {
1700 157918 return body_limit_ - body_total_;
1701 }
1702
1703 std::size_t
1704 52714 apply_filter(
1705 system::error_code& ec,
1706 std::size_t payload_avail,
1707 bool more)
1708 {
1709 52714 std::size_t p0 = payload_avail;
1710 for(;;)
1711 {
1712
4/4
✓ Branch 0 taken 51022 times.
✓ Branch 1 taken 56128 times.
✓ Branch 2 taken 50974 times.
✓ Branch 3 taken 48 times.
107150 if(payload_avail == 0 && more)
1713 51094 break;
1714
1715 auto f_rs = [&](){
1716
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56176 times.
56176 BOOST_ASSERT(filter_ != nullptr);
1717
2/2
✓ Branch 0 taken 18143 times.
✓ Branch 1 taken 38033 times.
56176 if(style_ == style::elastic)
1718 {
1719 18143 std::size_t n = clamp(body_limit_remain());
1720 18143 n = clamp(n, svc_.cfg.min_buffer);
1721 18143 n = clamp(n, eb_->max_size() - eb_->size());
1722
1723 // fill capacity first to avoid
1724 // an allocation
1725 std::size_t avail =
1726 18143 eb_->capacity() - eb_->size();
1727
1/2
✓ Branch 0 taken 18143 times.
✗ Branch 1 not taken.
18143 if(avail != 0)
1728 18143 n = clamp(n, avail);
1729
1730
2/2
✓ Branch 2 taken 18143 times.
✓ Branch 5 taken 18143 times.
36286 return filter_->process(
1731
1/1
✓ Branch 1 taken 18143 times.
18143 eb_->prepare(n),
1732 18143 buffers::prefix(cb0_.data(), payload_avail),
1733 36286 more);
1734 }
1735 else // in-place and sink
1736 {
1737 38033 std::size_t n = clamp(body_limit_remain());
1738 38033 n = clamp(n, cb1_.capacity());
1739
1740
2/2
✓ Branch 2 taken 38033 times.
✓ Branch 5 taken 38033 times.
76066 return filter_->process(
1741
2/2
✓ Branch 1 taken 38033 times.
✓ Branch 4 taken 38033 times.
38033 detail::make_span(cb1_.prepare(n)),
1742 38033 buffers::prefix(cb0_.data(), payload_avail),
1743 76066 more);
1744 }
1745
1/1
✓ Branch 1 taken 56176 times.
56176 }();
1746
1747 56176 cb0_.consume(f_rs.in_bytes);
1748 56176 payload_avail -= f_rs.in_bytes;
1749 56176 body_total_ += f_rs.out_bytes;
1750
1751
3/4
✓ Branch 0 taken 20026 times.
✓ Branch 1 taken 18007 times.
✓ Branch 2 taken 18143 times.
✗ Branch 3 not taken.
56176 switch(style_)
1752 {
1753 20026 case style::in_place:
1754 {
1755 20026 cb1_.commit(f_rs.out_bytes);
1756 20026 body_avail_ += f_rs.out_bytes;
1757 20026 if(cb1_.capacity() == 0 &&
1758
8/8
✓ Branch 0 taken 3261 times.
✓ Branch 1 taken 16765 times.
✓ Branch 2 taken 3245 times.
✓ Branch 3 taken 16 times.
✓ Branch 4 taken 1620 times.
✓ Branch 5 taken 1625 times.
✓ Branch 6 taken 1620 times.
✓ Branch 7 taken 18406 times.
20026 !f_rs.finished && f_rs.in_bytes == 0)
1759 {
1760 3240 ec = BOOST_HTTP_PROTO_ERR(
1761 error::in_place_overflow);
1762 1620 goto done;
1763 }
1764 18406 break;
1765 }
1766 18007 case style::sink:
1767 {
1768 18007 cb1_.commit(f_rs.out_bytes);
1769
1/1
✓ Branch 1 taken 18007 times.
18007 auto sink_rs = sink_->write(
1770
4/4
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 17967 times.
✓ Branch 2 taken 16 times.
✓ Branch 3 taken 24 times.
18007 cb1_.data(), !f_rs.finished || more);
1771 18007 cb1_.consume(sink_rs.bytes);
1772
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18007 times.
18007 if(sink_rs.ec.failed())
1773 {
1774 ec = sink_rs.ec;
1775 state_ = state::reset;
1776 goto done;
1777 }
1778 18007 break;
1779 }
1780 18143 case style::elastic:
1781 {
1782
1/1
✓ Branch 1 taken 18143 times.
18143 eb_->commit(f_rs.out_bytes);
1783
2/2
✓ Branch 1 taken 18143 times.
✓ Branch 4 taken 18143 times.
18143 if(eb_->max_size() - eb_->size() == 0 &&
1784
2/8
✗ Branch 0 not taken.
✓ Branch 1 taken 18143 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 18143 times.
18143 !f_rs.finished && f_rs.in_bytes == 0)
1785 {
1786 ec = BOOST_HTTP_PROTO_ERR(
1787 error::buffer_overflow);
1788 state_ = state::reset;
1789 goto done;
1790 }
1791 18143 break;
1792 }
1793 }
1794
1795
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 54556 times.
54556 if(f_rs.ec.failed())
1796 {
1797 ec = f_rs.ec;
1798 state_ = state::reset;
1799 break;
1800 }
1801
1802 54556 if(body_limit_remain() == 0 &&
1803
4/8
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 54436 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 120 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 54556 times.
54556 !f_rs.finished && f_rs.in_bytes == 0)
1804 {
1805 ec = BOOST_HTTP_PROTO_ERR(
1806 error::body_too_large);
1807 state_ = state::reset;
1808 break;
1809 }
1810
1811
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 54436 times.
54556 if(f_rs.finished)
1812 {
1813
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 48 times.
120 if(!more)
1814 {
1815 72 state_ = (style_ == style::in_place)
1816
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 48 times.
72 ? state::complete_in_place
1817 : state::complete;
1818 }
1819 120 break;
1820 }
1821 54436 }
1822
1823 52714 done:
1824 52714 return p0 - payload_avail;
1825 }
1826 };
1827
1828 //------------------------------------------------
1829 //
1830 // Special Members
1831 //
1832 //------------------------------------------------
1833
1834 1070 parser::
1835 ~parser()
1836 {
1837
2/2
✓ Branch 0 taken 1056 times.
✓ Branch 1 taken 14 times.
1070 delete impl_;
1838 1070 }
1839
1840 11 parser::
1841 11 parser() noexcept
1842 11 : impl_(nullptr)
1843 {
1844 11 }
1845
1846 3 parser::
1847 3 parser(parser&& other) noexcept
1848 3 : impl_(other.impl_)
1849 {
1850 3 other.impl_ = nullptr;
1851 3 }
1852
1853 1056 parser::
1854 parser(
1855 capy::polystore& ctx,
1856 1056 detail::kind k)
1857
1/3
✓ Branch 2 taken 1056 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1056 : impl_(new impl(ctx, k))
1858 {
1859 // TODO: use a single allocation for
1860 // impl and workspace buffer.
1861 1056 }
1862
1863 void
1864 4 parser::
1865 assign(parser&& other) noexcept
1866 {
1867
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(this == &other)
1868 return;
1869
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 delete impl_;
1870 4 impl_ = other.impl_;
1871 4 other.impl_ = nullptr;
1872 }
1873
1874 //--------------------------------------------
1875 //
1876 // Observers
1877 //
1878 //--------------------------------------------
1879
1880 bool
1881 11938 parser::got_header() const noexcept
1882 {
1883
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 11938 times.
11938 BOOST_ASSERT(impl_);
1884 11938 return impl_->got_header();
1885 }
1886
1887 bool
1888 51344 parser::is_complete() const noexcept
1889 {
1890
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51344 times.
51344 BOOST_ASSERT(impl_);
1891 51344 return impl_->is_complete();
1892 }
1893
1894 //------------------------------------------------
1895 //
1896 // Modifiers
1897 //
1898 //------------------------------------------------
1899
1900 void
1901 2480 parser::
1902 reset() noexcept
1903 {
1904
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2480 times.
2480 BOOST_ASSERT(impl_);
1905 2480 impl_->reset();
1906 2480 }
1907
1908 void
1909 10307 parser::start()
1910 {
1911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10307 times.
10307 BOOST_ASSERT(impl_);
1912 10307 impl_->start(false);
1913 10302 }
1914
1915 auto
1916 51003 parser::
1917 prepare() ->
1918 mutable_buffers_type
1919 {
1920
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51003 times.
51003 BOOST_ASSERT(impl_);
1921 51003 return impl_->prepare();
1922 }
1923
1924 void
1925 51000 parser::
1926 commit(
1927 std::size_t n)
1928 {
1929
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 51000 times.
51000 BOOST_ASSERT(impl_);
1930 51000 impl_->commit(n);
1931 50993 }
1932
1933 void
1934 401 parser::
1935 commit_eof()
1936 {
1937
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 401 times.
401 BOOST_ASSERT(impl_);
1938 401 impl_->commit_eof();
1939 398 }
1940
1941 void
1942 69827 parser::
1943 parse(
1944 system::error_code& ec)
1945 {
1946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69827 times.
69827 BOOST_ASSERT(impl_);
1947 69827 impl_->parse(ec);
1948 69825 }
1949
1950 auto
1951 41250 parser::
1952 pull_body() ->
1953 const_buffers_type
1954 {
1955
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41250 times.
41250 BOOST_ASSERT(impl_);
1956 41250 return impl_->pull_body();
1957 }
1958
1959 void
1960 39606 parser::
1961 consume_body(std::size_t n)
1962 {
1963
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39606 times.
39606 BOOST_ASSERT(impl_);
1964 39606 impl_->consume_body(n);
1965 39606 }
1966
1967 core::string_view
1968 700 parser::
1969 body() const
1970 {
1971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 BOOST_ASSERT(impl_);
1972 700 return impl_->body();
1973 }
1974
1975 core::string_view
1976 parser::
1977 release_buffered_data() noexcept
1978 {
1979 // TODO
1980 return {};
1981 }
1982
1983 void
1984 77 parser::
1985 set_body_limit(std::uint64_t n)
1986 {
1987
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 77 times.
77 BOOST_ASSERT(impl_);
1988 77 impl_->set_body_limit(n);
1989 74 }
1990
1991 //------------------------------------------------
1992 //
1993 // Implementation
1994 //
1995 //------------------------------------------------
1996
1997 void
1998 parser::
1999 start_impl(bool head_response)
2000 {
2001 BOOST_ASSERT(impl_);
2002 impl_->start(head_response);
2003 }
2004
2005 detail::header const&
2006 316 parser::
2007 safe_get_header() const
2008 {
2009
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 316 times.
316 BOOST_ASSERT(impl_);
2010 316 return impl_->safe_get_header();
2011 }
2012
2013 detail::workspace&
2014 755 parser::
2015 ws() noexcept
2016 {
2017
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 755 times.
755 BOOST_ASSERT(impl_);
2018 755 return impl_->ws();
2019 }
2020
2021 bool
2022 755 parser::
2023 is_body_set() const noexcept
2024 {
2025
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 755 times.
755 BOOST_ASSERT(impl_);
2026 755 return impl_->is_body_set();
2027 }
2028
2029 void
2030 383 parser::
2031 set_body_impl(
2032 buffers::any_dynamic_buffer& eb) noexcept
2033 {
2034
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 383 times.
383 BOOST_ASSERT(impl_);
2035 383 impl_->set_body(eb);
2036 383 }
2037
2038 void
2039 372 parser::
2040 set_body_impl(sink& s) noexcept
2041 {
2042
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 BOOST_ASSERT(impl_);
2043 372 impl_->set_body(s);
2044 372 }
2045
2046 } // http_proto
2047 } // boost
2048