GCC Code Coverage Report


Directory: libs/http_proto/
File: src/parser.cpp
Date: 2025-09-21 18:08:15
Exec Total Coverage
Lines: 748 876 85.4%
Functions: 82 96 85.4%
Branches: 372 531 70.1%

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