GCC Code Coverage Report


Directory: libs/http_proto/
File: src/parser.cpp
Date: 2025-05-26 06:53:21
Exec Total Coverage
Lines: 669 756 88.5%
Functions: 61 69 88.4%
Branches: 362 477 75.9%

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/context.hpp>
12 #include <boost/http_proto/detail/except.hpp>
13 #include <boost/http_proto/error.hpp>
14 #include <boost/http_proto/parser.hpp>
15 #include <boost/http_proto/rfc/detail/rules.hpp>
16 #include <boost/http_proto/service/zlib_service.hpp>
17
18 #include <boost/assert.hpp>
19 #include <boost/buffers/copy.hpp>
20 #include <boost/buffers/prefix.hpp>
21 #include <boost/buffers/size.hpp>
22 #include <boost/buffers/make_buffer.hpp>
23 #include <boost/url/grammar/ci_string.hpp>
24 #include <boost/url/grammar/hexdig_chars.hpp>
25
26 #include "detail/filter.hpp"
27
28 namespace boost {
29 namespace http_proto {
30
31 /*
32 Principles for fixed-size buffer design
33
34 axiom 1:
35 To read data you must have a buffer.
36
37 axiom 2:
38 The size of the HTTP header is not
39 known in advance.
40
41 conclusion 3:
42 A single I/O can produce a complete
43 HTTP header and additional payload
44 data.
45
46 conclusion 4:
47 A single I/O can produce multiple
48 complete HTTP headers, complete
49 payloads, and a partial header or
50 payload.
51
52 axiom 5:
53 A process is in one of two states:
54 1. at or below capacity
55 2. above capacity
56
57 axiom 6:
58 A program which can allocate an
59 unbounded number of resources can
60 go above capacity.
61
62 conclusion 7:
63 A program can guarantee never going
64 above capacity if all resources are
65 provisioned at program startup.
66
67 corollary 8:
68 `parser` and `serializer` should each
69 allocate a single buffer of calculated
70 size, and never resize it.
71
72 axiom #:
73 A parser and a serializer are always
74 used in pairs.
75
76 Buffer Usage
77
78 | | begin
79 | H | p | | f | read headers
80 | H | p | | T | f | set T body
81 | H | p | | C | T | f | make codec C
82 | H | p | b | C | T | f | decode p into b
83 | H | p | b | C | T | f | read/parse loop
84 | H | | T | f | destroy codec
85 | H | | T | f | finished
86
87 H headers
88 C codec
89 T body
90 f table
91 p partial payload
92 b body data
93
94 "payload" is the bytes coming in from
95 the stream.
96
97 "body" is the logical body, after transfer
98 encoding is removed. This can be the
99 same as the payload.
100
101 A "plain payload" is when the payload and
102 body are identical (no transfer encodings).
103
104 A "buffered payload" is any payload which is
105 not plain. A second buffer is required
106 for reading.
107
108 "overread" is additional data received past
109 the end of the headers when reading headers,
110 or additional data received past the end of
111 the message payload.
112 */
113
114 namespace {
115 class inflator_filter
116 : public http_proto::detail::filter
117 {
118 zlib::stream& inflator_;
119
120 public:
121 73 inflator_filter(
122 context& ctx,
123 http_proto::detail::workspace& ws,
124 bool use_gzip)
125 292 : inflator_{ ctx.get_service<zlib::service>()
126
2/2
✓ Branch 2 taken 36 times.
✓ Branch 3 taken 37 times.
73 .make_inflator(ws, use_gzip ? 31 : 15) }
127 {
128 73 }
129
130 virtual filter::results
131 119418 on_process(
132 buffers::mutable_buffer out,
133 buffers::const_buffer in,
134 bool more) override
135 {
136 119418 auto flush =
137
2/2
✓ Branch 0 taken 119303 times.
✓ Branch 1 taken 115 times.
119418 more ? zlib::flush::none : zlib::flush::finish;
138 119418 filter::results results;
139
140 for(;;)
141 {
142 119418 auto params = zlib::params{in.data(), in.size(),
143 119418 out.data(), out.size() };
144 119418 auto ec = inflator_.write(params, flush);
145
146 119418 results.in_bytes += in.size() - params.avail_in;
147 119418 results.out_bytes += out.size() - params.avail_out;
148
149
4/4
✓ Branch 1 taken 58922 times.
✓ Branch 2 taken 60496 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 58921 times.
178340 if( ec.failed() &&
150
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 119417 times.
178340 ec != zlib::error::buf_err )
151 {
152 1 results.ec = ec;
153 119418 return results;
154 }
155
156
2/2
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 119297 times.
119417 if( ec == zlib::error::stream_end )
157 {
158 120 results.finished = true;
159 120 return results;
160 }
161
162 119297 in = buffers::suffix(in, params.avail_in);
163 119297 out = buffers::suffix(out, params.avail_out);
164
165
4/6
✓ Branch 1 taken 5991 times.
✓ Branch 2 taken 113306 times.
✓ Branch 4 taken 5991 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 119297 times.
✗ Branch 7 not taken.
119297 if( in.size() == 0 || out.size() == 0 )
166 119297 return results;
167 }
168 }
169 };
170
171 class chained_sequence
172 {
173 char const* pos_;
174 char const* end_;
175 char const* begin_b_;
176 char const* end_b_;
177
178 public:
179 125668 chained_sequence(buffers::const_buffer_pair const& cbp)
180 125668 : pos_(static_cast<char const*>(cbp[0].data()))
181 125668 , end_(pos_ + cbp[0].size())
182 125668 , begin_b_(static_cast<char const*>(cbp[1].data()))
183 125668 , end_b_(begin_b_ + cbp[1].size())
184 {
185 125668 }
186
187 char const*
188 651569 next() noexcept
189 {
190 651569 ++pos_;
191 // most frequently taken branch
192
2/2
✓ Branch 0 taken 630246 times.
✓ Branch 1 taken 21323 times.
651569 if(pos_ < end_)
193 630246 return pos_;
194
195 // swap with the second range
196
2/2
✓ Branch 0 taken 39 times.
✓ Branch 1 taken 21284 times.
21323 if(begin_b_ != end_b_)
197 {
198 39 pos_ = begin_b_;
199 39 end_ = end_b_;
200 39 begin_b_ = end_b_;
201 39 return pos_;
202 }
203
204 // undo the increament
205 21284 pos_ = end_;
206 21284 return nullptr;
207 }
208
209 bool
210 432880 empty() const noexcept
211 {
212 432880 return pos_ == end_;
213 }
214
215 char
216 637061 value() const noexcept
217 {
218 637061 return *pos_;
219 }
220
221 std::size_t
222 446856 size() const noexcept
223 {
224 446856 return (end_ - pos_) + (end_b_ - begin_b_);
225 }
226 };
227
228 std::uint64_t
229 121428 parse_hex(
230 chained_sequence& cs,
231 system::error_code& ec) noexcept
232 {
233 121428 std::uint64_t v = 0;
234 121428 std::size_t init_size = cs.size();
235
2/2
✓ Branch 1 taken 299906 times.
✓ Branch 2 taken 19283 times.
319189 while(!cs.empty())
236 {
237 299906 auto n = grammar::hexdig_value(cs.value());
238
2/2
✓ Branch 0 taken 102144 times.
✓ Branch 1 taken 197762 times.
299906 if(n < 0)
239 {
240
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 102143 times.
102144 if(init_size == cs.size())
241 {
242 2 ec = BOOST_HTTP_PROTO_ERR(
243 error::bad_payload);
244 1 return 0;
245 }
246 102143 return v;
247 }
248
249 // at least 4 significant bits are free
250
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 197761 times.
197762 if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
251 {
252 2 ec = BOOST_HTTP_PROTO_ERR(
253 error::bad_payload);
254 1 return 0;
255 }
256
257 197761 v = (v << 4) | static_cast<std::uint64_t>(n);
258 197761 cs.next();
259 }
260 38566 ec = BOOST_HTTP_PROTO_ERR(
261 error::need_data);
262 19283 return 0;
263 }
264
265 void
266 102495 find_eol(
267 chained_sequence& cs,
268 system::error_code& ec) noexcept
269 {
270
2/2
✓ Branch 1 taken 109096 times.
✓ Branch 2 taken 88 times.
109184 while(!cs.empty())
271 {
272
2/2
✓ Branch 1 taken 102407 times.
✓ Branch 2 taken 6689 times.
109096 if(cs.value() == '\r')
273 {
274
2/2
✓ Branch 1 taken 10 times.
✓ Branch 2 taken 102397 times.
102407 if(!cs.next())
275 10 break;
276
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 102395 times.
102397 if(cs.value() != '\n')
277 {
278 4 ec = BOOST_HTTP_PROTO_ERR(
279 error::bad_payload);
280 2 return;
281 }
282 102395 cs.next();
283 102395 return;
284 }
285 6689 cs.next();
286 }
287 196 ec = BOOST_HTTP_PROTO_ERR(
288 error::need_data);
289 }
290
291 void
292 117028 parse_eol(
293 chained_sequence& cs,
294 system::error_code& ec) noexcept
295 {
296
2/2
✓ Branch 1 taken 117014 times.
✓ Branch 2 taken 14 times.
117028 if(cs.size() >= 2)
297 {
298 // we are sure size is at least 2
299
6/6
✓ Branch 1 taken 117012 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 117011 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 117011 times.
✓ Branch 7 taken 3 times.
117014 if(cs.value() == '\r' && *cs.next() == '\n')
300 {
301 117011 cs.next();
302 117011 return;
303 }
304 6 ec = BOOST_HTTP_PROTO_ERR(
305 error::bad_payload);
306 3 return;
307 }
308 28 ec = BOOST_HTTP_PROTO_ERR(
309 error::need_data);
310 }
311
312 void
313 4223 skip_trailer_headers(
314 chained_sequence& cs,
315 system::error_code& ec) noexcept
316 {
317
2/2
✓ Branch 1 taken 4501 times.
✓ Branch 2 taken 6 times.
4507 while(!cs.empty())
318 {
319
2/2
✓ Branch 1 taken 4149 times.
✓ Branch 2 taken 352 times.
4501 if(cs.value() == '\r')
320 {
321
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4147 times.
4149 if(!cs.next())
322 2 break;
323
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4145 times.
4147 if(cs.value() != '\n')
324 {
325 4 ec = BOOST_HTTP_PROTO_ERR(
326 error::bad_payload);
327 2 return;
328 }
329 4145 cs.next();
330 4145 return;
331 }
332 // skip to the end of field
333 352 find_eol(cs, ec);
334
2/2
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 284 times.
352 if(ec)
335 68 return;
336 }
337 16 ec = BOOST_HTTP_PROTO_ERR(
338 error::need_data);
339 }
340
341 template<class UInt>
342 std::size_t
343 377847 clamp(
344 UInt x,
345 std::size_t limit = (std::numeric_limits<
346 std::size_t>::max)()) noexcept
347 {
348
2/2
✓ Branch 0 taken 105520 times.
✓ Branch 1 taken 272327 times.
377847 if(x >= limit)
349 105520 return limit;
350 272327 return static_cast<std::size_t>(x);
351 }
352 } // namespace
353
354 class parser_service
355 : public service
356 {
357 public:
358 parser::config_base cfg;
359 std::size_t space_needed = 0;
360 std::size_t max_codec = 0;
361 zlib::service const* zlib_svc = nullptr;
362
363 parser_service(
364 context& ctx,
365 parser::config_base const& cfg_);
366
367 std::size_t
368 55253 max_overread() const noexcept
369 {
370 return
371 55253 cfg.headers.max_size +
372 55253 cfg.min_buffer;
373 }
374 };
375
376 40 parser_service::
377 parser_service(
378 context& ctx,
379 40 parser::config_base const& cfg_)
380 40 : cfg(cfg_)
381 {
382 /*
383 | fb | cb0 | cb1 | C | T | f |
384
385 fb flat_buffer headers.max_size
386 cb0 circular_buffer min_buffer
387 cb1 circular_buffer min_buffer
388 C codec max_codec
389 T body max_type_erase
390 f table max_table_space
391
392 */
393 // validate
394 //if(cfg.min_prepare > cfg.max_prepare)
395 //detail::throw_invalid_argument();
396
397
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40 times.
40 if(cfg.max_prepare < 1)
398 detail::throw_invalid_argument();
399
400 // VFALCO TODO OVERFLOW CHECING
401 {
402 //fb_.size() - h_.size +
403 //svc_.cfg.min_buffer +
404 //svc_.cfg.min_buffer +
405 //svc_.max_codec;
406 }
407
408 // VFALCO OVERFLOW CHECKING ON THIS
409 40 space_needed +=
410
1/2
✓ Branch 1 taken 40 times.
✗ Branch 2 not taken.
40 cfg.headers.valid_space_needed();
411
412 // cb0_, cb1_
413 // VFALCO OVERFLOW CHECKING ON THIS
414 40 space_needed +=
415 40 cfg.min_buffer +
416 cfg.min_buffer;
417
418 // T
419 40 space_needed += cfg.max_type_erase;
420
421 // max_codec
422 {
423
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 37 times.
40 if(cfg.apply_deflate_decoder)
424 {
425 auto const n = ctx.get_service<
426
1/2
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
3 zlib::service>().inflator_space_needed(15);
427
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if( max_codec < n)
428 3 max_codec = n;
429 }
430 }
431 40 space_needed += max_codec;
432
433 // round up to alignof(detail::header::entry)
434 40 auto const al = alignof(
435 detail::header::entry);
436 40 space_needed = al * ((
437 40 space_needed + al - 1) / al);
438 40 }
439
440 void
441 40 install_parser_service(
442 context& ctx,
443 parser::config_base const& cfg)
444 {
445 ctx.make_service<
446 40 parser_service>(cfg);
447 40 }
448
449 //------------------------------------------------
450 //
451 // Special Members
452 //
453 //------------------------------------------------
454
455 1052 parser::
456 1052 parser(context& ctx, detail::kind k)
457 1052 : ctx_(ctx)
458 1052 , svc_(ctx.get_service<parser_service>())
459 1052 , h_(detail::empty{ k })
460 1052 , st_(state::reset)
461 2104 , got_header_(false)
462 {
463 1052 auto const n = svc_.space_needed;
464
1/2
✓ Branch 1 taken 1052 times.
✗ Branch 2 not taken.
1052 ws_.allocate(n);
465 1052 h_.cap = n;
466 1052 }
467
468 1052 parser::
469 ~parser()
470 {
471 1052 }
472
473 //--------------------------------------------
474 //
475 // Observers
476 //
477 //--------------------------------------------
478
479 bool
480 11938 parser::got_header() const noexcept
481 {
482 11938 return got_header_;
483 }
484
485 bool
486 51343 parser::is_complete() const noexcept
487 {
488 51343 return st_ >= state::complete_in_place;
489 }
490
491 //------------------------------------------------
492 //
493 // Modifiers
494 //
495 //------------------------------------------------
496
497 void
498 2480 parser::
499 reset() noexcept
500 {
501 2480 ws_.clear();
502 2480 st_ = state::start;
503 2480 got_header_ = false;
504 2480 got_eof_ = false;
505 2480 }
506
507 void
508 10307 parser::start()
509 {
510 10307 start_impl(false);
511 10302 }
512
513 void
514 10307 parser::
515 start_impl(
516 bool head_response)
517 {
518 10307 std::size_t leftover = 0;
519
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(st_)
520 {
521 1 default:
522 case state::reset:
523 // reset must be called first
524 1 detail::throw_logic_error();
525
526 2255 case state::start:
527 // reset required on eof
528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2255 times.
2255 if(got_eof_)
529 detail::throw_logic_error();
530 2255 break;
531
532 3 case state::header:
533
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(fb_.size() == 0)
534 {
535 // start() called twice
536 2 detail::throw_logic_error();
537 }
538 BOOST_FALLTHROUGH;
539
540 case state::header_done:
541 case state::body:
542 case state::set_body:
543 // current message is incomplete
544 2 detail::throw_logic_error();
545
546 8015 case state::complete_in_place:
547 // remove available body.
548
2/2
✓ Branch 1 taken 4000 times.
✓ Branch 2 taken 4015 times.
8015 if(is_plain())
549 4000 cb0_.consume(body_avail_);
550 BOOST_FALLTHROUGH;
551
552 case state::complete:
553 {
554 // move leftovers to front
555
556 8047 ws_.clear();
557 8047 leftover = cb0_.size();
558
559 8047 auto* dest = reinterpret_cast<char*>(ws_.data());
560 8047 auto cbp = cb0_.data();
561 8047 auto* a = static_cast<char const*>(cbp[0].data());
562 8047 auto* b = static_cast<char const*>(cbp[1].data());
563 8047 auto an = cbp[0].size();
564 8047 auto bn = cbp[1].size();
565
566
2/2
✓ Branch 0 taken 7609 times.
✓ Branch 1 taken 438 times.
8047 if(bn == 0)
567 {
568 7609 std::memmove(dest, a, an);
569 }
570 else
571 {
572 // if `a` can fit between `dest` and `b`, shift `b` to the left
573 // and copy `a` to its position. if `a` fits perfectly, the
574 // shift will be of size 0.
575 // if `a` requires more space, shift `b` to the right and
576 // copy `a` to its position. this process may require multiple
577 // iterations and should be done chunk by chunk to prevent `b`
578 // from overlapping with `a`.
579 do
580 {
581 // clamp right shifts to prevent overlap with `a`
582 438 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
583 438 b = static_cast<char const*>(std::memmove(bp, b, bn));
584
585 // a chunk or all of `a` based on available space
586 438 auto chunk_a = static_cast<std::size_t>(b - dest);
587 438 std::memcpy(dest, a, chunk_a); // never overlap
588 438 an -= chunk_a;
589 438 dest += chunk_a;
590 438 a += chunk_a;
591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 } while(an);
592 }
593
594 8047 break;
595 }
596 }
597
598 10302 ws_.clear();
599
600 20604 fb_ = {
601 10302 ws_.data(),
602
1/2
✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
10302 svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
603 leftover };
604
605
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10302 times.
10302 BOOST_ASSERT(
606 fb_.capacity() == svc_.max_overread() - leftover);
607
608
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10302 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10302 BOOST_ASSERT(
609 head_response == false ||
610 h_.kind == detail::kind::response);
611
612 10302 h_ = detail::header(detail::empty{h_.kind});
613 10302 h_.buf = reinterpret_cast<char*>(ws_.data());
614 10302 h_.cbuf = h_.buf;
615 10302 h_.cap = ws_.size();
616
617 10302 st_ = state::header;
618 10302 how_ = how::in_place;
619
620 // reset to the configured default
621 10302 body_limit_ = svc_.cfg.body_limit;
622
623 10302 body_total_ = 0;
624 10302 payload_remain_ = 0;
625 10302 chunk_remain_ = 0;
626 10302 body_avail_ = 0;
627 10302 nprepare_ = 0;
628
629 10302 filter_ = nullptr;
630 10302 eb_ = nullptr;
631 10302 sink_ = nullptr;
632
633 10302 got_header_ = false;
634 10302 head_response_ = head_response;
635 10302 needs_chunk_close_ = false;
636 10302 trailer_headers_ = false;
637 10302 chunked_body_ended = false;
638 10302 }
639
640 auto
641 51100 parser::
642 prepare() ->
643 mutable_buffers_type
644 {
645 51100 nprepare_ = 0;
646
647
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40723 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
51100 switch(st_)
648 {
649 1 default:
650 case state::reset:
651 // reset must be called first
652 1 detail::throw_logic_error();
653
654 1 case state::start:
655 // start must be called first
656 1 detail::throw_logic_error();
657
658 10374 case state::header:
659 {
660
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10374 times.
10374 BOOST_ASSERT(
661 h_.size < svc_.cfg.headers.max_size);
662 10374 std::size_t n = fb_.capacity() - fb_.size();
663
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10374 times.
10374 BOOST_ASSERT(n <= svc_.max_overread());
664 10374 n = clamp(n, svc_.cfg.max_prepare);
665 10374 mbp_[0] = fb_.prepare(n);
666 10374 nprepare_ = n;
667 10374 return mutable_buffers_type(&mbp_[0], 1);
668 }
669
670 case state::header_done:
671 // forgot to call parse()
672 detail::throw_logic_error();
673
674 40723 case state::body:
675 {
676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40723 times.
40723 if(got_eof_)
677 {
678 // forgot to call parse()
679 detail::throw_logic_error();
680 }
681
682
2/2
✓ Branch 1 taken 21673 times.
✓ Branch 2 taken 19050 times.
40723 if(! is_plain())
683 {
684 // buffered payload
685 21673 std::size_t n = cb0_.capacity();
686 21673 n = clamp(n, svc_.cfg.max_prepare);
687 21673 nprepare_ = n;
688 21673 mbp_ = cb0_.prepare(n);
689 21673 return mutable_buffers_type(mbp_);
690 }
691 else
692 {
693
2/2
✓ Branch 0 taken 19029 times.
✓ Branch 1 taken 21 times.
19050 switch(how_)
694 {
695 19029 default:
696 case how::in_place:
697 case how::sink:
698 {
699 19029 std::size_t n = cb0_.capacity();
700 19029 n = clamp(n, svc_.cfg.max_prepare);
701
702
2/2
✓ Branch 0 taken 19004 times.
✓ Branch 1 taken 25 times.
19029 if(h_.md.payload == payload::size)
703 {
704
2/2
✓ Branch 0 taken 17797 times.
✓ Branch 1 taken 1207 times.
19004 if(n > payload_remain_)
705 {
706 17797 std::size_t overread =
707 17797 n - static_cast<std::size_t>(payload_remain_);
708
2/2
✓ Branch 1 taken 7877 times.
✓ Branch 2 taken 9920 times.
17797 if(overread > svc_.max_overread())
709 7877 n = static_cast<std::size_t>(payload_remain_) +
710 7877 svc_.max_overread();
711 }
712 }
713 else
714 {
715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 BOOST_ASSERT(
716 h_.md.payload == payload::to_eof);
717 25 n = clamp(body_limit_remain() + 1, n);
718 }
719
720 19029 nprepare_ = n;
721 19029 mbp_ = cb0_.prepare(n);
722 19029 return mutable_buffers_type(mbp_);
723 }
724 21 case how::elastic:
725 {
726
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
21 BOOST_ASSERT(cb0_.size() == 0);
727
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(body_avail_ == 0);
728
729 21 std::size_t n = svc_.cfg.min_buffer;
730
731
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
21 if(h_.md.payload == payload::size)
732 {
733 // Overreads are not allowed, or
734 // else the caller will see extra
735 // unrelated data.
736 6 n = clamp(payload_remain_, n);
737 }
738 else
739 {
740
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 BOOST_ASSERT(
741 h_.md.payload == payload::to_eof);
742 15 n = clamp(body_limit_remain() + 1, n);
743 15 n = clamp(n, eb_->max_size() - eb_->size());
744 // fill capacity first to avoid an allocation
745 std::size_t avail =
746 15 eb_->capacity() - eb_->size();
747
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if(avail != 0)
748 15 n = clamp(n, avail);
749
750
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if(n == 0)
751 {
752 // dynamic buffer is full
753 // attempt a 1 byte read so
754 // we can detect overflow
755 1 nprepare_ = 1;
756 1 mbp_ = cb0_.prepare(1);
757 1 return mutable_buffers_type(mbp_);
758 }
759 }
760
761 20 n = clamp(n, svc_.cfg.max_prepare);
762
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 BOOST_ASSERT(n != 0);
763 20 nprepare_ = n;
764 20 return eb_->prepare(n);
765 }
766 }
767 }
768 }
769
770 case state::set_body:
771 // forgot to call parse()
772 detail::throw_logic_error();
773
774 1 case state::complete_in_place:
775 case state::complete:
776 // already complete
777 1 detail::throw_logic_error();
778 }
779 }
780
781 void
782 51097 parser::
783 commit(
784 std::size_t n)
785 {
786
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10374 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40720 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
51097 switch(st_)
787 {
788 1 default:
789 case state::reset:
790 {
791 // reset must be called first
792 1 detail::throw_logic_error();
793 }
794
795 1 case state::start:
796 {
797 // forgot to call start()
798 1 detail::throw_logic_error();
799 }
800
801 10374 case state::header:
802 {
803
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10373 times.
10374 if(n > nprepare_)
804 {
805 // n can't be greater than size of
806 // the buffers returned by prepare()
807 1 detail::throw_invalid_argument();
808 }
809
810
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 10372 times.
10373 if(got_eof_)
811 {
812 // can't commit after EOF
813 1 detail::throw_logic_error();
814 }
815
816 10372 nprepare_ = 0; // invalidate
817 10372 fb_.commit(n);
818 10372 break;
819 }
820
821 case state::header_done:
822 {
823 // forgot to call parse()
824 detail::throw_logic_error();
825 }
826
827 40720 case state::body:
828 {
829
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40718 times.
40720 if(n > nprepare_)
830 {
831 // n can't be greater than size of
832 // the buffers returned by prepare()
833 2 detail::throw_invalid_argument();
834 }
835
836
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40718 times.
40718 if(got_eof_)
837 {
838 // can't commit after EOF
839 detail::throw_logic_error();
840 }
841
842 40718 nprepare_ = 0; // invalidate
843
6/6
✓ Branch 1 taken 19045 times.
✓ Branch 2 taken 21673 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 19025 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 40698 times.
40718 if(is_plain() && how_ == how::elastic)
844 {
845
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
20 if(eb_->max_size() == eb_->size())
846 {
847 // borrowed 1 byte from
848 // cb0_ in prepare()
849
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
850 1 cb0_.commit(n);
851 }
852 else
853 {
854 19 eb_->commit(n);
855 19 payload_remain_ -= n;
856 19 body_total_ += n;
857 }
858 }
859 else
860 {
861 40698 cb0_.commit(n);
862 }
863 40718 break;
864 }
865
866 case state::set_body:
867 {
868 // forgot to call parse()
869 detail::throw_logic_error();
870 }
871
872 1 case state::complete_in_place:
873 case state::complete:
874 {
875 // already complete
876 1 detail::throw_logic_error();
877 }
878 }
879 51090 }
880
881 void
882 402 parser::
883 commit_eof()
884 {
885 402 nprepare_ = 0; // invalidate
886
887
5/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 368 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1 times.
402 switch(st_)
888 {
889 1 default:
890 case state::reset:
891 // reset must be called first
892 1 detail::throw_logic_error();
893
894 1 case state::start:
895 // forgot to call prepare()
896 1 detail::throw_logic_error();
897
898 31 case state::header:
899 31 got_eof_ = true;
900 31 break;
901
902 case state::header_done:
903 // forgot to call parse()
904 detail::throw_logic_error();
905
906 368 case state::body:
907 368 got_eof_ = true;
908 368 break;
909
910 case state::set_body:
911 // forgot to call parse()
912 detail::throw_logic_error();
913
914 1 case state::complete_in_place:
915 case state::complete:
916 // can't commit eof when complete
917 1 detail::throw_logic_error();
918 }
919 399 }
920
921 void
922 69899 parser::
923 parse(
924 system::error_code& ec)
925 {
926 69899 ec = {};
927
7/7
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 16649 times.
✓ Branch 3 taken 9083 times.
✓ Branch 4 taken 41372 times.
✓ Branch 5 taken 2333 times.
✓ Branch 6 taken 460 times.
69899 switch(st_)
928 {
929 1 default:
930 case state::reset:
931 // reset must be called first
932 1 detail::throw_logic_error();
933
934 1 case state::start:
935 // start must be called first
936 1 detail::throw_logic_error();
937
938 16649 case state::header:
939 {
940
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16649 times.
16649 BOOST_ASSERT(h_.buf == static_cast<
941 void const*>(ws_.data()));
942
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 16649 times.
16649 BOOST_ASSERT(h_.cbuf == static_cast<
943 void const*>(ws_.data()));
944
945 16649 h_.parse(fb_.size(), svc_.cfg.headers, ec);
946
947
2/2
✓ Branch 2 taken 6384 times.
✓ Branch 3 taken 10265 times.
16649 if(ec == condition::need_more_input)
948 {
949
2/2
✓ Branch 0 taken 6357 times.
✓ Branch 1 taken 27 times.
6384 if(! got_eof_)
950 {
951 // headers incomplete
952 6357 return;
953 }
954
955
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 15 times.
27 if(fb_.size() == 0)
956 {
957 // stream closed cleanly
958 12 st_ = state::reset;
959 24 ec = BOOST_HTTP_PROTO_ERR(
960 error::end_of_stream);
961 12 return;
962 }
963
964 // stream closed with a
965 // partial message received
966 15 st_ = state::reset;
967 30 ec = BOOST_HTTP_PROTO_ERR(
968 error::incomplete);
969 15 return;
970 }
971
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 10006 times.
10265 else if(ec.failed())
972 {
973 // other error,
974 //
975 // VFALCO map this to a bad
976 // request or bad response error?
977 //
978 259 st_ = state::reset; // unrecoverable
979 259 return;
980 }
981
982 10006 got_header_ = true;
983
984 // reserve headers + table
985 10006 ws_.reserve_front(h_.size);
986 10006 ws_.reserve_back(h_.table_space());
987
988 // no payload
989
2/2
✓ Branch 0 taken 9084 times.
✓ Branch 1 taken 922 times.
10006 if(h_.md.payload == payload::none ||
990
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9084 times.
9084 head_response_)
991 {
992 // octets of the next message
993 922 auto overread = fb_.size() - h_.size;
994
1/2
✓ Branch 2 taken 922 times.
✗ Branch 3 not taken.
922 cb0_ = { ws_.data(), overread, overread };
995 922 ws_.reserve_front(overread);
996 922 st_ = state::complete_in_place;
997 922 return;
998 }
999
1000 9084 st_ = state::header_done;
1001 9084 break;
1002 }
1003
1004 9083 case state::header_done:
1005 {
1006 // metadata error
1007
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 8903 times.
9083 if(h_.md.payload == payload::error)
1008 {
1009 // VFALCO This needs looking at
1010 360 ec = BOOST_HTTP_PROTO_ERR(
1011 error::bad_payload);
1012 180 st_ = state::reset; // unrecoverable
1013 180 return;
1014 }
1015
1016 // overread currently includes any and all octets that
1017 // extend beyond the current end of the header
1018 // this can include associated body octets for the
1019 // current message or octets of the next message in the
1020 // stream, e.g. pipelining is being used
1021 8903 auto const overread = fb_.size() - h_.size;
1022
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8903 times.
8903 BOOST_ASSERT(overread <= svc_.max_overread());
1023
1024 8903 auto cap = fb_.capacity() + overread +
1025 8903 svc_.cfg.min_buffer;
1026
1027 // reserve body buffers first, as the decoder
1028 // must be installed after them.
1029 8903 auto const p = ws_.reserve_front(cap);
1030
1031
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 8830 times.
8903 if(svc_.cfg.apply_deflate_decoder &&
1032
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 36 times.
73 h_.md.content_encoding.encoding == encoding::deflate)
1033 {
1034
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, false);
1035 }
1036
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8830 times.
8866 else if(svc_.cfg.apply_gzip_decoder &&
1037
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 h_.md.content_encoding.encoding == encoding::gzip)
1038 {
1039
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 filter_ = &ws_.emplace<inflator_filter>(ctx_, ws_, true);
1040 }
1041 else
1042 {
1043 8830 cap += svc_.max_codec;
1044 8830 ws_.reserve_front(svc_.max_codec);
1045 }
1046
1047
6/6
✓ Branch 1 taken 4206 times.
✓ Branch 2 taken 4697 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 4182 times.
✓ Branch 5 taken 4721 times.
✓ Branch 6 taken 4182 times.
8903 if(is_plain() || how_ == how::elastic)
1048 {
1049
1/2
✓ Branch 1 taken 4721 times.
✗ Branch 2 not taken.
4721 cb0_ = { p, cap, overread };
1050 4721 cb1_ = {};
1051 }
1052 else
1053 {
1054 // buffered payload
1055 8364 std::size_t n0 = (overread > svc_.cfg.min_buffer)
1056
2/2
✓ Branch 0 taken 4158 times.
✓ Branch 1 taken 24 times.
4182 ? overread
1057 4158 : svc_.cfg.min_buffer;
1058 4182 std::size_t n1 = svc_.cfg.min_buffer;
1059
1060
1/2
✓ Branch 1 taken 4182 times.
✗ Branch 2 not taken.
4182 cb0_ = { p , n0, overread };
1061 4182 cb1_ = { p + n0 , n1 };
1062 }
1063
1064
2/2
✓ Branch 0 taken 4373 times.
✓ Branch 1 taken 4530 times.
8903 if(h_.md.payload == payload::size)
1065 {
1066
2/2
✓ Branch 0 taken 4349 times.
✓ Branch 1 taken 24 times.
4373 if(!filter_ &&
1067
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4348 times.
4349 body_limit_ < h_.md.payload_size)
1068 {
1069 2 ec = BOOST_HTTP_PROTO_ERR(
1070 error::body_too_large);
1071 1 st_ = state::reset;
1072 1 return;
1073 }
1074 4372 payload_remain_ = h_.md.payload_size;
1075 }
1076
1077 8902 st_ = state::body;
1078 BOOST_FALLTHROUGH;
1079 }
1080
1081 case state::body:
1082 {
1083 50274 do_body:
1084
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
50274 BOOST_ASSERT(st_ == state::body);
1085
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
50274 BOOST_ASSERT(h_.md.payload != payload::none);
1086
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50274 times.
50274 BOOST_ASSERT(h_.md.payload != payload::error);
1087
1088 8796 auto set_state_to_complete = [&]()
1089 {
1090
2/2
✓ Branch 0 taken 8336 times.
✓ Branch 1 taken 460 times.
8796 if(how_ == how::in_place)
1091 {
1092 8336 st_ = state::complete_in_place;
1093 8336 return;
1094 }
1095 460 st_ = state::complete;
1096 50274 };
1097
1098
2/2
✓ Branch 0 taken 24462 times.
✓ Branch 1 taken 25812 times.
50274 if(h_.md.payload == payload::chunked)
1099 {
1100 for(;;)
1101 {
1102
2/2
✓ Branch 0 taken 129813 times.
✓ Branch 1 taken 1203 times.
131016 if(chunk_remain_ == 0
1103
2/2
✓ Branch 0 taken 125668 times.
✓ Branch 1 taken 4145 times.
129813 && !chunked_body_ended)
1104 {
1105 125668 auto cs = chained_sequence(cb0_.data());
1106 19412 auto check_ec = [&]()
1107 {
1108
4/6
✓ Branch 2 taken 19403 times.
✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 19403 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 19412 times.
19412 if(ec == condition::need_more_input && got_eof_)
1109 {
1110 ec = BOOST_HTTP_PROTO_ERR(error::incomplete);
1111 st_ = state::reset;
1112 }
1113 145080 };
1114
1115
2/2
✓ Branch 0 taken 117028 times.
✓ Branch 1 taken 8640 times.
125668 if(needs_chunk_close_)
1116 {
1117 117028 parse_eol(cs, ec);
1118
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 117011 times.
117028 if(ec)
1119 {
1120 17 check_ec();
1121 19412 return;
1122 }
1123 }
1124
2/2
✓ Branch 0 taken 4223 times.
✓ Branch 1 taken 4417 times.
8640 else if(trailer_headers_)
1125 {
1126 4223 skip_trailer_headers(cs, ec);
1127
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 4145 times.
4223 if(ec)
1128 {
1129 78 check_ec();
1130 78 return;
1131 }
1132 4145 cb0_.consume(cb0_.size() - cs.size());
1133 4145 chunked_body_ended = true;
1134 8292 continue;
1135 }
1136
1137 121428 auto chunk_size = parse_hex(cs, ec);
1138
2/2
✓ Branch 1 taken 19285 times.
✓ Branch 2 taken 102143 times.
121428 if(ec)
1139 {
1140 19285 check_ec();
1141 19285 return;
1142 }
1143
1144 // skip chunk extensions
1145 102143 find_eol(cs, ec);
1146
2/2
✓ Branch 1 taken 32 times.
✓ Branch 2 taken 102111 times.
102143 if(ec)
1147 {
1148 32 check_ec();
1149 32 return;
1150 }
1151
1152 102111 cb0_.consume(cb0_.size() - cs.size());
1153 102111 chunk_remain_ = chunk_size;
1154
1155 102111 needs_chunk_close_ = true;
1156
2/2
✓ Branch 0 taken 4147 times.
✓ Branch 1 taken 97964 times.
102111 if(chunk_remain_ == 0)
1157 {
1158 4147 needs_chunk_close_ = false;
1159 4147 trailer_headers_ = true;
1160 4147 continue;
1161 }
1162 }
1163
1164
6/6
✓ Branch 1 taken 2510 times.
✓ Branch 2 taken 100802 times.
✓ Branch 3 taken 365 times.
✓ Branch 4 taken 2145 times.
✓ Branch 5 taken 365 times.
✓ Branch 6 taken 102947 times.
103312 if(cb0_.size() == 0 && !chunked_body_ended)
1165 {
1166
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 364 times.
365 if(got_eof_)
1167 {
1168 2 ec = BOOST_HTTP_PROTO_ERR(
1169 error::incomplete);
1170 1 st_ = state::reset;
1171 1 return;
1172 }
1173
1174 728 ec = BOOST_HTTP_PROTO_ERR(
1175 error::need_data);
1176 364 return;
1177 }
1178
1179
2/2
✓ Branch 0 taken 56565 times.
✓ Branch 1 taken 46382 times.
102947 if(filter_)
1180 {
1181
1/2
✓ Branch 2 taken 56565 times.
✗ Branch 3 not taken.
56565 chunk_remain_ -= apply_filter(
1182 ec,
1183 clamp(chunk_remain_, cb0_.size()),
1184 56565 !chunked_body_ended);
1185
1186
6/6
✓ Branch 1 taken 56025 times.
✓ Branch 2 taken 540 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 56001 times.
✓ Branch 5 taken 564 times.
✓ Branch 6 taken 56001 times.
56565 if(ec || chunked_body_ended)
1187 564 return;
1188 }
1189 else
1190 {
1191 const std::size_t chunk_avail =
1192 46382 clamp(chunk_remain_, cb0_.size());
1193 const auto chunk =
1194 46382 buffers::prefix(cb0_.data(), chunk_avail);
1195
1196
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 46382 times.
46382 if(body_limit_remain() < chunk_avail)
1197 {
1198 ec = BOOST_HTTP_PROTO_ERR(
1199 error::body_too_large);
1200 st_ = state::reset;
1201 4121 return;
1202 }
1203
1204
1/4
✓ Branch 0 taken 46382 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46382 switch(how_)
1205 {
1206 46382 case how::in_place:
1207 {
1208 46382 auto copied = buffers::copy(
1209
1/2
✓ Branch 2 taken 46382 times.
✗ Branch 3 not taken.
46382 cb1_.prepare(cb1_.capacity()),
1210 chunk);
1211 46382 chunk_remain_ -= copied;
1212 46382 body_avail_ += copied;
1213 46382 body_total_ += copied;
1214 46382 cb0_.consume(copied);
1215 46382 cb1_.commit(copied);
1216 46382 if(cb1_.capacity() == 0
1217
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)
1218 {
1219 ec = BOOST_HTTP_PROTO_ERR(
1220 error::in_place_overflow);
1221 return;
1222 }
1223 46382 break;
1224 }
1225 case how::sink:
1226 {
1227 auto sink_rs = sink_->write(
1228 chunk, !chunked_body_ended);
1229 chunk_remain_ -= sink_rs.bytes;
1230 body_total_ += sink_rs.bytes;
1231 cb0_.consume(sink_rs.bytes);
1232 if(sink_rs.ec.failed())
1233 {
1234 body_avail_ +=
1235 chunk_avail - sink_rs.bytes;
1236 ec = sink_rs.ec;
1237 st_ = state::reset;
1238 return;
1239 }
1240 break;
1241 }
1242 case how::elastic:
1243 {
1244 if(eb_->max_size() - eb_->size()
1245 < chunk_avail)
1246 {
1247 ec = BOOST_HTTP_PROTO_ERR(
1248 error::buffer_overflow);
1249 st_ = state::reset;
1250 return;
1251 }
1252 buffers::copy(
1253 eb_->prepare(chunk_avail),
1254 chunk);
1255 chunk_remain_ -= chunk_avail;
1256 body_total_ += chunk_avail;
1257 cb0_.consume(chunk_avail);
1258 eb_->commit(chunk_avail);
1259 break;
1260 }
1261 }
1262
1263
2/2
✓ Branch 0 taken 4121 times.
✓ Branch 1 taken 42261 times.
46382 if(chunked_body_ended)
1264 {
1265 4121 set_state_to_complete();
1266 4121 return;
1267 }
1268 }
1269 106554 }
1270 }
1271 else
1272 {
1273 // non-chunked payload
1274
1275 77436 const std::size_t payload_avail = [&]()
1276 {
1277 25812 auto ret = cb0_.size();
1278
2/2
✓ Branch 0 taken 24119 times.
✓ Branch 1 taken 1693 times.
25812 if(!filter_)
1279 24119 ret -= body_avail_;
1280
2/2
✓ Branch 0 taken 24181 times.
✓ Branch 1 taken 1631 times.
25812 if(h_.md.payload == payload::size)
1281 24181 return clamp(payload_remain_, ret);
1282 // payload::eof
1283 1631 return ret;
1284 25812 }();
1285
1286 77436 const bool is_complete = [&]()
1287 {
1288
2/2
✓ Branch 0 taken 24181 times.
✓ Branch 1 taken 1631 times.
25812 if(h_.md.payload == payload::size)
1289 24181 return payload_avail == payload_remain_;
1290 // payload::eof
1291 1631 return got_eof_;
1292 25812 }();
1293
1294
2/2
✓ Branch 0 taken 1693 times.
✓ Branch 1 taken 24119 times.
25812 if(filter_)
1295 {
1296 3386 payload_remain_ -= apply_filter(
1297
1/2
✓ Branch 1 taken 1693 times.
✗ Branch 2 not taken.
1693 ec, payload_avail, !is_complete);
1298
6/6
✓ Branch 1 taken 612 times.
✓ Branch 2 taken 1081 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 564 times.
✓ Branch 5 taken 1129 times.
✓ Branch 6 taken 564 times.
1693 if(ec || is_complete)
1299 1129 return;
1300 }
1301 else
1302 {
1303 // plain body
1304
1305
2/2
✓ Branch 0 taken 764 times.
✓ Branch 1 taken 23355 times.
24119 if(h_.md.payload == payload::to_eof)
1306 {
1307
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 763 times.
764 if(body_limit_remain() < payload_avail)
1308 {
1309 2 ec = BOOST_HTTP_PROTO_ERR(
1310 error::body_too_large);
1311 1 st_ = state::reset;
1312 1 return;
1313 }
1314 }
1315
1316
3/4
✓ Branch 0 taken 23361 times.
✓ Branch 1 taken 371 times.
✓ Branch 2 taken 386 times.
✗ Branch 3 not taken.
24118 switch(how_)
1317 {
1318 23361 case how::in_place:
1319 {
1320 23361 payload_remain_ -= payload_avail;
1321 23361 body_avail_ += payload_avail;
1322 23361 body_total_ += payload_avail;
1323
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23360 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 23360 times.
23361 if(cb0_.capacity() == 0 && !is_complete)
1324 {
1325 2 ec = BOOST_HTTP_PROTO_ERR(
1326 error::in_place_overflow);
1327 1 return;
1328 }
1329 23360 break;
1330 }
1331 371 case how::sink:
1332 {
1333 371 payload_remain_ -= payload_avail;
1334 371 body_total_ += payload_avail;
1335
1/2
✓ Branch 1 taken 371 times.
✗ Branch 2 not taken.
371 auto sink_rs = sink_->write(
1336 371 buffers::prefix(
1337 cb0_.data(),
1338 payload_avail),
1339 371 !is_complete);
1340 371 cb0_.consume(sink_rs.bytes);
1341
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 371 times.
371 if(sink_rs.ec.failed())
1342 {
1343 body_avail_ +=
1344 payload_avail - sink_rs.bytes;
1345 ec = sink_rs.ec;
1346 st_ = state::reset;
1347 return;
1348 }
1349 371 break;
1350 }
1351 386 case how::elastic:
1352 {
1353 // payload_remain_ and body_total_
1354 // are already updated in commit()
1355
1356 // cb0_ contains data
1357
2/2
✓ Branch 0 taken 193 times.
✓ Branch 1 taken 193 times.
386 if(payload_avail != 0)
1358 {
1359
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()
1360
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 192 times.
193 < payload_avail)
1361 {
1362 2 ec = BOOST_HTTP_PROTO_ERR(
1363 error::buffer_overflow);
1364 1 st_ = state::reset;
1365 1 return;
1366 }
1367 // only happens when an elastic body
1368 // is attached in header_done state
1369 192 buffers::copy(
1370
1/2
✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
192 eb_->prepare(payload_avail),
1371 192 cb0_.data());
1372 192 cb0_.consume(payload_avail);
1373
1/2
✓ Branch 1 taken 192 times.
✗ Branch 2 not taken.
192 eb_->commit(payload_avail);
1374 192 payload_remain_ -= payload_avail;
1375 192 body_total_ += payload_avail;
1376 }
1377 385 break;
1378 }
1379 }
1380
1381
2/2
✓ Branch 0 taken 4675 times.
✓ Branch 1 taken 19441 times.
24116 if(is_complete)
1382 {
1383 4675 set_state_to_complete();
1384 4675 return;
1385 }
1386 }
1387
1388
4/4
✓ Branch 0 taken 19280 times.
✓ Branch 1 taken 725 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19279 times.
20005 if(h_.md.payload == payload::size && got_eof_)
1389 {
1390 2 ec = BOOST_HTTP_PROTO_ERR(
1391 error::incomplete);
1392 1 st_ = state::reset;
1393 1 return;
1394 }
1395
1396 40008 ec = BOOST_HTTP_PROTO_ERR(
1397 error::need_data);
1398 20004 return;
1399 }
1400
1401 break;
1402 }
1403
1404 2333 case state::set_body:
1405 case state::complete_in_place:
1406 {
1407
2/2
✓ Branch 1 taken 426 times.
✓ Branch 2 taken 1907 times.
2333 auto& body_buf = is_plain() ? cb0_ : cb1_;
1408
1409
3/4
✓ Branch 0 taken 2216 times.
✓ Branch 1 taken 58 times.
✓ Branch 2 taken 59 times.
✗ Branch 3 not taken.
2333 switch(how_)
1410 {
1411 2216 case how::in_place:
1412 2216 return; // no-op
1413 58 case how::sink:
1414 {
1415
1/2
✓ Branch 1 taken 58 times.
✗ Branch 2 not taken.
58 auto rs = sink_->write(
1416 58 buffers::prefix(
1417 body_buf.data(),
1418 body_avail_),
1419 58 st_ == state::set_body);
1420 58 body_buf.consume(rs.bytes);
1421 58 body_avail_ -= rs.bytes;
1422
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 58 times.
58 if(rs.ec.failed())
1423 {
1424 ec = rs.ec;
1425 st_ = state::reset;
1426 return;
1427 }
1428 58 break;
1429 }
1430 59 case how::elastic:
1431 {
1432 59 if(eb_->max_size() - eb_->size()
1433
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 59 times.
59 < body_avail_)
1434 {
1435 ec = BOOST_HTTP_PROTO_ERR(
1436 error::buffer_overflow);
1437 return;
1438 }
1439 59 buffers::copy(
1440
1/2
✓ Branch 1 taken 59 times.
✗ Branch 2 not taken.
59 eb_->prepare(body_avail_),
1441 59 body_buf.data());
1442 59 body_buf.consume(body_avail_);
1443 59 eb_->commit(body_avail_);
1444 59 body_avail_ = 0;
1445 // TODO: expand cb0_ when possible?
1446 59 break;
1447 }
1448 }
1449
1450
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if(st_ == state::set_body)
1451 {
1452 st_ = state::body;
1453 goto do_body;
1454 }
1455
1456 117 st_ = state::complete;
1457 117 break;
1458 }
1459
1460 460 case state::complete:
1461 460 break;
1462 }
1463 }
1464
1465 auto
1466 41250 parser::
1467 pull_body() ->
1468 const_buffers_type
1469 {
1470
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 41250 times.
✗ Branch 2 not taken.
41250 switch(st_)
1471 {
1472 case state::header_done:
1473 return {};
1474 41250 case state::body:
1475 case state::complete_in_place:
1476 41250 cbp_ = buffers::prefix(
1477
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 22269 times.
41250 (is_plain() ? cb0_ : cb1_).data(),
1478 body_avail_);
1479 41250 return const_buffers_type(cbp_);
1480 default:
1481 detail::throw_logic_error();
1482 }
1483 }
1484
1485 void
1486 39606 parser::
1487 consume_body(std::size_t n)
1488 {
1489
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 39606 times.
✗ Branch 2 not taken.
39606 switch(st_)
1490 {
1491 case state::header_done:
1492 return;
1493 39606 case state::body:
1494 case state::complete_in_place:
1495 39606 n = clamp(n, body_avail_);
1496
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 20625 times.
39606 (is_plain() ? cb0_ : cb1_).consume(n);
1497 39606 body_avail_ -= n;
1498 39606 return;
1499 default:
1500 detail::throw_logic_error();
1501 }
1502 }
1503
1504 core::string_view
1505 699 parser::
1506 body() const noexcept
1507 {
1508
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 699 times.
699 if(st_ != state::complete_in_place)
1509 {
1510 // Precondition violation
1511 detail::throw_logic_error();
1512 }
1513
1514
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 699 times.
699 if(body_avail_ != body_total_)
1515 {
1516 // Precondition violation
1517 detail::throw_logic_error();
1518 }
1519
1520
2/2
✓ Branch 1 taken 578 times.
✓ Branch 2 taken 121 times.
699 auto cbp = (is_plain() ? cb0_ : cb1_).data();
1521
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 699 times.
699 BOOST_ASSERT(cbp[1].size() == 0);
1522
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 699 times.
699 BOOST_ASSERT(cbp[0].size() == body_avail_);
1523 699 return core::string_view(
1524 699 static_cast<char const*>(cbp[0].data()),
1525 1398 body_avail_);
1526 }
1527
1528 core::string_view
1529 parser::
1530 release_buffered_data() noexcept
1531 {
1532 return {};
1533 }
1534
1535 void
1536 77 parser::
1537 set_body_limit(std::uint64_t n)
1538 {
1539
3/3
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
77 switch(st_)
1540 {
1541 73 case state::header:
1542 case state::header_done:
1543 73 body_limit_ = n;
1544 73 break;
1545 2 case state::complete_in_place:
1546 // only allowed for empty bodies
1547
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(body_total_ == 0)
1548 1 break;
1549 BOOST_FALLTHROUGH;
1550 default:
1551 // set body_limit before parsing the body
1552 3 detail::throw_logic_error();
1553 }
1554 74 }
1555
1556 //------------------------------------------------
1557 //
1558 // Implementation
1559 //
1560 //------------------------------------------------
1561
1562 void
1563 755 parser::
1564 on_set_body() noexcept
1565 {
1566
4/6
✓ Branch 0 taken 118 times.
✓ Branch 1 taken 637 times.
✓ Branch 2 taken 118 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 118 times.
755 BOOST_ASSERT(
1567 st_ == state::header_done ||
1568 st_ == state::body ||
1569 st_ == state::complete_in_place);
1570
1571 755 nprepare_ = 0; // invalidate
1572
1573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 755 times.
755 if(st_ == state::body)
1574 st_ = state::set_body;
1575 755 }
1576
1577 std::size_t
1578 58258 parser::
1579 apply_filter(
1580 system::error_code& ec,
1581 std::size_t payload_avail,
1582 bool more)
1583 {
1584 58258 std::size_t p0 = payload_avail;
1585 for(;;)
1586 {
1587
4/4
✓ Branch 0 taken 56566 times.
✓ Branch 1 taken 60035 times.
✓ Branch 2 taken 56517 times.
✓ Branch 3 taken 49 times.
116601 if(payload_avail == 0 && more)
1588 56638 break;
1589
1590 auto f_rs = [&](){
1591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 60084 times.
60084 BOOST_ASSERT(filter_ != nullptr);
1592
2/2
✓ Branch 0 taken 19887 times.
✓ Branch 1 taken 40197 times.
60084 if(how_ == how::elastic)
1593 {
1594 19887 std::size_t n = clamp(body_limit_remain());
1595 19887 n = clamp(n, svc_.cfg.min_buffer);
1596 19887 n = clamp(n, eb_->max_size() - eb_->size());
1597
1598 // fill capacity first to avoid
1599 // an allocation
1600 std::size_t avail =
1601 19887 eb_->capacity() - eb_->size();
1602
2/2
✓ Branch 0 taken 19886 times.
✓ Branch 1 taken 1 times.
19887 if(avail != 0)
1603 19886 n = clamp(n, avail);
1604
1605
1/2
✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
19887 return filter_->process(
1606
1/2
✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
19887 eb_->prepare(n),
1607 19887 buffers::prefix(
1608 19887 cb0_.data(),
1609 payload_avail),
1610 39774 more);
1611 }
1612 else // in-place and sink
1613 {
1614 40197 std::size_t n = clamp(body_limit_remain());
1615 40197 n = clamp(n, cb1_.capacity());
1616
1617
1/2
✓ Branch 1 taken 40197 times.
✗ Branch 2 not taken.
40197 return filter_->process(
1618
1/2
✓ Branch 1 taken 40197 times.
✗ Branch 2 not taken.
40197 cb1_.prepare(n),
1619 40197 buffers::prefix(
1620 40197 cb0_.data(),
1621 payload_avail),
1622 80394 more);
1623 }
1624
1/2
✓ Branch 1 taken 60084 times.
✗ Branch 2 not taken.
60084 }();
1625
1626 60084 cb0_.consume(f_rs.in_bytes);
1627 60084 payload_avail -= f_rs.in_bytes;
1628 60084 body_total_ += f_rs.out_bytes;
1629
1630
2/2
✓ Branch 0 taken 59964 times.
✓ Branch 1 taken 120 times.
120048 bool needs_more_space = !f_rs.finished &&
1631
2/2
✓ Branch 0 taken 3462 times.
✓ Branch 1 taken 56502 times.
59964 payload_avail != 0;
1632
1633
3/4
✓ Branch 0 taken 20238 times.
✓ Branch 1 taken 19959 times.
✓ Branch 2 taken 19887 times.
✗ Branch 3 not taken.
60084 switch(how_)
1634 {
1635 20238 case how::in_place:
1636 {
1637 20238 cb1_.commit(f_rs.out_bytes);
1638 20238 body_avail_ += f_rs.out_bytes;
1639
6/6
✓ Branch 1 taken 1641 times.
✓ Branch 2 taken 18597 times.
✓ Branch 3 taken 1620 times.
✓ Branch 4 taken 21 times.
✓ Branch 5 taken 1620 times.
✓ Branch 6 taken 18618 times.
20238 if(cb1_.capacity() == 0 &&
1640 needs_more_space)
1641 {
1642 3240 ec = BOOST_HTTP_PROTO_ERR(
1643 error::in_place_overflow);
1644 1620 goto done;
1645 }
1646 18618 break;
1647 }
1648 19959 case how::sink:
1649 {
1650 19959 cb1_.commit(f_rs.out_bytes);
1651
1/2
✓ Branch 1 taken 19959 times.
✗ Branch 2 not taken.
19959 auto sink_rs = sink_->write(
1652 19959 cb1_.data(), !f_rs.finished);
1653 19959 cb1_.consume(sink_rs.bytes);
1654
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19959 times.
19959 if(sink_rs.ec.failed())
1655 {
1656 ec = sink_rs.ec;
1657 st_ = state::reset;
1658 goto done;
1659 }
1660 19959 break;
1661 }
1662 19887 case how::elastic:
1663 {
1664
1/2
✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
19887 eb_->commit(f_rs.out_bytes);
1665
4/10
✓ Branch 1 taken 19887 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19887 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 19887 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 19887 times.
19887 if(eb_->max_size() - eb_->size() == 0 &&
1666 needs_more_space)
1667 {
1668 ec = BOOST_HTTP_PROTO_ERR(
1669 error::buffer_overflow);
1670 st_ = state::reset;
1671 goto done;
1672 }
1673 19887 break;
1674 }
1675 }
1676
1677
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 58463 times.
58464 if(f_rs.ec.failed())
1678 {
1679 1 ec = f_rs.ec;
1680 1 st_ = state::reset;
1681 1 break;
1682 }
1683
1684
4/6
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 58343 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 120 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 58463 times.
58463 if(body_limit_remain() == 0 &&
1685 needs_more_space)
1686 {
1687 ec = BOOST_HTTP_PROTO_ERR(
1688 error::body_too_large);
1689 st_ = state::reset;
1690 break;
1691 }
1692
1693
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 58343 times.
58463 if(f_rs.finished)
1694 {
1695
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 48 times.
120 if(!more)
1696 {
1697 72 st_ = (how_ == how::in_place)
1698
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 48 times.
72 ? state::complete_in_place
1699 : state::complete;
1700 }
1701 120 break;
1702 }
1703 58343 }
1704
1705 58258 done:
1706 58258 return p0 - payload_avail;
1707 }
1708
1709 detail::header const*
1710 315 parser::
1711 safe_get_header() const
1712 {
1713 // headers must be received
1714
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
315 if(! got_header_)
1715 detail::throw_logic_error();
1716
1717 315 return &h_;
1718 }
1719
1720 bool
1721 182247 parser::
1722 is_plain() const noexcept
1723 {
1724
2/2
✓ Branch 0 taken 172342 times.
✓ Branch 1 taken 9905 times.
354589 return ! filter_ &&
1725
2/2
✓ Branch 0 taken 85758 times.
✓ Branch 1 taken 86584 times.
354589 h_.md.payload != payload::chunked;
1726 }
1727
1728 std::uint64_t
1729 165733 parser::
1730 body_limit_remain() const noexcept
1731 {
1732 165733 return body_limit_ - body_total_;
1733 }
1734
1735 } // http_proto
1736 } // boost
1737