GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/parser.cpp
Date: 2024-07-12 15:42:46
Exec Total Coverage
Lines: 591 726 81.4%
Functions: 34 41 82.9%
Branches: 306 487 62.8%

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 bool& needs_chunk_close_)
326 {
327
2/2
✓ Branch 1 taken 72 times.
✓ Branch 2 taken 166 times.
238 if( input.size() == 0 )
328 {
329 72 ec = error::need_data;
330 72 return false;
331 }
332
333 350 for(;;)
334 {
335
2/2
✓ Branch 0 taken 403 times.
✓ Branch 1 taken 113 times.
516 if( chunk_remain_ == 0 )
336 {
337
2/2
✓ Branch 0 taken 155 times.
✓ Branch 1 taken 248 times.
403 if( needs_chunk_close_ )
338 {
339
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 149 times.
155 if( input.size() < 2 )
340 {
341 6 ec = error::need_data;
342 9 return false;
343 }
344
345 149 std::size_t const crlf_len = 2;
346 149 char tmp[crlf_len] = {};
347
348 149 buffers::buffer_copy(
349 149 buffers::mutable_buffer(
350 tmp, crlf_len),
351 149 input.data());
352
353 149 core::string_view str(tmp, crlf_len);
354
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 146 times.
149 if( str != "\r\n" )
355 {
356 3 ec = error::bad_payload;
357 3 return false;
358 }
359
360 146 input.consume(crlf_len);
361 146 needs_chunk_close_ = false;
362 146 continue;
363 146 }
364
365 248 parse_chunk_header(input, ec, chunk_remain_);
366
2/2
✓ Branch 1 taken 98 times.
✓ Branch 2 taken 150 times.
248 if( ec )
367 {
368 98 system::error_code ec2;
369 98 parse_last_chunk(input, ec2);
370
2/2
✓ Branch 1 taken 25 times.
✓ Branch 2 taken 73 times.
98 if( ec2 )
371 {
372
2/2
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 12 times.
25 if( ec2 == condition::need_more_input )
373 13 ec = ec2;
374 25 return false;
375 }
376
377 // complete
378 73 ec.clear();
379 73 return true;
380 }
381
382 150 needs_chunk_close_ = true;
383 }
384
385 // we've successfully parsed a chunk-size and have
386 // consume()d the entire buffer
387
2/2
✓ Branch 1 taken 59 times.
✓ Branch 2 taken 204 times.
263 if( input.size() == 0 )
388 {
389 59 ec = error::need_data;
390 59 return false;
391 }
392
393 // TODO: this is an open-ended design space with no
394 // clear answer at time of writing.
395 // revisit this later
396
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 204 times.
204 if( output.capacity() == 0 )
397 detail::throw_length_error();
398
399 204 auto n = (std::min)(chunk_remain_, input.size());
400
401 204 auto m = buffers::buffer_copy(
402
1/2
✓ Branch 2 taken 204 times.
✗ Branch 3 not taken.
204 output.prepare(output.capacity()),
403 204 buffers::prefix(input.data(), n));
404
405
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
204 BOOST_ASSERT(m <= chunk_remain_);
406 204 chunk_remain_ -= m;
407 204 input.consume(m);
408 204 output.commit(m);
409 }
410 return false;
411 }
412
413 //-----------------------------------------------
414
415 class parser_service
416 : public service
417 {
418 public:
419 parser::config_base cfg;
420 std::size_t space_needed = 0;
421 std::size_t max_codec = 0;
422 zlib::detail::deflate_decoder_service const*
423 deflate_svc = nullptr;
424
425 parser_service(
426 context& ctx,
427 parser::config_base const& cfg_);
428
429 std::size_t
430 9215 max_overread() const noexcept
431 {
432 return
433 9215 cfg.headers.max_size +
434 9215 cfg.min_buffer;
435 }
436 };
437
438 33 parser_service::
439 parser_service(
440 context& ctx,
441 33 parser::config_base const& cfg_)
442 33 : cfg(cfg_)
443 {
444 /*
445 | fb | cb0 | cb1 | C | T | f |
446
447 fb flat_buffer headers.max_size
448 cb0 circular_buffer min_buffer
449 cb1 circular_buffer min_buffer
450 C codec max_codec
451 T body max_type_erase
452 f table max_table_space
453
454 */
455 // validate
456 //if(cfg.min_prepare > cfg.max_prepare)
457 //detail::throw_invalid_argument();
458
459
1/2
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
33 if( cfg.min_buffer < 1 ||
460
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 cfg.min_buffer > cfg.body_limit)
461 detail::throw_invalid_argument();
462
463
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 if(cfg.max_prepare < 1)
464 detail::throw_invalid_argument();
465
466 // VFALCO TODO OVERFLOW CHECING
467 {
468 //fb_.size() - h_.size +
469 //svc_.cfg.min_buffer +
470 //svc_.cfg.min_buffer +
471 //svc_.max_codec;
472 }
473
474 // VFALCO OVERFLOW CHECKING ON THIS
475 33 space_needed +=
476
1/2
✓ Branch 1 taken 33 times.
✗ Branch 2 not taken.
33 cfg.headers.valid_space_needed();
477
478 // cb0_, cb1_
479 // VFALCO OVERFLOW CHECKING ON THIS
480 33 space_needed +=
481 33 cfg.min_buffer +
482 cfg.min_buffer;
483
484 // T
485 33 space_needed += cfg.max_type_erase;
486
487 // max_codec
488 {
489
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 32 times.
33 if(cfg.apply_deflate_decoder)
490 {
491 1 deflate_svc = &ctx.get_service<
492
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 zlib::detail::deflate_decoder_service>();
493 auto const n =
494 1 deflate_svc->space_needed();
495
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if( max_codec < n)
496 max_codec = n;
497 }
498 }
499 33 space_needed += max_codec;
500
501 // round up to alignof(detail::header::entry)
502 33 auto const al = alignof(
503 detail::header::entry);
504 33 space_needed = al * ((
505 33 space_needed + al - 1) / al);
506 33 }
507
508 void
509 33 install_parser_service(
510 context& ctx,
511 parser::config_base const& cfg)
512 {
513 ctx.make_service<
514 33 parser_service>(cfg);
515 33 }
516
517 //------------------------------------------------
518 //
519 // Special Members
520 //
521 //------------------------------------------------
522
523 1045 parser::
524 parser(
525 context& ctx,
526 1045 detail::kind k)
527 1045 : ctx_(ctx)
528 1045 , svc_(ctx.get_service<
529 1045 parser_service>())
530 1045 , h_(detail::empty{k})
531 1045 , eb_(nullptr)
532 2090 , st_(state::reset)
533 {
534 1045 auto const n =
535 1045 svc_.space_needed;
536
1/2
✓ Branch 1 taken 1045 times.
✗ Branch 2 not taken.
1045 ws_.allocate(n);
537 1045 h_.cap = n;
538 1045 }
539
540 //------------------------------------------------
541
542 1045 parser::
543 ~parser()
544 {
545 1045 }
546
547 //------------------------------------------------
548 //
549 // Modifiers
550 //
551 //------------------------------------------------
552
553 // prepare for a new stream
554 void
555 1600 parser::
556 reset() noexcept
557 {
558 1600 ws_.clear();
559 1600 eb_ = nullptr;
560 1600 st_ = state::start;
561 1600 got_eof_ = false;
562 1600 }
563
564 void
565 1830 parser::
566 start_impl(
567 bool head_response)
568 {
569 1830 std::size_t leftover = 0;
570
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_)
571 {
572 1 default:
573 case state::reset:
574 // reset must be called first
575 1 detail::throw_logic_error();
576
577 1585 case state::start:
578 // reset required on eof
579
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1585 times.
1585 if(got_eof_)
580 detail::throw_logic_error();
581 1585 break;
582
583 3 case state::header:
584
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
3 if(fb_.size() == 0)
585 {
586 // start() called twice
587 2 detail::throw_logic_error();
588 }
589 BOOST_FALLTHROUGH;
590
591 case state::body:
592 case state::set_body:
593 // current message is incomplete
594 2 detail::throw_logic_error();
595
596 240 case state::complete:
597 {
598 // remove partial body.
599
1/2
✓ Branch 0 taken 240 times.
✗ Branch 1 not taken.
240 if(body_buf_ == &cb0_)
600 240 cb0_.consume(static_cast<std::size_t>(body_avail_));
601
602
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 240 times.
240 if(cb0_.size() > 0)
603 {
604 // headers with no body
605 BOOST_ASSERT(h_.size > 0);
606 fb_.consume(h_.size);
607 leftover = fb_.size();
608 // move unused octets to front
609 buffers::buffer_copy(
610 buffers::mutable_buffer(
611 ws_.data(),
612 leftover),
613 fb_.data());
614 }
615 else
616 {
617 // leftover data after body
618 }
619 240 break;
620 }
621 }
622
623 1825 ws_.clear();
624
625 3650 fb_ = {
626 1825 ws_.data(),
627 1825 svc_.cfg.headers.max_size +
628 1825 svc_.cfg.min_buffer,
629 leftover };
630
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1825 times.
1825 BOOST_ASSERT(fb_.capacity() ==
631 svc_.max_overread());
632
633 3650 h_ = detail::header(
634 1825 detail::empty{h_.kind});
635 1825 h_.buf = reinterpret_cast<
636 1825 char*>(ws_.data());
637 1825 h_.cbuf = h_.buf;
638 1825 h_.cap = ws_.size();
639
640
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1825 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1825 BOOST_ASSERT(! head_response ||
641 h_.kind == detail::kind::response);
642 1825 head_response_ = head_response;
643
644 // begin with in_place mode
645 1825 how_ = how::in_place;
646 1825 st_ = state::header;
647 1825 nprepare_ = 0;
648 1825 chunk_remain_ = 0;
649 1825 needs_chunk_close_ = false;
650 1825 }
651
652 auto
653 5801 parser::
654 prepare() ->
655 mutable_buffers_type
656 {
657 5801 nprepare_ = 0;
658
659
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5589 times.
✓ Branch 3 taken 180 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 3 times.
5801 switch(st_)
660 {
661 1 default:
662 case state::reset:
663 // reset must be called first
664 1 detail::throw_logic_error();
665
666 1 case state::start:
667 // start must be called first
668 1 detail::throw_logic_error();
669
670 5589 case state::header:
671 {
672
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5589 times.
5589 BOOST_ASSERT(h_.size <
673 svc_.cfg.headers.max_size);
674 5589 auto n = fb_.capacity() - fb_.size();
675
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(n <= svc_.max_overread());
676
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 5560 times.
5589 if( n > svc_.cfg.max_prepare)
677 29 n = svc_.cfg.max_prepare;
678 5589 mbp_[0] = fb_.prepare(n);
679 5589 nprepare_ = n;
680 5589 return mutable_buffers_type(
681 11178 &mbp_[0], 1);
682 }
683
684 180 case state::body:
685 {
686
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 180 times.
180 if(got_eof_)
687 return mutable_buffers_type{};
688
689 180 do_body:
690
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 55 times.
204 if(! is_plain())
691 {
692 // buffered payload
693 149 auto n = cb0_.capacity() -
694 149 cb0_.size();
695
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 149 times.
149 if( n > svc_.cfg.max_prepare)
696 n = svc_.cfg.max_prepare;
697 149 mbp_ = cb0_.prepare(n);
698 149 nprepare_ = n;
699 149 return mutable_buffers_type(mbp_);
700 }
701
702 // plain payload
703
704
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 26 times.
55 if(how_ == how::in_place)
705 {
706 auto n =
707 29 body_buf_->capacity() -
708 29 body_buf_->size();
709
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 28 times.
29 if( n > svc_.cfg.max_prepare)
710 1 n = svc_.cfg.max_prepare;
711 29 mbp_ = body_buf_->prepare(n);
712 29 nprepare_ = n;
713 29 return mutable_buffers_type(mbp_);
714 }
715
716
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if(how_ == how::elastic)
717 {
718 // Overreads are not allowed, or
719 // else the caller will see extra
720 // unrelated data.
721
722
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 17 times.
26 if(h_.md.payload == payload::size)
723 {
724 // set_body moves avail to dyn
725
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(body_buf_->size() == 0);
726
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(body_avail_ == 0);
727 9 auto n = static_cast<std::size_t>(payload_remain_);
728
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if( n > svc_.cfg.max_prepare)
729 1 n = svc_.cfg.max_prepare;
730 9 nprepare_ = n;
731 9 return eb_->prepare(n);
732 }
733
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 BOOST_ASSERT(
735 h_.md.payload == payload::to_eof);
736 17 std::size_t n = 0;
737
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if(! got_eof_)
738 {
739 // calculate n heuristically
740 17 n = svc_.cfg.min_buffer;
741
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 if( n > svc_.cfg.max_prepare)
742 1 n = svc_.cfg.max_prepare;
743 {
744 // apply max_size()
745 auto avail =
746 17 eb_->max_size() -
747 17 eb_->size();
748
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if( n > avail)
749 8 n = avail;
750 }
751 // fill capacity() first,
752 // to avoid an allocation
753 {
754 auto avail =
755 17 eb_->capacity() -
756 17 eb_->size();
757
3/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
17 if( n > avail &&
758 avail != 0)
759 1 n = avail;
760 }
761
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if(n == 0)
762 {
763 // dynamic buffer is full
764 // attempt a 1 byte read so
765 // we can detect overflow
766
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 BOOST_ASSERT(
767 body_buf_->size() == 0);
768 // handled in init_dynamic
769
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
770 body_avail_ == 0);
771 2 mbp_ = body_buf_->prepare(1);
772 2 nprepare_ = 1;
773 return
774 2 mutable_buffers_type(mbp_);
775 }
776 }
777 15 nprepare_ = n;
778 15 return eb_->prepare(n);
779 }
780
781 // VFALCO TODO
782 if(how_ == how::pull)
783 detail::throw_logic_error();
784
785 // VFALCO TODO
786 detail::throw_logic_error();
787 }
788
789 27 case state::set_body:
790 {
791
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if(how_ == how::elastic)
792 {
793 // attempt to transfer in-place
794 // body into the dynamic buffer.
795 27 system::error_code ec;
796
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 init_dynamic(ec);
797
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 times.
27 if(! ec.failed())
798 {
799
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
26 if(st_ == state::body)
800 24 goto do_body;
801
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
802 st_ == state::complete);
803 2 return mutable_buffers_type{};
804 }
805
806 // not enough room, so we
807 // return this error from parse()
808 return
809 1 mutable_buffers_type{};
810 }
811
812 if(how_ == how::sink)
813 {
814 // this is a no-op, to get the
815 // caller to call parse next.
816 return mutable_buffers_type{};
817 }
818
819 // VFALCO TODO
820 detail::throw_logic_error();
821 }
822
823 3 case state::complete:
824 // intended no-op
825 3 return mutable_buffers_type{};
826 }
827 }
828
829 void
830 5792 parser::
831 commit(
832 std::size_t n)
833 {
834
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5589 times.
✓ Branch 3 taken 195 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
5792 switch(st_)
835 {
836 1 default:
837 case state::reset:
838 {
839 // reset must be called first
840 1 detail::throw_logic_error();
841 }
842
843 1 case state::start:
844 {
845 // forgot to call start()
846 1 detail::throw_logic_error();
847 }
848
849 5589 case state::header:
850 {
851
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5588 times.
5589 if(n > nprepare_)
852 {
853 // n can't be greater than size of
854 // the buffers returned by prepare()
855 1 detail::throw_invalid_argument();
856 }
857
858
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5587 times.
5588 if(got_eof_)
859 {
860 // can't commit after EOF
861 1 detail::throw_logic_error();
862 }
863
864 5587 nprepare_ = 0; // invalidate
865 5587 fb_.commit(n);
866 5587 break;
867 }
868
869 195 case state::body:
870 {
871
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 194 times.
195 if(n > nprepare_)
872 {
873 // n can't be greater than size of
874 // the buffers returned by prepare()
875 1 detail::throw_invalid_argument();
876 }
877
878
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 194 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
194 BOOST_ASSERT(! got_eof_ || n == 0);
879
880
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 45 times.
194 if(! is_plain())
881 {
882 // buffered payload
883 149 cb0_.commit(n);
884 149 break;
885 }
886
887 // plain payload
888
889
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19 times.
45 if(how_ == how::in_place)
890 {
891
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 BOOST_ASSERT(body_buf_ == &cb0_);
892 26 cb0_.commit(n);
893
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 14 times.
26 if(h_.md.payload == payload::size)
894 {
895 12 if(cb0_.size() <
896
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 h_.md.payload_size)
897 {
898 4 body_avail_ += n;
899 4 payload_remain_ -= n;
900 4 break;
901 }
902 8 body_avail_ = h_.md.payload_size;
903 8 payload_remain_ = 0;
904 8 st_ = state::complete;
905 8 break;
906 }
907
908
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 BOOST_ASSERT(
909 h_.md.payload == payload::to_eof);
910 14 body_avail_ += n;
911 14 break;
912 }
913
914
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if(how_ == how::elastic)
915 {
916
2/2
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 1 times.
19 if(eb_->size() < eb_->max_size())
917 {
918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 BOOST_ASSERT(body_avail_ == 0);
919
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 BOOST_ASSERT(
920 body_buf_->size() == 0);
921 18 eb_->commit(n);
922 }
923 else
924 {
925 // If we get here then either
926 // n==0 as a no-op, or n==1 for
927 // an intended one byte read.
928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
929 1 body_buf_->commit(n);
930 1 body_avail_ += n;
931 }
932 19 body_total_ += n;
933
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
19 if(h_.md.payload == payload::size)
934 {
935
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(
936 n <= payload_remain_);
937 6 payload_remain_ -= n;
938
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(payload_remain_ == 0)
939 6 st_ = state::complete;
940 }
941 19 break;
942 }
943
944 if(how_ == how::sink)
945 {
946 cb0_.commit(n);
947 break;
948 }
949
950 if(how_ == how::pull)
951 {
952 // VFALCO TODO
953 detail::throw_logic_error();
954 }
955 break;
956 }
957
958 2 case state::set_body:
959 {
960
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(n > nprepare_)
961 {
962 // n can't be greater than size of
963 // the buffers returned by prepare()
964 1 detail::throw_invalid_argument();
965 }
966
967
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 BOOST_ASSERT(is_plain());
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n == 0);
969
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if( how_ == how::elastic ||
970 how_ == how::sink)
971 {
972 // intended no-op
973 break;
974 }
975
976 // VFALCO TODO
977 detail::throw_logic_error();
978 }
979
980 4 case state::complete:
981 {
982
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(nprepare_ == 0);
983
984
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(n > 0)
985 {
986 // n can't be greater than size of
987 // the buffers returned by prepare()
988 1 detail::throw_invalid_argument();
989 }
990
991 // intended no-op
992 3 break;
993 }
994 }
995 5785 }
996
997 void
998 363 parser::
999 commit_eof()
1000 {
1001 363 nprepare_ = 0; // invalidate
1002
1003
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_)
1004 {
1005 1 default:
1006 case state::reset:
1007 // reset must be called first
1008 1 detail::throw_logic_error();
1009
1010 1 case state::start:
1011 // forgot to call prepare()
1012 1 detail::throw_logic_error();
1013
1014 21 case state::header:
1015 21 got_eof_ = true;
1016 21 break;
1017
1018 127 case state::body:
1019 127 got_eof_ = true;
1020 127 break;
1021
1022 212 case state::set_body:
1023 212 got_eof_ = true;
1024 212 break;
1025
1026 1 case state::complete:
1027 // can't commit eof when complete
1028 1 detail::throw_logic_error();
1029 }
1030 360 }
1031
1032 //-----------------------------------------------
1033
1034 // process input data then
1035 // eof if input data runs out.
1036 void
1037 6700 parser::
1038 parse(
1039 system::error_code& ec)
1040 {
1041 6700 ec = {};
1042
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 5589 times.
✓ Branch 3 taken 306 times.
✓ Branch 4 taken 211 times.
✓ Branch 5 taken 592 times.
6700 switch(st_)
1043 {
1044 1 default:
1045 case state::reset:
1046 // reset must be called first
1047 1 detail::throw_logic_error();
1048
1049 1 case state::start:
1050 // start must be called first
1051 1 detail::throw_logic_error();
1052
1053 5589 case state::header:
1054 {
1055
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(h_.buf == static_cast<
1056 void const*>(ws_.data()));
1057
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(h_.cbuf == static_cast<
1058 void const*>(ws_.data()));
1059
1060 5589 h_.parse(fb_.size(), svc_.cfg.headers, ec);
1061
1062
2/2
✓ Branch 2 taken 3792 times.
✓ Branch 3 taken 1797 times.
5589 if(ec == condition::need_more_input)
1063 {
1064
2/2
✓ Branch 0 taken 3774 times.
✓ Branch 1 taken 18 times.
3792 if(! got_eof_)
1065 {
1066 // headers incomplete
1067 3774 return;
1068 }
1069
1070
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
18 if(fb_.size() == 0)
1071 {
1072 // stream closed cleanly
1073 8 st_ = state::complete;
1074 16 ec = BOOST_HTTP_PROTO_ERR(
1075 error::end_of_stream);
1076 8 return;
1077 }
1078
1079 // stream closed with a
1080 // partial message received
1081 10 st_ = state::reset;
1082 20 ec = BOOST_HTTP_PROTO_ERR(
1083 error::incomplete);
1084 10 return;
1085 }
1086
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 1538 times.
1797 if(ec.failed())
1087 {
1088 // other error,
1089 //
1090 // VFALCO map this to a bad
1091 // request or bad response error?
1092 //
1093 259 st_ = state::reset; // unrecoverable
1094 259 return;
1095 }
1096
1097 // headers are complete
1098 1538 on_headers(ec);
1099
2/2
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 1418 times.
1538 if(ec.failed())
1100 120 return;
1101
2/2
✓ Branch 0 taken 844 times.
✓ Branch 1 taken 574 times.
1418 if(st_ == state::complete)
1102 844 break;
1103
1104 BOOST_FALLTHROUGH;
1105 }
1106
1107 case state::body:
1108 {
1109 574 do_body:
1110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(st_ == state::body);
1111
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(
1112 h_.md.payload != payload::none);
1113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(
1114 h_.md.payload != payload::error);
1115
1116
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 744 times.
982 if( h_.md.payload == payload::chunked )
1117 {
1118 238 auto completed = false;
1119 238 auto& input = cb0_;
1120
1121
1/2
✓ Branch 0 taken 238 times.
✗ Branch 1 not taken.
238 if( how_ == how::in_place )
1122 {
1123 completed =
1124 238 parse_chunked(
1125 238 input, cb1_, ec, chunk_remain_,
1126 238 needs_chunk_close_);
1127 }
1128 else
1129 detail::throw_logic_error();
1130
1131
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 165 times.
238 if( completed )
1132 73 st_ = state::complete;
1133
1134 238 return;
1135 }
1136
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
744 else if( filt_ )
1137 {
1138 // VFALCO TODO apply filter
1139 detail::throw_logic_error();
1140 }
1141
1142
2/2
✓ Branch 0 taken 618 times.
✓ Branch 1 taken 126 times.
744 if(how_ == how::in_place)
1143 {
1144
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 618 times.
618 BOOST_ASSERT(body_avail_ ==
1145 body_buf_->size());
1146
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 363 times.
618 if(h_.md.payload == payload::size)
1147 {
1148 255 if(body_avail_ <
1149
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 225 times.
255 h_.md.payload_size)
1150 {
1151
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
30 if(got_eof_)
1152 {
1153 // incomplete
1154 2 ec = BOOST_HTTP_PROTO_ERR(
1155 error::incomplete);
1156 1 return;
1157 }
1158
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 28 times.
29 if(body_buf_->capacity() == 0)
1159 {
1160 // in_place buffer limit
1161 2 ec = BOOST_HTTP_PROTO_ERR(
1162 error::in_place_overflow);
1163 1 return;
1164 }
1165 56 ec = BOOST_HTTP_PROTO_ERR(
1166 error::need_data);
1167 28 return;
1168 }
1169
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 BOOST_ASSERT(body_avail_ ==
1170 h_.md.payload_size);
1171 225 st_ = state::complete;
1172 225 break;
1173 }
1174
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 362 times.
363 if(body_avail_ > svc_.cfg.body_limit)
1175 {
1176 2 ec = BOOST_HTTP_PROTO_ERR(
1177 error::body_too_large);
1178 1 st_ = state::reset; // unrecoverable
1179 1 return;
1180 }
1181
1/2
✓ Branch 0 taken 362 times.
✗ Branch 1 not taken.
362 if( h_.md.payload == payload::chunked ||
1182
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 114 times.
362 ! got_eof_)
1183 {
1184 496 ec = BOOST_HTTP_PROTO_ERR(
1185 error::need_data);
1186 248 return;
1187 }
1188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 BOOST_ASSERT(got_eof_);
1189 114 st_ = state::complete;
1190 114 break;
1191 }
1192
1193
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if(how_ == how::elastic)
1194 {
1195 // state already updated in commit
1196
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if(h_.md.payload == payload::size)
1197 {
1198 BOOST_ASSERT(body_total_ <
1199 h_.md.payload_size);
1200 BOOST_ASSERT(payload_remain_ > 0);
1201 if(body_avail_ != 0)
1202 {
1203 BOOST_ASSERT(
1204 eb_->max_size() -
1205 eb_->size() <
1206 payload_remain_);
1207 ec = BOOST_HTTP_PROTO_ERR(
1208 error::buffer_overflow);
1209 st_ = state::reset; // unrecoverable
1210 return;
1211 }
1212 if(got_eof_)
1213 {
1214 ec = BOOST_HTTP_PROTO_ERR(
1215 error::incomplete);
1216 st_ = state::reset; // unrecoverable
1217 return;
1218 }
1219 return;
1220 }
1221
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 BOOST_ASSERT(
1222 h_.md.payload == payload::to_eof);
1223
3/4
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 80 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 126 times.
172 if( eb_->size() == eb_->max_size() &&
1224
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 body_avail_ > 0)
1225 {
1226 // got here from the 1-byte read
1227 ec = BOOST_HTTP_PROTO_ERR(
1228 error::buffer_overflow);
1229 st_ = state::reset; // unrecoverable
1230 return;
1231 }
1232
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 13 times.
126 if(got_eof_)
1233 {
1234
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113 BOOST_ASSERT(body_avail_ == 0);
1235 113 st_ = state::complete;
1236 113 break;
1237 }
1238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 BOOST_ASSERT(body_avail_ == 0);
1239 13 break;
1240 }
1241
1242 // VFALCO TODO
1243 detail::throw_logic_error();
1244 }
1245
1246 211 case state::set_body:
1247 {
1248 // transfer in_place data into set body
1249
1250
1/2
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
211 if(how_ == how::elastic)
1251 {
1252 211 init_dynamic(ec);
1253
1/2
✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
211 if(! ec.failed())
1254 {
1255
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 109 times.
211 if(st_ == state::body)
1256 102 goto do_body;
1257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 BOOST_ASSERT(
1258 st_ == state::complete);
1259 109 break;
1260 }
1261 st_ = state::reset; // unrecoverable
1262 return;
1263 }
1264
1265 if(how_ == how::sink)
1266 {
1267 auto n = body_buf_->size();
1268 if(h_.md.payload == payload::size)
1269 {
1270 // sink_->size_hint(h_.md.payload_size, ec);
1271
1272 if(n < h_.md.payload_size)
1273 {
1274 auto rv = sink_->write(
1275 body_buf_->data(), false);
1276 BOOST_ASSERT(rv.ec.failed() ||
1277 rv.bytes == body_buf_->size());
1278 BOOST_ASSERT(
1279 rv.bytes >= body_avail_);
1280 BOOST_ASSERT(
1281 rv.bytes < payload_remain_);
1282 body_buf_->consume(rv.bytes);
1283 body_avail_ -= rv.bytes;
1284 body_total_ += rv.bytes;
1285 payload_remain_ -= rv.bytes;
1286 if(rv.ec.failed())
1287 {
1288 ec = rv.ec;
1289 st_ = state::reset; // unrecoverable
1290 return;
1291 }
1292 st_ = state::body;
1293 goto do_body;
1294 }
1295
1296 n = static_cast<std::size_t>(h_.md.payload_size);
1297 }
1298 // complete
1299 BOOST_ASSERT(body_buf_ == &cb0_);
1300 auto rv = sink_->write(
1301 body_buf_->data(), true);
1302 BOOST_ASSERT(rv.ec.failed() ||
1303 rv.bytes == body_buf_->size());
1304 body_buf_->consume(rv.bytes);
1305 if(rv.ec.failed())
1306 {
1307 ec = rv.ec;
1308 st_ = state::reset; // unrecoverable
1309 return;
1310 }
1311 st_ = state::complete;
1312 return;
1313 }
1314
1315 // VFALCO TODO
1316 detail::throw_logic_error();
1317 }
1318
1319 592 case state::complete:
1320 {
1321 // This is a no-op except when set_body
1322 // was called and we have in-place data.
1323
2/4
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
592 switch(how_)
1324 {
1325 296 default:
1326 case how::in_place:
1327 296 break;
1328
1329 296 case how::elastic:
1330 {
1331
1/2
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
296 if(body_buf_->size() == 0)
1332 296 break;
1333 BOOST_ASSERT(eb_->size() == 0);
1334 auto n = buffers::buffer_copy(
1335 eb_->prepare(
1336 body_buf_->size()),
1337 body_buf_->data());
1338 body_buf_->consume(n);
1339 break;
1340 }
1341
1342 case how::sink:
1343 {
1344 if(body_buf_->size() == 0)
1345 break;
1346 auto rv = sink_->write(
1347 body_buf_->data(), false);
1348 body_buf_->consume(rv.bytes);
1349 if(rv.ec.failed())
1350 {
1351 ec = rv.ec;
1352 st_ = state::reset; // unrecoverable
1353 return;
1354 }
1355 break;
1356 }
1357
1358 case how::pull:
1359 // VFALCO TODO
1360 detail::throw_logic_error();
1361 }
1362 }
1363 }
1364 }
1365
1366 //------------------------------------------------
1367
1368 auto
1369 parser::
1370 pull_some() ->
1371 const_buffers_type
1372 {
1373 return {};
1374 }
1375
1376 core::string_view
1377 1344 parser::
1378 body() const noexcept
1379 {
1380
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 995 times.
1344 switch(st_)
1381 {
1382 349 default:
1383 case state::reset:
1384 case state::start:
1385 case state::header:
1386 case state::body:
1387 case state::set_body:
1388 // not complete
1389 349 return {};
1390
1391 995 case state::complete:
1392
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 649 times.
995 if(how_ != how::in_place)
1393 {
1394 // not in_place
1395 346 return {};
1396 }
1397 649 auto cbp = body_buf_->data();
1398
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[1].size() == 0);
1399
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[0].size() >= body_avail_);
1400 1298 return core::string_view(
1401 static_cast<char const*>(
1402 649 cbp[0].data()),
1403 1298 cbp[0].size());
1404 }
1405 }
1406
1407 core::string_view
1408 parser::
1409 release_buffered_data() noexcept
1410 {
1411 return {};
1412 }
1413
1414 //------------------------------------------------
1415 //
1416 // Implementation
1417 //
1418 //------------------------------------------------
1419
1420 auto
1421 314 parser::
1422 safe_get_header() const ->
1423 detail::header const*
1424 {
1425 // headers must be received
1426
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() ||
1427 314 fb_.size() == 0) // happens on eof
1428 detail::throw_logic_error();
1429
1430 314 return &h_;
1431 }
1432
1433 bool
1434 973 parser::
1435 is_plain() const noexcept
1436 {
1437
1/2
✓ Branch 0 taken 973 times.
✗ Branch 1 not taken.
1946 return ! filt_ &&
1438
2/2
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 387 times.
973 h_.md.payload !=
1439 973 payload::chunked;
1440 }
1441
1442 // Called immediately after complete headers
1443 // are received. We leave fb_ as-is to indicate
1444 // whether any data was received before eof.
1445 //
1446 void
1447 1538 parser::
1448 on_headers(
1449 system::error_code& ec)
1450 {
1451 1538 auto const overread = fb_.size() - h_.size;
1452
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1538 times.
1538 BOOST_ASSERT(
1453 overread <= svc_.max_overread());
1454
1455 // metadata error
1456
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 1418 times.
1538 if(h_.md.payload == payload::error)
1457 {
1458 // VFALCO This needs looking at
1459 240 ec = BOOST_HTTP_PROTO_ERR(
1460 error::bad_payload);
1461 120 st_ = state::reset; // unrecoverable
1462 120 return;
1463 }
1464
1465 // reserve headers + table
1466 1418 ws_.reserve_front(h_.size);
1467 1418 ws_.reserve_back(h_.table_space());
1468
1469 // no payload
1470
2/2
✓ Branch 0 taken 574 times.
✓ Branch 1 taken 844 times.
1418 if( h_.md.payload == payload::none ||
1471
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 574 times.
574 head_response_)
1472 {
1473 // set cb0_ to overread
1474 1688 cb0_ = {
1475 844 ws_.data(),
1476 844 fb_.capacity() - h_.size,
1477 overread };
1478 844 body_avail_ = 0;
1479 844 body_total_ = 0;
1480 844 body_buf_ = &cb0_;
1481 844 st_ = state::complete;
1482 844 return;
1483 }
1484
1485 // calculate filter
1486 574 filt_ = nullptr; // VFALCO TODO
1487
1488
2/2
✓ Branch 1 taken 485 times.
✓ Branch 2 taken 89 times.
574 if(is_plain())
1489 {
1490 // plain payload
1491
2/2
✓ Branch 0 taken 250 times.
✓ Branch 1 taken 235 times.
485 if(h_.md.payload == payload::size)
1492 {
1493 250 if(h_.md.payload_size >
1494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
250 svc_.cfg.body_limit)
1495 {
1496 ec = BOOST_HTTP_PROTO_ERR(
1497 error::body_too_large);
1498 st_ = state::reset; // unrecoverable
1499 return;
1500 }
1501
1502 auto n0 =
1503 250 fb_.capacity() - h_.size +
1504 250 svc_.cfg.min_buffer +
1505 250 svc_.max_codec;
1506 // limit the capacity of cb0_ so
1507 // that going over max_overread
1508 // is impossible.
1509
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( n0 > h_.md.payload_size &&
1510 249 n0 - h_.md.payload_size >=
1511 249 svc_.max_overread())
1512 14 n0 = static_cast<std::size_t>(h_.md.payload_size) +
1513 14 svc_.max_overread();
1514
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 250 times.
250 BOOST_ASSERT(n0 <= ws_.size());
1515 250 cb0_ = { ws_.data(), n0, overread };
1516 250 body_buf_ = &cb0_;
1517 250 body_avail_ = cb0_.size();
1518
2/2
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 25 times.
250 if( body_avail_ >= h_.md.payload_size)
1519 225 body_avail_ = h_.md.payload_size;
1520 250 body_total_ = body_avail_;
1521 250 payload_remain_ =
1522 250 h_.md.payload_size - body_total_;
1523 250 st_ = state::body;
1524 250 return;
1525 }
1526
1527 // overread is not applicable
1528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
235 BOOST_ASSERT(
1529 h_.md.payload == payload::to_eof);
1530 auto const n0 =
1531 235 fb_.capacity() - h_.size +
1532 235 svc_.cfg.min_buffer +
1533 235 svc_.max_codec;
1534
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 235 times.
235 BOOST_ASSERT(n0 <= ws_.size());
1535 235 cb0_ = { ws_.data(), n0, overread };
1536 235 body_buf_ = &cb0_;
1537 235 body_avail_ = cb0_.size();
1538 235 body_total_ = body_avail_;
1539 235 st_ = state::body;
1540 235 return;
1541 }
1542
1543
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 if( h_.md.payload == payload::chunked )
1544 {
1545 auto const n0 =
1546 89 fb_.capacity() - fb_.size();
1547
1548 89 cb0_ = { ws_.data(), n0 / 2, overread };
1549 89 cb1_ = { ws_.data() + n0 / 2, n0 - (n0 / 2), 0 };
1550 89 body_buf_ = &cb1_;
1551 89 st_ = state::body;
1552 89 return;
1553 }
1554
1555 // buffered payload
1556 auto const n0 = fb_.capacity() - h_.size;
1557 BOOST_ASSERT(n0 <= svc_.max_overread());
1558 auto n1 = svc_.cfg.min_buffer;
1559 if(! filt_)
1560 n1 += svc_.max_codec;
1561 BOOST_ASSERT(n0 + n1 <= ws_.size());
1562 cb0_ = { ws_.data(), n0, overread };
1563 cb1_ = { ws_.data() + n0, n1 };
1564 body_buf_ = &cb1_;
1565 body_avail_ = 0;
1566 body_total_ = 0;
1567 st_ = state::body;
1568 }
1569
1570 // Called at the end of set_body
1571 void
1572 299 parser::
1573 on_set_body()
1574 {
1575 // This function is called after all
1576 // limit checking and calculation of
1577 // chunked or filter.
1578
1579
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
299 BOOST_ASSERT(got_header());
1580
1581 299 nprepare_ = 0; // invalidate
1582
1583
1/2
✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
299 if(how_ == how::elastic)
1584 {
1585
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 241 times.
299 if(h_.md.payload == payload::none)
1586 {
1587
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 BOOST_ASSERT(st_ == state::complete);
1588 58 return;
1589 }
1590
1591 241 st_ = state::set_body;
1592 241 return;
1593 }
1594
1595 if(how_ == how::sink)
1596 {
1597 if(h_.md.payload == payload::none)
1598 {
1599 BOOST_ASSERT(st_ == state::complete);
1600 // force a trip through parse so
1601 // we can calculate any error.
1602 st_ = state::set_body;
1603 return;
1604 }
1605
1606 st_ = state::set_body;
1607 return;
1608 }
1609
1610 // VFALCO TODO
1611 detail::throw_logic_error();
1612 }
1613
1614 void
1615 238 parser::
1616 init_dynamic(
1617 system::error_code& ec)
1618 {
1619 // attempt to transfer in-place
1620 // body into the dynamic buffer.
1621
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
238 BOOST_ASSERT(
1622 body_avail_ == body_buf_->size());
1623
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 BOOST_ASSERT(
1624 body_total_ == body_avail_);
1625 auto const space_left =
1626 238 eb_->max_size() - eb_->size();
1627
1628
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 117 times.
238 if(h_.md.payload == payload::size)
1629 {
1630
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 120 times.
121 if(space_left < h_.md.payload_size)
1631 {
1632 2 ec = BOOST_HTTP_PROTO_ERR(
1633 error::buffer_overflow);
1634 1 return;
1635 }
1636 // reserve the full size
1637 120 eb_->prepare(static_cast<std::size_t>(h_.md.payload_size));
1638 // transfer in-place body
1639 120 auto n = static_cast<std::size_t>(body_avail_);
1640
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if( n > h_.md.payload_size)
1641 n = static_cast<std::size_t>(h_.md.payload_size);
1642
1/2
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
120 eb_->commit(
1643 buffers::buffer_copy(
1644
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 eb_->prepare(n),
1645 120 body_buf_->data()));
1646
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_avail_ == n);
1647
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_total_ == n);
1648
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(payload_remain_ ==
1649 h_.md.payload_size - n);
1650 120 body_buf_->consume(n);
1651 120 body_avail_ = 0;
1652
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 111 times.
120 if(n < h_.md.payload_size)
1653 {
1654
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(
1655 body_buf_->size() == 0);
1656 9 st_ = state::body;
1657 9 return;
1658 }
1659 // complete
1660 111 st_ = state::complete;
1661 111 return;
1662 }
1663
1664
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 BOOST_ASSERT(h_.md.payload ==
1665 payload::to_eof);
1666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if(space_left < body_avail_)
1667 {
1668 ec = BOOST_HTTP_PROTO_ERR(
1669 error::buffer_overflow);
1670 return;
1671 }
1672
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 eb_->commit(
1673 buffers::buffer_copy(
1674
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 eb_->prepare(static_cast<std::size_t>(body_avail_)),
1675 117 body_buf_->data()));
1676 117 body_buf_->consume(static_cast<std::size_t>(body_avail_));
1677 117 body_avail_ = 0;
1678
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
117 BOOST_ASSERT(
1679 body_buf_->size() == 0);
1680 117 st_ = state::body;
1681 }
1682
1683 } // http_proto
1684 } // boost
1685