GCC Code Coverage Report


Directory: ./
File: libs/http_proto/src/parser.cpp
Date: 2025-12-05 19:49:26
Exec Total Coverage
Lines: 764 894 85.5%
Functions: 86 100 86.0%
Branches: 380 507 75.0%

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