GCC Code Coverage Report


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