GCC Code Coverage Report


Directory: libs/http_proto/
File: src/parser.cpp
Date: 2025-01-06 18:34:49
Exec Total Coverage
Lines: 638 716 89.1%
Functions: 55 64 85.9%
Branches: 342 446 76.7%

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/algorithm.hpp>
20 #include <boost/buffers/buffer_copy.hpp>
21 #include <boost/buffers/buffer_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 119613 on_process(
132 buffers::mutable_buffer out,
133 buffers::const_buffer in,
134 bool more) override
135 {
136 119613 auto flush =
137
2/2
✓ Branch 0 taken 119507 times.
✓ Branch 1 taken 106 times.
119613 more ? zlib::flush::none : zlib::flush::finish;
138 119613 filter::results results;
139
140 for(;;)
141 {
142 119613 auto params = zlib::params{in.data(), in.size(),
143 119613 out.data(), out.size() };
144 119613 auto ec = inflator_.write(params, flush);
145
146 119613 results.in_bytes += in.size() - params.avail_in;
147 119613 results.out_bytes += out.size() - params.avail_out;
148
149
4/4
✓ Branch 1 taken 59024 times.
✓ Branch 2 taken 60589 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 59023 times.
178637 if( ec.failed() &&
150
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 119612 times.
178637 ec != zlib::error::buf_err )
151 {
152 1 results.ec = ec;
153 119613 return results;
154 }
155
156
2/2
✓ Branch 2 taken 120 times.
✓ Branch 3 taken 119492 times.
119612 if( ec == zlib::error::stream_end )
157 {
158 120 results.finished = true;
159 120 return results;
160 }
161
162 119492 in = buffers::suffix(in, params.avail_in);
163 119492 out = buffers::suffix(out, params.avail_out);
164
165
4/6
✓ Branch 1 taken 5997 times.
✓ Branch 2 taken 113495 times.
✓ Branch 4 taken 5997 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 119492 times.
✗ Branch 7 not taken.
119492 if( in.size() == 0 || out.size() == 0 )
166 119492 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 125355 chained_sequence(buffers::const_buffer_pair const& cbp)
180 125355 : pos_(static_cast<char const*>(cbp[0].data()))
181 125355 , end_(pos_ + cbp[0].size())
182 125355 , begin_b_(static_cast<char const*>(cbp[1].data()))
183 125355 , end_b_(begin_b_ + cbp[1].size())
184 {
185 125355 }
186
187 char const*
188 650739 next() noexcept
189 {
190 650739 ++pos_;
191 // most frequently taken branch
192
2/2
✓ Branch 0 taken 629440 times.
✓ Branch 1 taken 21299 times.
650739 if(pos_ < end_)
193 629440 return pos_;
194
195 // swap with the second range
196
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 21241 times.
21299 if(begin_b_ != end_b_)
197 {
198 58 pos_ = begin_b_;
199 58 end_ = end_b_;
200 58 begin_b_ = end_b_;
201 58 return pos_;
202 }
203
204 // undo the increament
205 21241 pos_ = end_;
206 21241 return nullptr;
207 }
208
209 bool
210 431711 empty() const noexcept
211 {
212 431711 return pos_ == end_;
213 }
214
215 char
216 636159 value() const noexcept
217 {
218 636159 return *pos_;
219 }
220
221 std::size_t
222 446563 size() const noexcept
223 {
224 446563 return (end_ - pos_) + (end_b_ - begin_b_);
225 }
226 };
227
228 std::uint64_t
229 121166 parse_hex(
230 chained_sequence& cs,
231 system::error_code& ec) noexcept
232 {
233 121166 std::uint64_t v = 0;
234 121166 std::size_t init_size = cs.size();
235
2/2
✓ Branch 1 taken 299875 times.
✓ Branch 2 taken 19037 times.
318912 while(!cs.empty())
236 {
237 299875 auto n = grammar::hexdig_value(cs.value());
238
2/2
✓ Branch 0 taken 102128 times.
✓ Branch 1 taken 197747 times.
299875 if(n < 0)
239 {
240
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 102127 times.
102128 if(init_size == cs.size())
241 {
242 2 ec = BOOST_HTTP_PROTO_ERR(
243 error::bad_payload);
244 1 return 0;
245 }
246 102127 return v;
247 }
248
249 // at least 4 significant bits are free
250
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 197746 times.
197747 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 197746 v = (v << 4) | static_cast<std::uint64_t>(n);
258 197746 cs.next();
259 }
260 38074 ec = BOOST_HTTP_PROTO_ERR(
261 error::need_data);
262 19037 return 0;
263 }
264
265 void
266 102421 find_eol(
267 chained_sequence& cs,
268 system::error_code& ec) noexcept
269 {
270
2/2
✓ Branch 1 taken 108311 times.
✓ Branch 2 taken 44 times.
108355 while(!cs.empty())
271 {
272
2/2
✓ Branch 1 taken 102377 times.
✓ Branch 2 taken 5934 times.
108311 if(cs.value() == '\r')
273 {
274
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 102372 times.
102377 if(!cs.next())
275 5 break;
276
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 102370 times.
102372 if(cs.value() != '\n')
277 {
278 4 ec = BOOST_HTTP_PROTO_ERR(
279 error::bad_payload);
280 2 return;
281 }
282 102370 cs.next();
283 102370 return;
284 }
285 5934 cs.next();
286 }
287 98 ec = BOOST_HTTP_PROTO_ERR(
288 error::need_data);
289 }
290
291 void
292 117014 parse_eol(
293 chained_sequence& cs,
294 system::error_code& ec) noexcept
295 {
296
2/2
✓ Branch 1 taken 117012 times.
✓ Branch 2 taken 2 times.
117014 if(cs.size() >= 2)
297 {
298 // we are sure size is at least 2
299
6/6
✓ Branch 1 taken 117010 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 117009 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 117009 times.
✓ Branch 7 taken 3 times.
117012 if(cs.value() == '\r' && *cs.next() == '\n')
300 {
301 117009 cs.next();
302 117009 return;
303 }
304 6 ec = BOOST_HTTP_PROTO_ERR(
305 error::bad_payload);
306 3 return;
307 }
308 4 ec = BOOST_HTTP_PROTO_ERR(
309 error::need_data);
310 }
311
312 void
313 4184 skip_trailer_headers(
314 chained_sequence& cs,
315 system::error_code& ec) noexcept
316 {
317
2/2
✓ Branch 1 taken 4442 times.
✓ Branch 2 taken 2 times.
4444 while(!cs.empty())
318 {
319
2/2
✓ Branch 1 taken 4148 times.
✓ Branch 2 taken 294 times.
4442 if(cs.value() == '\r')
320 {
321
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4147 times.
4148 if(!cs.next())
322 1 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 294 find_eol(cs, ec);
334
2/2
✓ Branch 1 taken 34 times.
✓ Branch 2 taken 260 times.
294 if(ec)
335 34 return;
336 }
337 6 ec = BOOST_HTTP_PROTO_ERR(
338 error::need_data);
339 }
340
341 template<class UInt>
342 std::size_t
343 378049 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 105787 times.
✓ Branch 1 taken 272262 times.
378049 if(x >= limit)
349 105787 return limit;
350 272262 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 56352 max_overread() const noexcept
369 {
370 return
371 56352 cfg.headers.max_size +
372 56352 cfg.min_buffer;
373 }
374 };
375
376 38 parser_service::
377 parser_service(
378 context& ctx,
379 38 parser::config_base const& cfg_)
380 38 : 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 38 times.
38 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 38 space_needed +=
410
1/2
✓ Branch 1 taken 38 times.
✗ Branch 2 not taken.
38 cfg.headers.valid_space_needed();
411
412 // cb0_, cb1_
413 // VFALCO OVERFLOW CHECKING ON THIS
414 38 space_needed +=
415 38 cfg.min_buffer +
416 cfg.min_buffer;
417
418 // T
419 38 space_needed += cfg.max_type_erase;
420
421 // max_codec
422 {
423
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 35 times.
38 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 38 space_needed += max_codec;
432
433 // round up to alignof(detail::header::entry)
434 38 auto const al = alignof(
435 detail::header::entry);
436 38 space_needed = al * ((
437 38 space_needed + al - 1) / al);
438 38 }
439
440 void
441 38 install_parser_service(
442 context& ctx,
443 parser::config_base const& cfg)
444 {
445 ctx.make_service<
446 38 parser_service>(cfg);
447 38 }
448
449 //------------------------------------------------
450 //
451 // Special Members
452 //
453 //------------------------------------------------
454
455 1050 parser::
456 parser(
457 context& ctx,
458 1050 detail::kind k)
459 1050 : ctx_(ctx)
460 1050 , svc_(ctx.get_service<
461 1050 parser_service>())
462 1050 , h_(detail::empty{k})
463 1050 , eb_(nullptr)
464 2100 , st_(state::reset)
465 {
466 1050 auto const n =
467 1050 svc_.space_needed;
468
1/2
✓ Branch 1 taken 1050 times.
✗ Branch 2 not taken.
1050 ws_.allocate(n);
469 1050 h_.cap = n;
470 1050 }
471
472 //------------------------------------------------
473
474 1050 parser::
475 ~parser()
476 {
477 1050 }
478
479 //------------------------------------------------
480 //
481 // Modifiers
482 //
483 //------------------------------------------------
484
485 // prepare for a new stream
486 void
487 1744 parser::
488 reset() noexcept
489 {
490 1744 ws_.clear();
491 1744 eb_ = nullptr;
492 1744 st_ = state::start;
493 1744 got_eof_ = false;
494 1744 }
495
496 void
497 10303 parser::
498 start_impl(
499 bool head_response)
500 {
501 10303 std::size_t leftover = 0;
502
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1728 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8189 times.
✓ Branch 5 taken 381 times.
10303 switch(st_)
503 {
504 1 default:
505 case state::reset:
506 // reset must be called first
507 1 detail::throw_logic_error();
508
509 1728 case state::start:
510 // reset required on eof
511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1728 times.
1728 if(got_eof_)
512 detail::throw_logic_error();
513 1728 break;
514
515 3 case state::header:
516
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(fb_.size() == 0)
517 {
518 // start() called twice
519 2 detail::throw_logic_error();
520 }
521 BOOST_FALLTHROUGH;
522
523 case state::body:
524 case state::set_body:
525 // current message is incomplete
526 2 detail::throw_logic_error();
527
528 8189 case state::complete_in_place:
529 // remove available body.
530
2/2
✓ Branch 1 taken 4174 times.
✓ Branch 2 taken 4015 times.
8189 if(is_plain())
531 4174 cb0_.consume(body_avail_);
532 BOOST_FALLTHROUGH;
533
534 case state::complete:
535 {
536 // move leftovers to front
537
538 8570 ws_.clear();
539 8570 leftover = cb0_.size();
540
541 8570 auto* dest = reinterpret_cast<char*>(ws_.data());
542 8570 auto cbp = cb0_.data();
543 8570 auto* a = static_cast<char const*>(cbp[0].data());
544 8570 auto* b = static_cast<char const*>(cbp[1].data());
545 8570 auto an = cbp[0].size();
546 8570 auto bn = cbp[1].size();
547
548
2/2
✓ Branch 0 taken 8132 times.
✓ Branch 1 taken 438 times.
8570 if(bn == 0)
549 {
550 8132 std::memmove(dest, a, an);
551 }
552 else
553 {
554 // if `a` can fit between `dest` and `b`, shift `b` to the left
555 // and copy `a` to its position. if `a` fits perfectly, the
556 // shift will be of size 0.
557 // if `a` requires more space, shift `b` to the right and
558 // copy `a` to its position. this process may require multiple
559 // iterations and should be done chunk by chunk to prevent `b`
560 // from overlapping with `a`.
561 do
562 {
563 // clamp right shifts to prevent overlap with `a`
564 438 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
565 438 b = static_cast<char const*>(std::memmove(bp, b, bn));
566
567 // a chunk or all of `a` based on available space
568 438 auto chunk_a = static_cast<std::size_t>(b - dest);
569 438 std::memcpy(dest, a, chunk_a); // never overlap
570 438 an -= chunk_a;
571 438 dest += chunk_a;
572 438 a += chunk_a;
573
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 } while(an);
574 }
575
576 8570 break;
577 }
578 }
579
580 10298 ws_.clear();
581
582 20596 fb_ = {
583 10298 ws_.data(),
584
1/2
✓ Branch 1 taken 10298 times.
✗ Branch 2 not taken.
10298 svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
585 leftover };
586
587
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 10298 times.
10298 BOOST_ASSERT(
588 fb_.capacity() == svc_.max_overread() - leftover);
589
590 10298 h_ = detail::header(detail::empty{h_.kind});
591 10298 h_.buf = reinterpret_cast<char*>(ws_.data());
592 10298 h_.cbuf = h_.buf;
593 10298 h_.cap = ws_.size();
594
595
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10298 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10298 BOOST_ASSERT(! head_response ||
596 h_.kind == detail::kind::response);
597 10298 head_response_ = head_response;
598
599 // begin with in_place mode
600 10298 how_ = how::in_place;
601 10298 st_ = state::header;
602 10298 nprepare_ = 0;
603 10298 chunk_remain_ = 0;
604 10298 needs_chunk_close_ = false;
605 10298 trailer_headers_ = false;
606 10298 chunked_body_ended = false;
607 10298 filter_ = nullptr;
608 10298 sink_ = nullptr;
609 10298 body_avail_ = 0;
610 10298 body_total_ = 0;
611 10298 }
612
613 auto
614 51209 parser::
615 prepare() ->
616 mutable_buffers_type
617 {
618 51209 nprepare_ = 0;
619
620
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10378 times.
✓ Branch 3 taken 40828 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
51209 switch(st_)
621 {
622 1 default:
623 case state::reset:
624 // reset must be called first
625 1 detail::throw_logic_error();
626
627 1 case state::start:
628 // start must be called first
629 1 detail::throw_logic_error();
630
631 10378 case state::header:
632 {
633
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10378 times.
10378 BOOST_ASSERT(
634 h_.size < svc_.cfg.headers.max_size);
635 10378 std::size_t n = fb_.capacity() - fb_.size();
636
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10378 times.
10378 BOOST_ASSERT(n <= svc_.max_overread());
637 10378 n = clamp(n, svc_.cfg.max_prepare);
638 10378 mbp_[0] = fb_.prepare(n);
639 10378 nprepare_ = n;
640 10378 return mutable_buffers_type(&mbp_[0], 1);
641 }
642
643 40828 case state::body:
644 {
645
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40828 times.
40828 if(got_eof_)
646 {
647 // forgot to call parse()
648 detail::throw_logic_error();
649 }
650
651
2/2
✓ Branch 1 taken 21778 times.
✓ Branch 2 taken 19050 times.
40828 if(! is_plain())
652 {
653 // buffered payload
654 21778 std::size_t n = cb0_.capacity();
655 21778 n = clamp(n, svc_.cfg.max_prepare);
656 21778 nprepare_ = n;
657 21778 mbp_ = cb0_.prepare(n);
658 21778 return mutable_buffers_type(mbp_);
659 }
660 else
661 {
662
2/2
✓ Branch 0 taken 19029 times.
✓ Branch 1 taken 21 times.
19050 switch(how_)
663 {
664 19029 default:
665 case how::in_place:
666 case how::sink:
667 {
668 19029 std::size_t n = cb0_.capacity();
669 19029 n = clamp(n, svc_.cfg.max_prepare);
670
671
2/2
✓ Branch 0 taken 19004 times.
✓ Branch 1 taken 25 times.
19029 if(h_.md.payload == payload::size)
672 {
673
2/2
✓ Branch 0 taken 17797 times.
✓ Branch 1 taken 1207 times.
19004 if(n > payload_remain_)
674 {
675 17797 std::size_t overread =
676 17797 n - static_cast<std::size_t>(payload_remain_);
677
2/2
✓ Branch 1 taken 7877 times.
✓ Branch 2 taken 9920 times.
17797 if(overread > svc_.max_overread())
678 7877 n = static_cast<std::size_t>(payload_remain_) +
679 7877 svc_.max_overread();
680 }
681 }
682 else
683 {
684
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 BOOST_ASSERT(
685 h_.md.payload == payload::to_eof);
686
687 25 n = clamp(
688 25 svc_.cfg.body_limit - body_total_ + 1, n);
689 }
690
691 19029 nprepare_ = n;
692 19029 mbp_ = cb0_.prepare(n);
693 19029 return mutable_buffers_type(mbp_);
694 }
695 21 case how::elastic:
696 {
697
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
21 BOOST_ASSERT(cb0_.size() == 0);
698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
21 BOOST_ASSERT(body_avail_ == 0);
699
700 21 std::size_t n = svc_.cfg.min_buffer;
701
702
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
21 if(h_.md.payload == payload::size)
703 {
704 // Overreads are not allowed, or
705 // else the caller will see extra
706 // unrelated data.
707 6 n = clamp(payload_remain_, n);
708 }
709 else
710 {
711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
15 BOOST_ASSERT(
712 h_.md.payload == payload::to_eof);
713 15 n = clamp(
714 15 svc_.cfg.body_limit - body_total_, n);
715 15 n = clamp(
716 15 n, eb_->max_size() - eb_->size());
717 // fill capacity first to avoid an allocation
718 std::size_t avail =
719 15 eb_->capacity() - eb_->size();
720
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if(avail != 0)
721 15 n = clamp(n, avail);
722
723
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 14 times.
15 if(n == 0)
724 {
725 // dynamic buffer is full
726 // attempt a 1 byte read so
727 // we can detect overflow
728 1 nprepare_ = 1;
729 1 mbp_ = cb0_.prepare(1);
730 1 return mutable_buffers_type(mbp_);
731 }
732 }
733
734 20 n = clamp(n, svc_.cfg.max_prepare);
735
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 BOOST_ASSERT(n != 0);
736 20 nprepare_ = n;
737 20 return eb_->prepare(n);
738 }
739 }
740 }
741 }
742
743 case state::set_body:
744 // forgot to call parse()
745 detail::throw_logic_error();
746
747 1 case state::complete_in_place:
748 case state::complete:
749 // already complete
750 1 detail::throw_logic_error();
751 }
752 }
753
754 void
755 51206 parser::
756 commit(
757 std::size_t n)
758 {
759
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 51202 times.
51206 if(n > nprepare_)
760 {
761 // n can't be greater than size of
762 // the buffers returned by prepare()
763 4 detail::throw_invalid_argument();
764 }
765
766
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 51201 times.
51202 if(got_eof_)
767 {
768 // can't commit after EOF
769 1 detail::throw_logic_error();
770 }
771
772
4/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 10376 times.
✓ Branch 3 taken 40823 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
51201 switch(st_)
773 {
774 1 default:
775 case state::reset:
776 {
777 // reset must be called first
778 1 detail::throw_logic_error();
779 }
780
781 1 case state::start:
782 {
783 // forgot to call start()
784 1 detail::throw_logic_error();
785 }
786
787 10376 case state::header:
788 {
789 10376 nprepare_ = 0; // invalidate
790 10376 fb_.commit(n);
791 10376 break;
792 }
793
794 40823 case state::body:
795 {
796 40823 nprepare_ = 0; // invalidate
797
6/6
✓ Branch 1 taken 19045 times.
✓ Branch 2 taken 21778 times.
✓ Branch 3 taken 20 times.
✓ Branch 4 taken 19025 times.
✓ Branch 5 taken 20 times.
✓ Branch 6 taken 40803 times.
40823 if(is_plain() && how_ == how::elastic)
798 {
799
2/2
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
20 if(eb_->max_size() == eb_->size())
800 {
801 // borrowed 1 byte from cb0_
802
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
803 1 cb0_.commit(n);
804 1 break;
805 }
806 19 eb_->commit(n);
807 19 payload_remain_ -= n;
808 19 body_total_ += n;
809 }
810 else
811 {
812 40803 cb0_.commit(n);
813 }
814 40822 break;
815 }
816
817 case state::set_body:
818 {
819 // forgot to call parse()
820 detail::throw_logic_error();
821 }
822
823 case state::complete_in_place:
824 case state::complete:
825 {
826 // already complete
827 detail::throw_logic_error();
828 }
829 }
830 51199 }
831
832 void
833 402 parser::
834 commit_eof()
835 {
836 402 nprepare_ = 0; // invalidate
837
838
5/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 368 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
402 switch(st_)
839 {
840 1 default:
841 case state::reset:
842 // reset must be called first
843 1 detail::throw_logic_error();
844
845 1 case state::start:
846 // forgot to call prepare()
847 1 detail::throw_logic_error();
848
849 31 case state::header:
850 31 got_eof_ = true;
851 31 break;
852
853 368 case state::body:
854 368 got_eof_ = true;
855 368 break;
856
857 case state::set_body:
858 // forgot to call parse()
859 detail::throw_logic_error();
860
861 1 case state::complete_in_place:
862 case state::complete:
863 // can't commit eof when complete
864 1 detail::throw_logic_error();
865 }
866 399 }
867
868 //-----------------------------------------------
869
870 // process input data then
871 // eof if input data runs out.
872 void
873 57081 parser::
874 parse(
875 system::error_code& ec)
876 {
877 57081 ec = {};
878
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 14393 times.
✓ Branch 3 taken 41188 times.
✓ Branch 4 taken 922 times.
✓ Branch 5 taken 576 times.
57081 switch(st_)
879 {
880 1 default:
881 case state::reset:
882 // reset must be called first
883 1 detail::throw_logic_error();
884
885 1 case state::start:
886 // start must be called first
887 1 detail::throw_logic_error();
888
889 14393 case state::header:
890 {
891
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14393 times.
14393 BOOST_ASSERT(h_.buf == static_cast<
892 void const*>(ws_.data()));
893
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14393 times.
14393 BOOST_ASSERT(h_.cbuf == static_cast<
894 void const*>(ws_.data()));
895
896 14393 h_.parse(fb_.size(), svc_.cfg.headers, ec);
897
898
2/2
✓ Branch 2 taken 4132 times.
✓ Branch 3 taken 10261 times.
14393 if(ec == condition::need_more_input)
899 {
900
2/2
✓ Branch 0 taken 4105 times.
✓ Branch 1 taken 27 times.
4132 if(! got_eof_)
901 {
902 // headers incomplete
903 4105 return;
904 }
905
906
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 15 times.
27 if(fb_.size() == 0)
907 {
908 // stream closed cleanly
909 12 st_ = state::complete_in_place;
910 24 ec = BOOST_HTTP_PROTO_ERR(
911 error::end_of_stream);
912 12 return;
913 }
914
915 // stream closed with a
916 // partial message received
917 15 st_ = state::reset;
918 30 ec = BOOST_HTTP_PROTO_ERR(
919 error::incomplete);
920 15 return;
921 }
922
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 10002 times.
10261 if(ec.failed())
923 {
924 // other error,
925 //
926 // VFALCO map this to a bad
927 // request or bad response error?
928 //
929 259 st_ = state::reset; // unrecoverable
930 259 return;
931 }
932
933 // headers are complete
934 10002 on_headers(ec);
935
2/2
✓ Branch 1 taken 180 times.
✓ Branch 2 taken 9822 times.
10002 if(ec.failed())
936 180 return;
937
2/2
✓ Branch 0 taken 921 times.
✓ Branch 1 taken 8901 times.
9822 if(st_ == state::complete_in_place)
938 921 break;
939
940 BOOST_FALLTHROUGH;
941 }
942
943 case state::body:
944 {
945 8901 do_body:
946
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50367 times.
50367 BOOST_ASSERT(st_ == state::body);
947
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50367 times.
50367 BOOST_ASSERT(h_.md.payload != payload::none);
948
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50367 times.
50367 BOOST_ASSERT(h_.md.payload != payload::error);
949
950 8796 auto set_state_to_complete = [&]()
951 {
952
2/2
✓ Branch 0 taken 8558 times.
✓ Branch 1 taken 238 times.
8796 if(how_ == how::in_place)
953 {
954 8558 st_ = state::complete_in_place;
955 8558 return;
956 }
957 238 st_ = state::complete;
958 50367 };
959
960
2/2
✓ Branch 0 taken 24247 times.
✓ Branch 1 taken 26120 times.
50367 if(h_.md.payload == payload::chunked)
961 {
962 for(;;)
963 {
964
2/2
✓ Branch 0 taken 129629 times.
✓ Branch 1 taken 1191 times.
130820 if(chunk_remain_ == 0
965
2/2
✓ Branch 0 taken 125484 times.
✓ Branch 1 taken 4145 times.
129629 && !chunked_body_ended)
966 {
967
2/2
✓ Branch 1 taken 129 times.
✓ Branch 2 taken 125355 times.
125484 if(cb0_.size() == 0)
968 {
969 258 ec = BOOST_HTTP_PROTO_ERR(
970 error::need_data);
971 19229 return;
972 }
973
974 125355 auto cs = chained_sequence(cb0_.data());
975
976
2/2
✓ Branch 0 taken 117014 times.
✓ Branch 1 taken 8341 times.
125355 if(needs_chunk_close_)
977 {
978 117014 parse_eol(cs, ec);
979
2/2
✓ Branch 1 taken 5 times.
✓ Branch 2 taken 117009 times.
117014 if(ec)
980 5 return;
981 }
982
2/2
✓ Branch 0 taken 4184 times.
✓ Branch 1 taken 4157 times.
8341 else if(trailer_headers_)
983 {
984 4184 skip_trailer_headers(cs, ec);
985
2/2
✓ Branch 1 taken 39 times.
✓ Branch 2 taken 4145 times.
4184 if(ec)
986 39 return;
987 4145 cb0_.consume(cb0_.size() - cs.size());
988 4145 chunked_body_ended = true;
989 8292 continue;
990 }
991
992 121166 auto chunk_size = parse_hex(cs, ec);
993
2/2
✓ Branch 1 taken 19039 times.
✓ Branch 2 taken 102127 times.
121166 if(ec)
994 19039 return;
995
996 // skip chunk extensions
997 102127 find_eol(cs, ec);
998
2/2
✓ Branch 1 taken 17 times.
✓ Branch 2 taken 102110 times.
102127 if(ec)
999 17 return;
1000
1001 102110 cb0_.consume(cb0_.size() - cs.size());
1002 102110 chunk_remain_ = chunk_size;
1003
1004 102110 needs_chunk_close_ = true;
1005
2/2
✓ Branch 0 taken 4147 times.
✓ Branch 1 taken 97963 times.
102110 if(chunk_remain_ == 0)
1006 {
1007 4147 needs_chunk_close_ = false;
1008 4147 trailer_headers_ = true;
1009 4147 continue;
1010 }
1011 }
1012
1013
6/6
✓ Branch 1 taken 2470 times.
✓ Branch 2 taken 100829 times.
✓ Branch 3 taken 325 times.
✓ Branch 4 taken 2145 times.
✓ Branch 5 taken 325 times.
✓ Branch 6 taken 102974 times.
103299 if(cb0_.size() == 0 && !chunked_body_ended)
1014 {
1015
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 325 times.
325 if(got_eof_)
1016 {
1017 ec = BOOST_HTTP_PROTO_ERR(
1018 error::incomplete);
1019 st_ = state::reset;
1020 return;
1021 }
1022
1023 650 ec = BOOST_HTTP_PROTO_ERR(
1024 error::need_data);
1025 325 return;
1026 }
1027
1028
2/2
✓ Branch 0 taken 56592 times.
✓ Branch 1 taken 46382 times.
102974 if(filter_)
1029 {
1030
1/2
✓ Branch 2 taken 56592 times.
✗ Branch 3 not taken.
56592 chunk_remain_ -= apply_filter(
1031 ec,
1032 clamp(chunk_remain_, cb0_.size()),
1033 56592 !chunked_body_ended);
1034
1035
6/6
✓ Branch 1 taken 56044 times.
✓ Branch 2 taken 548 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 56020 times.
✓ Branch 5 taken 572 times.
✓ Branch 6 taken 56020 times.
56592 if(ec || chunked_body_ended)
1036 572 return;
1037 }
1038 else
1039 {
1040 const std::size_t chunk_avail =
1041 46382 clamp(chunk_remain_, cb0_.size());
1042 const auto chunk =
1043 46382 buffers::prefix(cb0_.data(), chunk_avail);
1044
1045
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46382 times.
46382 if(svc_.cfg.body_limit - body_total_
1046 < chunk_avail)
1047 {
1048 ec = BOOST_HTTP_PROTO_ERR(
1049 error::body_too_large);
1050 st_ = state::reset;
1051 4121 return;
1052 }
1053
1054
1/4
✓ Branch 0 taken 46382 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
46382 switch(how_)
1055 {
1056 46382 case how::in_place:
1057 {
1058 46382 auto copied = buffers::buffer_copy(
1059
1/2
✓ Branch 2 taken 46382 times.
✗ Branch 3 not taken.
46382 cb1_.prepare(cb1_.capacity()),
1060 chunk);
1061 46382 chunk_remain_ -= copied;
1062 46382 body_avail_ += copied;
1063 46382 body_total_ += copied;
1064 46382 cb0_.consume(copied);
1065 46382 cb1_.commit(copied);
1066 46382 if(cb1_.capacity() == 0
1067
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)
1068 {
1069 ec = BOOST_HTTP_PROTO_ERR(
1070 error::in_place_overflow);
1071 return;
1072 }
1073 46382 break;
1074 }
1075 case how::sink:
1076 {
1077 auto sink_rs = sink_->write(
1078 chunk, !chunked_body_ended);
1079 chunk_remain_ -= sink_rs.bytes;
1080 body_total_ += sink_rs.bytes;
1081 cb0_.consume(sink_rs.bytes);
1082 if(sink_rs.ec.failed())
1083 {
1084 body_avail_ +=
1085 chunk_avail - sink_rs.bytes;
1086 ec = sink_rs.ec;
1087 st_ = state::reset;
1088 return;
1089 }
1090 break;
1091 }
1092 case how::elastic:
1093 {
1094 if(eb_->max_size() - eb_->size()
1095 < chunk_avail)
1096 {
1097 ec = BOOST_HTTP_PROTO_ERR(
1098 error::buffer_overflow);
1099 st_ = state::reset;
1100 return;
1101 }
1102 buffers::buffer_copy(
1103 eb_->prepare(chunk_avail),
1104 chunk);
1105 chunk_remain_ -= chunk_avail;
1106 body_total_ += chunk_avail;
1107 cb0_.consume(chunk_avail);
1108 eb_->commit(chunk_avail);
1109 break;
1110 }
1111 }
1112
1113
2/2
✓ Branch 0 taken 4121 times.
✓ Branch 1 taken 42261 times.
46382 if(chunked_body_ended)
1114 {
1115 4121 set_state_to_complete();
1116 4121 return;
1117 }
1118 }
1119 106573 }
1120 }
1121 else
1122 {
1123 // non-chunked payload
1124
1125 78360 const std::size_t payload_avail = [&]()
1126 {
1127 26120 auto ret = cb0_.size();
1128
2/2
✓ Branch 0 taken 24327 times.
✓ Branch 1 taken 1793 times.
26120 if(!filter_)
1129 24327 ret -= body_avail_;
1130
2/2
✓ Branch 0 taken 24241 times.
✓ Branch 1 taken 1879 times.
26120 if(h_.md.payload == payload::size)
1131 24241 return clamp(payload_remain_, ret);
1132 // payload::eof
1133 1879 return ret;
1134 26120 }();
1135
1136 78360 const bool is_complete = [&]()
1137 {
1138
2/2
✓ Branch 0 taken 24241 times.
✓ Branch 1 taken 1879 times.
26120 if(h_.md.payload == payload::size)
1139 24241 return payload_avail == payload_remain_;
1140 // payload::eof
1141 1879 return got_eof_;
1142 26120 }();
1143
1144
2/2
✓ Branch 0 taken 1793 times.
✓ Branch 1 taken 24327 times.
26120 if(filter_)
1145 {
1146 3586 payload_remain_ -= apply_filter(
1147
1/2
✓ Branch 1 taken 1793 times.
✗ Branch 2 not taken.
1793 ec, payload_avail, !is_complete);
1148
6/6
✓ Branch 1 taken 696 times.
✓ Branch 2 taken 1097 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 648 times.
✓ Branch 5 taken 1145 times.
✓ Branch 6 taken 648 times.
1793 if(ec || is_complete)
1149 1145 return;
1150 }
1151 else
1152 {
1153
3/4
✓ Branch 0 taken 23818 times.
✓ Branch 1 taken 249 times.
✓ Branch 2 taken 260 times.
✗ Branch 3 not taken.
24327 switch(how_)
1154 {
1155 23818 case how::in_place:
1156 {
1157 23818 payload_remain_ -= payload_avail;
1158 23818 body_avail_ += payload_avail;
1159 23818 body_total_ += payload_avail;
1160
5/6
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 23817 times.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 23817 times.
23818 if(cb0_.capacity() == 0 && !is_complete)
1161 {
1162 2 ec = BOOST_HTTP_PROTO_ERR(
1163 error::in_place_overflow);
1164 1 return;
1165 }
1166 23817 break;
1167 }
1168 249 case how::sink:
1169 {
1170 249 payload_remain_ -= payload_avail;
1171 249 body_total_ += payload_avail;
1172
1/2
✓ Branch 1 taken 249 times.
✗ Branch 2 not taken.
249 auto sink_rs = sink_->write(
1173 249 buffers::prefix(
1174 cb0_.data(),
1175 payload_avail),
1176 249 !is_complete);
1177 249 cb0_.consume(sink_rs.bytes);
1178
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 249 times.
249 if(sink_rs.ec.failed())
1179 {
1180 body_avail_ +=
1181 payload_avail - sink_rs.bytes;
1182 ec = sink_rs.ec;
1183 st_ = state::reset;
1184 return;
1185 }
1186 249 break;
1187 }
1188 260 case how::elastic:
1189 {
1190 // payload_remain_ and body_total_
1191 // are already updated in commit()
1192
1193
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 260 times.
260 if(cb0_.size() != 0)
1194 {
1195 // a non-empty cb0_ indicates that
1196 // the elastic buffer ran out of space
1197 ec = BOOST_HTTP_PROTO_ERR(
1198 error::buffer_overflow);
1199 st_ = state::reset;
1200 return;
1201 }
1202 260 break;
1203 }
1204 }
1205
1206
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 24325 times.
24326 if(body_total_ > svc_.cfg.body_limit)
1207 {
1208 2 ec = BOOST_HTTP_PROTO_ERR(
1209 error::body_too_large);
1210 1 st_ = state::reset;
1211 1 return;
1212 }
1213
1214
2/2
✓ Branch 0 taken 4675 times.
✓ Branch 1 taken 19650 times.
24325 if(is_complete)
1215 {
1216 4675 set_state_to_complete();
1217 4675 return;
1218 }
1219 }
1220
1221
4/4
✓ Branch 0 taken 19333 times.
✓ Branch 1 taken 965 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19332 times.
20298 if(h_.md.payload == payload::size && got_eof_)
1222 {
1223 2 ec = BOOST_HTTP_PROTO_ERR(
1224 error::incomplete);
1225 1 st_ = state::reset;
1226 1 return;
1227 }
1228
1229 40594 ec = BOOST_HTTP_PROTO_ERR(
1230 error::need_data);
1231 20297 return;
1232 }
1233
1234 break;
1235 }
1236
1237 922 case state::set_body:
1238 case state::complete_in_place:
1239 {
1240
2/2
✓ Branch 1 taken 874 times.
✓ Branch 2 taken 48 times.
922 auto& body_buf = is_plain() ? cb0_ : cb1_;
1241
1242
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 922 times.
922 BOOST_ASSERT(body_avail_ == body_buf.size());
1243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 922 times.
922 BOOST_ASSERT(body_total_ == body_avail_);
1244
1245
3/4
✓ Branch 0 taken 288 times.
✓ Branch 1 taken 312 times.
✓ Branch 2 taken 322 times.
✗ Branch 3 not taken.
922 switch(how_)
1246 {
1247 288 case how::in_place:
1248 288 return; // no-op
1249 312 case how::sink:
1250 {
1251
1/2
✓ Branch 1 taken 312 times.
✗ Branch 2 not taken.
312 auto rs = sink_->write(
1252 312 buffers::prefix(
1253 body_buf.data(),
1254 body_avail_),
1255 312 st_ == state::set_body);
1256 312 body_buf.consume(rs.bytes);
1257 312 body_avail_ -= rs.bytes;
1258
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 312 times.
312 if(rs.ec.failed())
1259 {
1260 ec = rs.ec;
1261 st_ = state::reset;
1262 return;
1263 }
1264 312 break;
1265 }
1266 322 case how::elastic:
1267 {
1268 322 if(eb_->max_size() - eb_->size()
1269
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 321 times.
322 < body_avail_)
1270 {
1271 2 ec = BOOST_HTTP_PROTO_ERR(
1272 error::buffer_overflow);
1273 1 return;
1274 }
1275 321 buffers::buffer_copy(
1276
1/2
✓ Branch 1 taken 321 times.
✗ Branch 2 not taken.
321 eb_->prepare(body_avail_),
1277 321 body_buf.data());
1278 321 body_buf.consume(body_avail_);
1279 321 eb_->commit(body_avail_);
1280 321 body_avail_ = 0;
1281 // TODO: expand cb_0 when possible?
1282 321 break;
1283 }
1284 }
1285
1286
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 355 times.
633 if(st_ == state::set_body)
1287 {
1288 278 st_ = state::body;
1289 278 goto do_body;
1290 }
1291
1292 355 st_ = state::complete;
1293 355 break;
1294 }
1295
1296 576 case state::complete:
1297 576 break;
1298 }
1299 }
1300
1301 //------------------------------------------------
1302
1303 auto
1304 41250 parser::
1305 pull_body() ->
1306 const_buffers_type
1307 {
1308
1/2
✓ Branch 0 taken 41250 times.
✗ Branch 1 not taken.
41250 switch(st_)
1309 {
1310 41250 case state::body:
1311 case state::complete_in_place:
1312 41250 cbp_ = buffers::prefix(
1313
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 22269 times.
41250 (is_plain() ? cb0_ : cb1_).data(),
1314 body_avail_);
1315 41250 return const_buffers_type(cbp_);
1316 default:
1317 detail::throw_logic_error();
1318 }
1319 }
1320
1321 void
1322 39606 parser::
1323 consume_body(std::size_t n)
1324 {
1325
1/2
✓ Branch 0 taken 39606 times.
✗ Branch 1 not taken.
39606 switch(st_)
1326 {
1327 39606 case state::body:
1328 case state::complete_in_place:
1329 39606 n = clamp(n, body_avail_);
1330
2/2
✓ Branch 1 taken 18981 times.
✓ Branch 2 taken 20625 times.
39606 (is_plain() ? cb0_ : cb1_).consume(n);
1331 39606 body_avail_ -= n;
1332 39606 return;
1333 default:
1334 detail::throw_logic_error();
1335 }
1336 }
1337
1338 core::string_view
1339 1968 parser::
1340 body() const noexcept
1341 {
1342
2/2
✓ Branch 0 taken 697 times.
✓ Branch 1 taken 1271 times.
1968 if(st_ == state::complete_in_place)
1343 {
1344
2/2
✓ Branch 1 taken 576 times.
✓ Branch 2 taken 121 times.
697 auto cbp = (is_plain() ? cb0_ : cb1_).data();
1345
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 697 times.
697 BOOST_ASSERT(cbp[1].size() == 0);
1346
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 697 times.
697 BOOST_ASSERT(cbp[0].size() == body_avail_);
1347 697 return core::string_view(
1348 697 static_cast<char const*>(cbp[0].data()),
1349 1394 body_avail_);
1350 }
1351 1271 return {};
1352 }
1353
1354 core::string_view
1355 parser::
1356 release_buffered_data() noexcept
1357 {
1358 return {};
1359 }
1360
1361 //------------------------------------------------
1362 //
1363 // Implementation
1364 //
1365 //------------------------------------------------
1366
1367 auto
1368 314 parser::
1369 safe_get_header() const ->
1370 detail::header const*
1371 {
1372 // headers must be received
1373
3/6
✓ Branch 1 taken 314 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 314 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 314 times.
628 if( ! got_header() ||
1374 314 fb_.size() == 0) // happens on eof
1375 detail::throw_logic_error();
1376
1377 314 return &h_;
1378 }
1379
1380 bool
1381 181216 parser::
1382 is_plain() const noexcept
1383 {
1384
2/2
✓ Branch 0 taken 171043 times.
✓ Branch 1 taken 10173 times.
352259 return ! filter_ &&
1385
2/2
✓ Branch 0 taken 86377 times.
✓ Branch 1 taken 84666 times.
352259 h_.md.payload != payload::chunked;
1386 }
1387
1388 // Called immediately after complete headers are received
1389 // to setup the circular buffers for subsequent operations.
1390 // We leave fb_ as-is to indicate whether any data was
1391 // received before eof.
1392 //
1393 void
1394 10002 parser::
1395 on_headers(
1396 system::error_code& ec)
1397 {
1398 // overread currently includes any and all octets that
1399 // extend beyond the current end of the header
1400 // this can include associated body octets for the
1401 // current message or octets of the next message in the
1402 // stream, e.g. pipelining is being used
1403 10002 auto const overread = fb_.size() - h_.size;
1404
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 10002 times.
10002 BOOST_ASSERT(
1405 overread <= svc_.max_overread());
1406
1407 // metadata error
1408
2/2
✓ Branch 0 taken 180 times.
✓ Branch 1 taken 9822 times.
10002 if(h_.md.payload == payload::error)
1409 {
1410 // VFALCO This needs looking at
1411 360 ec = BOOST_HTTP_PROTO_ERR(
1412 error::bad_payload);
1413 180 st_ = state::reset; // unrecoverable
1414 180 return;
1415 }
1416
1417 // reserve headers + table
1418 9822 ws_.reserve_front(h_.size);
1419 9822 ws_.reserve_back(h_.table_space());
1420
1421 // no payload
1422
2/2
✓ Branch 0 taken 8901 times.
✓ Branch 1 taken 921 times.
9822 if( h_.md.payload == payload::none ||
1423
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8901 times.
8901 head_response_ )
1424 {
1425 // set cb0_ to overread
1426
1/2
✓ Branch 1 taken 921 times.
✗ Branch 2 not taken.
921 cb0_ = {
1427 921 ws_.data(),
1428 921 overread + fb_.capacity(),
1429 overread };
1430 921 st_ = state::complete_in_place;
1431 921 return;
1432 }
1433
1434 8901 auto cap = fb_.capacity() + overread +
1435 8901 svc_.cfg.min_buffer;
1436
1437 // reserve body buffers first, as the decoder
1438 // must be installed after them.
1439 8901 auto const p = ws_.reserve_front(cap);
1440
1441
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 8828 times.
8901 if( svc_.cfg.apply_deflate_decoder &&
1442
2/2
✓ Branch 0 taken 37 times.
✓ Branch 1 taken 36 times.
73 h_.md.content_encoding.encoding == encoding::deflate )
1443 {
1444 74 filter_ = &ws_.emplace<inflator_filter>(
1445
1/2
✓ Branch 1 taken 37 times.
✗ Branch 2 not taken.
37 ctx_, ws_, false);
1446 }
1447
2/2
✓ Branch 0 taken 36 times.
✓ Branch 1 taken 8828 times.
8864 else if( svc_.cfg.apply_gzip_decoder &&
1448
1/2
✓ Branch 0 taken 36 times.
✗ Branch 1 not taken.
36 h_.md.content_encoding.encoding == encoding::gzip )
1449 {
1450 72 filter_ = &ws_.emplace<inflator_filter>(
1451
1/2
✓ Branch 1 taken 36 times.
✗ Branch 2 not taken.
36 ctx_, ws_, true);
1452 }
1453 else
1454 {
1455 8828 cap += svc_.max_codec;
1456 8828 ws_.reserve_front(svc_.max_codec);
1457 }
1458
1459
2/2
✓ Branch 0 taken 4372 times.
✓ Branch 1 taken 4529 times.
8901 if(h_.md.payload == payload::size )
1460 4372 payload_remain_ = h_.md.payload_size;
1461
1462
2/2
✓ Branch 1 taken 4696 times.
✓ Branch 2 taken 4205 times.
8901 if(is_plain())
1463 {
1464
1/2
✓ Branch 1 taken 4696 times.
✗ Branch 2 not taken.
4696 cb0_ = { p, cap, overread };
1465
2/2
✓ Branch 0 taken 4348 times.
✓ Branch 1 taken 348 times.
4696 if(h_.md.payload == payload::size)
1466 {
1467 4348 if(h_.md.payload_size >
1468
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4348 times.
4348 svc_.cfg.body_limit)
1469 {
1470 ec = BOOST_HTTP_PROTO_ERR(
1471 error::body_too_large);
1472 st_ = state::reset;
1473 return;
1474 }
1475 }
1476 4696 st_ = state::body;
1477 4696 return;
1478 }
1479
1480 // buffered payload
1481
1482 8410 const auto n0 = (overread > svc_.cfg.min_buffer)
1483
2/2
✓ Branch 0 taken 4169 times.
✓ Branch 1 taken 36 times.
4205 ? overread
1484 4169 : svc_.cfg.min_buffer;
1485 4205 const auto n1 = svc_.cfg.min_buffer;
1486
1487
1/2
✓ Branch 1 taken 4205 times.
✗ Branch 2 not taken.
4205 cb0_ = { p , n0, overread };
1488 4205 cb1_ = { p + n0 , n1 };
1489 4205 st_ = state::body;
1490 }
1491
1492 // Called at the end of set_body
1493 void
1494 635 parser::
1495 on_set_body()
1496 {
1497 // This function is called after all
1498 // limit checking and calculation of
1499 // chunked or filter.
1500
1501
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 635 times.
635 BOOST_ASSERT(got_header());
1502
1503 635 nprepare_ = 0; // invalidate
1504
1505
2/2
✓ Branch 0 taken 278 times.
✓ Branch 1 taken 357 times.
635 if(st_ == state::body)
1506 {
1507 278 st_ = state::set_body;
1508 278 return;
1509 }
1510
1511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 357 times.
357 BOOST_ASSERT(
1512 st_ == state::complete_in_place);
1513 }
1514
1515 std::size_t
1516 58385 parser::
1517 apply_filter(
1518 system::error_code& ec,
1519 std::size_t payload_avail,
1520 bool more)
1521 {
1522 58385 std::size_t p0 = payload_avail;
1523
1524 for(;;)
1525 {
1526
4/4
✓ Branch 0 taken 56669 times.
✓ Branch 1 taken 60121 times.
✓ Branch 2 taken 56620 times.
✓ Branch 3 taken 49 times.
116790 if(payload_avail == 0 && more)
1527 58385 return p0 - payload_avail;
1528
1529 auto f_rs = [&](){
1530
2/2
✓ Branch 0 taken 19806 times.
✓ Branch 1 taken 40364 times.
60170 if(how_ == how::elastic)
1531 {
1532 19806 std::size_t n = clamp(
1533 19806 svc_.cfg.body_limit - body_total_);
1534 19806 n = clamp(n, svc_.cfg.min_buffer);
1535 19806 n = clamp(n, eb_->max_size() - eb_->size());
1536
1537 // fill capacity first to avoid
1538 // an allocation
1539 std::size_t avail =
1540 19806 eb_->capacity() - eb_->size();
1541
2/2
✓ Branch 0 taken 19801 times.
✓ Branch 1 taken 5 times.
19806 if(avail != 0)
1542 19801 n = clamp(n, avail);
1543
1544
1/2
✓ Branch 1 taken 19806 times.
✗ Branch 2 not taken.
19806 return filter_->process(
1545
1/2
✓ Branch 1 taken 19806 times.
✗ Branch 2 not taken.
19806 eb_->prepare(n),
1546 19806 buffers::prefix(
1547 19806 cb0_.data(),
1548 payload_avail),
1549 39612 more);
1550 }
1551 else // in-place and sink
1552 {
1553 40364 std::size_t n = clamp(
1554 40364 svc_.cfg.body_limit - body_total_);
1555 40364 n = clamp(n, cb1_.capacity());
1556
1557
1/2
✓ Branch 1 taken 40364 times.
✗ Branch 2 not taken.
40364 return filter_->process(
1558
1/2
✓ Branch 1 taken 40364 times.
✗ Branch 2 not taken.
40364 cb1_.prepare(n),
1559 40364 buffers::prefix(
1560 40364 cb0_.data(),
1561 payload_avail),
1562 80728 more);
1563 }
1564
1/2
✓ Branch 1 taken 60170 times.
✗ Branch 2 not taken.
60170 }();
1565
1566 60170 cb0_.consume(f_rs.in_bytes);
1567 60170 payload_avail -= f_rs.in_bytes;
1568 60170 body_total_ += f_rs.out_bytes;
1569
1570
2/2
✓ Branch 0 taken 60050 times.
✓ Branch 1 taken 120 times.
120220 bool needs_more_space = !f_rs.finished &&
1571
2/2
✓ Branch 0 taken 3453 times.
✓ Branch 1 taken 56597 times.
60050 payload_avail != 0;
1572
1573
3/4
✓ Branch 0 taken 20567 times.
✓ Branch 1 taken 19797 times.
✓ Branch 2 taken 19806 times.
✗ Branch 3 not taken.
60170 switch(how_)
1574 {
1575 20567 case how::in_place:
1576 {
1577 20567 cb1_.commit(f_rs.out_bytes);
1578 20567 body_avail_ += f_rs.out_bytes;
1579
6/6
✓ Branch 1 taken 1670 times.
✓ Branch 2 taken 18897 times.
✓ Branch 3 taken 1644 times.
✓ Branch 4 taken 26 times.
✓ Branch 5 taken 1644 times.
✓ Branch 6 taken 18923 times.
20567 if( cb1_.capacity() == 0 &&
1580 needs_more_space)
1581 {
1582 3288 ec = BOOST_HTTP_PROTO_ERR(
1583 error::in_place_overflow);
1584 1644 return p0 - payload_avail;
1585 }
1586 18923 break;
1587 }
1588 19797 case how::sink:
1589 {
1590 19797 cb1_.commit(f_rs.out_bytes);
1591
1/2
✓ Branch 1 taken 19797 times.
✗ Branch 2 not taken.
19797 auto sink_rs = sink_->write(
1592 19797 cb1_.data(), !f_rs.finished);
1593 19797 cb1_.consume(sink_rs.bytes);
1594
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19797 times.
19797 if(sink_rs.ec.failed())
1595 {
1596 ec = sink_rs.ec;
1597 st_ = state::reset;
1598 return p0 - payload_avail;
1599 }
1600 19797 break;
1601 }
1602 19806 case how::elastic:
1603 {
1604
1/2
✓ Branch 1 taken 19806 times.
✗ Branch 2 not taken.
19806 eb_->commit(f_rs.out_bytes);
1605
4/10
✓ Branch 1 taken 19806 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 19806 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 19806 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 19806 times.
19806 if( eb_->max_size() - eb_->size() == 0 &&
1606 needs_more_space)
1607 {
1608 ec = BOOST_HTTP_PROTO_ERR(
1609 error::buffer_overflow);
1610 st_ = state::reset;
1611 return p0 - payload_avail;
1612 }
1613 19806 break;
1614 }
1615 }
1616
1617
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 58525 times.
58526 if(f_rs.ec.failed())
1618 {
1619 1 ec = f_rs.ec;
1620 1 st_ = state::reset;
1621 1 return p0 - payload_avail;
1622 }
1623
1624
3/4
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 58495 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 30 times.
58525 if( svc_.cfg.body_limit == body_total_ &&
1625 needs_more_space)
1626 {
1627 ec = BOOST_HTTP_PROTO_ERR(
1628 error::body_too_large);
1629 st_ = state::reset;
1630 return p0 - payload_avail;
1631 }
1632
1633
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 58405 times.
58525 if(f_rs.finished)
1634 {
1635
2/2
✓ Branch 0 taken 72 times.
✓ Branch 1 taken 48 times.
120 if(!more)
1636 {
1637 72 st_ = (how_ == how::in_place)
1638
2/2
✓ Branch 0 taken 40 times.
✓ Branch 1 taken 32 times.
72 ? state::complete_in_place
1639 : state::complete;
1640 }
1641
1642 120 return p0 - payload_avail;
1643 }
1644 58405 }
1645 }
1646
1647 } // http_proto
1648 } // boost
1649