GCC Code Coverage Report


Directory: libs/http_proto/
File: src/parser.cpp
Date: 2024-12-31 05:34:49
Exec Total Coverage
Lines: 656 784 83.7%
Functions: 54 64 84.4%
Branches: 357 532 67.1%

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