GCC Code Coverage Report


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