GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/parser.cpp
Date: 2024-09-20 16:11:52
Exec Total Coverage
Lines: 615 730 84.2%
Functions: 38 44 86.4%
Branches: 323 500 64.6%

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