GCC Code Coverage Report


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