GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/parser.cpp
Date: 2024-07-18 19:38:55
Exec Total Coverage
Lines: 594 729 81.5%
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 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 // move unused octets to front
611 buffers::buffer_copy(
612 buffers::mutable_buffer(
613 ws_.data(),
614 leftover),
615 fb_.data());
616 }
617 else
618 {
619 // leftover data after body
620 }
621 240 break;
622 }
623 }
624
625 1825 ws_.clear();
626
627 3650 fb_ = {
628 1825 ws_.data(),
629 1825 svc_.cfg.headers.max_size +
630 1825 svc_.cfg.min_buffer,
631 leftover };
632
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 1825 times.
1825 BOOST_ASSERT(fb_.capacity() ==
633 svc_.max_overread());
634
635 3650 h_ = detail::header(
636 1825 detail::empty{h_.kind});
637 1825 h_.buf = reinterpret_cast<
638 1825 char*>(ws_.data());
639 1825 h_.cbuf = h_.buf;
640 1825 h_.cap = ws_.size();
641
642
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1825 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1825 BOOST_ASSERT(! head_response ||
643 h_.kind == detail::kind::response);
644 1825 head_response_ = head_response;
645
646 // begin with in_place mode
647 1825 how_ = how::in_place;
648 1825 st_ = state::header;
649 1825 nprepare_ = 0;
650 1825 chunk_remain_ = 0;
651 1825 needs_chunk_close_ = false;
652 1825 body_avail_ = 0;
653 1825 }
654
655 auto
656 5801 parser::
657 prepare() ->
658 mutable_buffers_type
659 {
660 5801 nprepare_ = 0;
661
662
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_)
663 {
664 1 default:
665 case state::reset:
666 // reset must be called first
667 1 detail::throw_logic_error();
668
669 1 case state::start:
670 // start must be called first
671 1 detail::throw_logic_error();
672
673 5589 case state::header:
674 {
675
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5589 times.
5589 BOOST_ASSERT(h_.size <
676 svc_.cfg.headers.max_size);
677 5589 auto n = fb_.capacity() - fb_.size();
678
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(n <= svc_.max_overread());
679
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 5560 times.
5589 if( n > svc_.cfg.max_prepare)
680 29 n = svc_.cfg.max_prepare;
681 5589 mbp_[0] = fb_.prepare(n);
682 5589 nprepare_ = n;
683 5589 return mutable_buffers_type(
684 11178 &mbp_[0], 1);
685 }
686
687 180 case state::body:
688 {
689
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 180 times.
180 if(got_eof_)
690 return mutable_buffers_type{};
691
692 180 do_body:
693
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 55 times.
204 if(! is_plain())
694 {
695 // buffered payload
696 149 auto n = cb0_.capacity() -
697 149 cb0_.size();
698
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 149 times.
149 if( n > svc_.cfg.max_prepare)
699 n = svc_.cfg.max_prepare;
700 149 mbp_ = cb0_.prepare(n);
701 149 nprepare_ = n;
702 149 return mutable_buffers_type(mbp_);
703 }
704
705 // plain payload
706
707
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 26 times.
55 if(how_ == how::in_place)
708 {
709 auto n =
710 29 body_buf_->capacity() -
711 29 body_buf_->size();
712
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 28 times.
29 if( n > svc_.cfg.max_prepare)
713 1 n = svc_.cfg.max_prepare;
714 29 mbp_ = body_buf_->prepare(n);
715 29 nprepare_ = n;
716 29 return mutable_buffers_type(mbp_);
717 }
718
719
1/2
✓ Branch 0 taken 26 times.
✗ Branch 1 not taken.
26 if(how_ == how::elastic)
720 {
721 // Overreads are not allowed, or
722 // else the caller will see extra
723 // unrelated data.
724
725
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 17 times.
26 if(h_.md.payload == payload::size)
726 {
727 // set_body moves avail to dyn
728
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(body_buf_->size() == 0);
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(body_avail_ == 0);
730 9 auto n = static_cast<std::size_t>(payload_remain_);
731
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if( n > svc_.cfg.max_prepare)
732 1 n = svc_.cfg.max_prepare;
733 9 nprepare_ = n;
734 9 return eb_->prepare(n);
735 }
736
737
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 BOOST_ASSERT(
738 h_.md.payload == payload::to_eof);
739 17 std::size_t n = 0;
740
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if(! got_eof_)
741 {
742 // calculate n heuristically
743 17 n = svc_.cfg.min_buffer;
744
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 16 times.
17 if( n > svc_.cfg.max_prepare)
745 1 n = svc_.cfg.max_prepare;
746 {
747 // apply max_size()
748 auto avail =
749 17 eb_->max_size() -
750 17 eb_->size();
751
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 9 times.
17 if( n > avail)
752 8 n = avail;
753 }
754 // fill capacity() first,
755 // to avoid an allocation
756 {
757 auto avail =
758 17 eb_->capacity() -
759 17 eb_->size();
760
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 &&
761 avail != 0)
762 1 n = avail;
763 }
764
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 15 times.
17 if(n == 0)
765 {
766 // dynamic buffer is full
767 // attempt a 1 byte read so
768 // we can detect overflow
769
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 BOOST_ASSERT(
770 body_buf_->size() == 0);
771 // handled in init_dynamic
772
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
773 body_avail_ == 0);
774 2 mbp_ = body_buf_->prepare(1);
775 2 nprepare_ = 1;
776 return
777 2 mutable_buffers_type(mbp_);
778 }
779 }
780 15 nprepare_ = n;
781 15 return eb_->prepare(n);
782 }
783
784 // VFALCO TODO
785 if(how_ == how::pull)
786 detail::throw_logic_error();
787
788 // VFALCO TODO
789 detail::throw_logic_error();
790 }
791
792 27 case state::set_body:
793 {
794
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if(how_ == how::elastic)
795 {
796 // attempt to transfer in-place
797 // body into the dynamic buffer.
798 27 system::error_code ec;
799
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 init_dynamic(ec);
800
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 times.
27 if(! ec.failed())
801 {
802
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
26 if(st_ == state::body)
803 24 goto do_body;
804
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
805 st_ == state::complete);
806 2 return mutable_buffers_type{};
807 }
808
809 // not enough room, so we
810 // return this error from parse()
811 return
812 1 mutable_buffers_type{};
813 }
814
815 if(how_ == how::sink)
816 {
817 // this is a no-op, to get the
818 // caller to call parse next.
819 return mutable_buffers_type{};
820 }
821
822 // VFALCO TODO
823 detail::throw_logic_error();
824 }
825
826 3 case state::complete:
827 // intended no-op
828 3 return mutable_buffers_type{};
829 }
830 }
831
832 void
833 5792 parser::
834 commit(
835 std::size_t n)
836 {
837
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_)
838 {
839 1 default:
840 case state::reset:
841 {
842 // reset must be called first
843 1 detail::throw_logic_error();
844 }
845
846 1 case state::start:
847 {
848 // forgot to call start()
849 1 detail::throw_logic_error();
850 }
851
852 5589 case state::header:
853 {
854
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5588 times.
5589 if(n > nprepare_)
855 {
856 // n can't be greater than size of
857 // the buffers returned by prepare()
858 1 detail::throw_invalid_argument();
859 }
860
861
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 5587 times.
5588 if(got_eof_)
862 {
863 // can't commit after EOF
864 1 detail::throw_logic_error();
865 }
866
867 5587 nprepare_ = 0; // invalidate
868 5587 fb_.commit(n);
869 5587 break;
870 }
871
872 195 case state::body:
873 {
874
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 194 times.
195 if(n > nprepare_)
875 {
876 // n can't be greater than size of
877 // the buffers returned by prepare()
878 1 detail::throw_invalid_argument();
879 }
880
881
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);
882
883
2/2
✓ Branch 1 taken 149 times.
✓ Branch 2 taken 45 times.
194 if(! is_plain())
884 {
885 // buffered payload
886 149 cb0_.commit(n);
887 149 break;
888 }
889
890 // plain payload
891
892
2/2
✓ Branch 0 taken 26 times.
✓ Branch 1 taken 19 times.
45 if(how_ == how::in_place)
893 {
894
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 26 times.
26 BOOST_ASSERT(body_buf_ == &cb0_);
895 26 cb0_.commit(n);
896
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 14 times.
26 if(h_.md.payload == payload::size)
897 {
898 12 if(cb0_.size() <
899
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 8 times.
12 h_.md.payload_size)
900 {
901 4 body_avail_ += n;
902 4 payload_remain_ -= n;
903 4 break;
904 }
905 8 body_avail_ = h_.md.payload_size;
906 8 payload_remain_ = 0;
907 8 st_ = state::complete;
908 8 break;
909 }
910
911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 BOOST_ASSERT(
912 h_.md.payload == payload::to_eof);
913 14 body_avail_ += n;
914 14 break;
915 }
916
917
1/2
✓ Branch 0 taken 19 times.
✗ Branch 1 not taken.
19 if(how_ == how::elastic)
918 {
919
2/2
✓ Branch 2 taken 18 times.
✓ Branch 3 taken 1 times.
19 if(eb_->size() < eb_->max_size())
920 {
921
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 BOOST_ASSERT(body_avail_ == 0);
922
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 18 times.
18 BOOST_ASSERT(
923 body_buf_->size() == 0);
924 18 eb_->commit(n);
925 }
926 else
927 {
928 // If we get here then either
929 // n==0 as a no-op, or n==1 for
930 // an intended one byte read.
931
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
932 1 body_buf_->commit(n);
933 1 body_avail_ += n;
934 }
935 19 body_total_ += n;
936
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 13 times.
19 if(h_.md.payload == payload::size)
937 {
938
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(
939 n <= payload_remain_);
940 6 payload_remain_ -= n;
941
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(payload_remain_ == 0)
942 6 st_ = state::complete;
943 }
944 19 break;
945 }
946
947 if(how_ == how::sink)
948 {
949 cb0_.commit(n);
950 break;
951 }
952
953 if(how_ == how::pull)
954 {
955 // VFALCO TODO
956 detail::throw_logic_error();
957 }
958 break;
959 }
960
961 2 case state::set_body:
962 {
963
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(n > nprepare_)
964 {
965 // n can't be greater than size of
966 // the buffers returned by prepare()
967 1 detail::throw_invalid_argument();
968 }
969
970
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 BOOST_ASSERT(is_plain());
971
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n == 0);
972
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if( how_ == how::elastic ||
973 how_ == how::sink)
974 {
975 // intended no-op
976 break;
977 }
978
979 // VFALCO TODO
980 detail::throw_logic_error();
981 }
982
983 4 case state::complete:
984 {
985
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(nprepare_ == 0);
986
987
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(n > 0)
988 {
989 // n can't be greater than size of
990 // the buffers returned by prepare()
991 1 detail::throw_invalid_argument();
992 }
993
994 // intended no-op
995 3 break;
996 }
997 }
998 5785 }
999
1000 void
1001 363 parser::
1002 commit_eof()
1003 {
1004 363 nprepare_ = 0; // invalidate
1005
1006
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_)
1007 {
1008 1 default:
1009 case state::reset:
1010 // reset must be called first
1011 1 detail::throw_logic_error();
1012
1013 1 case state::start:
1014 // forgot to call prepare()
1015 1 detail::throw_logic_error();
1016
1017 21 case state::header:
1018 21 got_eof_ = true;
1019 21 break;
1020
1021 127 case state::body:
1022 127 got_eof_ = true;
1023 127 break;
1024
1025 212 case state::set_body:
1026 212 got_eof_ = true;
1027 212 break;
1028
1029 1 case state::complete:
1030 // can't commit eof when complete
1031 1 detail::throw_logic_error();
1032 }
1033 360 }
1034
1035 //-----------------------------------------------
1036
1037 // process input data then
1038 // eof if input data runs out.
1039 void
1040 6700 parser::
1041 parse(
1042 system::error_code& ec)
1043 {
1044 6700 ec = {};
1045
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_)
1046 {
1047 1 default:
1048 case state::reset:
1049 // reset must be called first
1050 1 detail::throw_logic_error();
1051
1052 1 case state::start:
1053 // start must be called first
1054 1 detail::throw_logic_error();
1055
1056 5589 case state::header:
1057 {
1058
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(h_.buf == static_cast<
1059 void const*>(ws_.data()));
1060
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 5589 times.
5589 BOOST_ASSERT(h_.cbuf == static_cast<
1061 void const*>(ws_.data()));
1062
1063 5589 h_.parse(fb_.size(), svc_.cfg.headers, ec);
1064
1065
2/2
✓ Branch 2 taken 3792 times.
✓ Branch 3 taken 1797 times.
5589 if(ec == condition::need_more_input)
1066 {
1067
2/2
✓ Branch 0 taken 3774 times.
✓ Branch 1 taken 18 times.
3792 if(! got_eof_)
1068 {
1069 // headers incomplete
1070 3774 return;
1071 }
1072
1073
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
18 if(fb_.size() == 0)
1074 {
1075 // stream closed cleanly
1076 8 st_ = state::complete;
1077 16 ec = BOOST_HTTP_PROTO_ERR(
1078 error::end_of_stream);
1079 8 return;
1080 }
1081
1082 // stream closed with a
1083 // partial message received
1084 10 st_ = state::reset;
1085 20 ec = BOOST_HTTP_PROTO_ERR(
1086 error::incomplete);
1087 10 return;
1088 }
1089
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 1538 times.
1797 if(ec.failed())
1090 {
1091 // other error,
1092 //
1093 // VFALCO map this to a bad
1094 // request or bad response error?
1095 //
1096 259 st_ = state::reset; // unrecoverable
1097 259 return;
1098 }
1099
1100 // headers are complete
1101 1538 on_headers(ec);
1102
2/2
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 1418 times.
1538 if(ec.failed())
1103 120 return;
1104
2/2
✓ Branch 0 taken 844 times.
✓ Branch 1 taken 574 times.
1418 if(st_ == state::complete)
1105 844 break;
1106
1107 BOOST_FALLTHROUGH;
1108 }
1109
1110 case state::body:
1111 {
1112 574 do_body:
1113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(st_ == state::body);
1114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(
1115 h_.md.payload != payload::none);
1116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 982 times.
982 BOOST_ASSERT(
1117 h_.md.payload != payload::error);
1118
1119
2/2
✓ Branch 0 taken 238 times.
✓ Branch 1 taken 744 times.
982 if( h_.md.payload == payload::chunked )
1120 {
1121 238 auto completed = false;
1122 238 auto& input = cb0_;
1123
1124
1/2
✓ Branch 0 taken 238 times.
✗ Branch 1 not taken.
238 if( how_ == how::in_place )
1125 {
1126 238 auto& output = cb1_;
1127 completed =
1128 238 parse_chunked(
1129 238 input, output, ec, chunk_remain_,
1130 238 body_avail_, needs_chunk_close_);
1131 }
1132 else
1133 detail::throw_logic_error();
1134
1135
2/2
✓ Branch 0 taken 73 times.
✓ Branch 1 taken 165 times.
238 if( completed )
1136 73 st_ = state::complete;
1137
1138 238 return;
1139 }
1140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 744 times.
744 else if( filt_ )
1141 {
1142 // VFALCO TODO apply filter
1143 detail::throw_logic_error();
1144 }
1145
1146
2/2
✓ Branch 0 taken 618 times.
✓ Branch 1 taken 126 times.
744 if(how_ == how::in_place)
1147 {
1148
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 618 times.
618 BOOST_ASSERT(body_avail_ ==
1149 body_buf_->size());
1150
2/2
✓ Branch 0 taken 255 times.
✓ Branch 1 taken 363 times.
618 if(h_.md.payload == payload::size)
1151 {
1152 255 if(body_avail_ <
1153
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 225 times.
255 h_.md.payload_size)
1154 {
1155
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 29 times.
30 if(got_eof_)
1156 {
1157 // incomplete
1158 2 ec = BOOST_HTTP_PROTO_ERR(
1159 error::incomplete);
1160 1 return;
1161 }
1162
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 28 times.
29 if(body_buf_->capacity() == 0)
1163 {
1164 // in_place buffer limit
1165 2 ec = BOOST_HTTP_PROTO_ERR(
1166 error::in_place_overflow);
1167 1 return;
1168 }
1169 56 ec = BOOST_HTTP_PROTO_ERR(
1170 error::need_data);
1171 28 return;
1172 }
1173
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 225 times.
225 BOOST_ASSERT(body_avail_ ==
1174 h_.md.payload_size);
1175 225 st_ = state::complete;
1176 225 break;
1177 }
1178
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 362 times.
363 if(body_avail_ > svc_.cfg.body_limit)
1179 {
1180 2 ec = BOOST_HTTP_PROTO_ERR(
1181 error::body_too_large);
1182 1 st_ = state::reset; // unrecoverable
1183 1 return;
1184 }
1185
1/2
✓ Branch 0 taken 362 times.
✗ Branch 1 not taken.
362 if( h_.md.payload == payload::chunked ||
1186
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 114 times.
362 ! got_eof_)
1187 {
1188 496 ec = BOOST_HTTP_PROTO_ERR(
1189 error::need_data);
1190 248 return;
1191 }
1192
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 BOOST_ASSERT(got_eof_);
1193 114 st_ = state::complete;
1194 114 break;
1195 }
1196
1197
1/2
✓ Branch 0 taken 126 times.
✗ Branch 1 not taken.
126 if(how_ == how::elastic)
1198 {
1199 // state already updated in commit
1200
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 if(h_.md.payload == payload::size)
1201 {
1202 BOOST_ASSERT(body_total_ <
1203 h_.md.payload_size);
1204 BOOST_ASSERT(payload_remain_ > 0);
1205 if(body_avail_ != 0)
1206 {
1207 BOOST_ASSERT(
1208 eb_->max_size() -
1209 eb_->size() <
1210 payload_remain_);
1211 ec = BOOST_HTTP_PROTO_ERR(
1212 error::buffer_overflow);
1213 st_ = state::reset; // unrecoverable
1214 return;
1215 }
1216 if(got_eof_)
1217 {
1218 ec = BOOST_HTTP_PROTO_ERR(
1219 error::incomplete);
1220 st_ = state::reset; // unrecoverable
1221 return;
1222 }
1223 return;
1224 }
1225
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 126 times.
126 BOOST_ASSERT(
1226 h_.md.payload == payload::to_eof);
1227
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() &&
1228
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 body_avail_ > 0)
1229 {
1230 // got here from the 1-byte read
1231 ec = BOOST_HTTP_PROTO_ERR(
1232 error::buffer_overflow);
1233 st_ = state::reset; // unrecoverable
1234 return;
1235 }
1236
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 13 times.
126 if(got_eof_)
1237 {
1238
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113 BOOST_ASSERT(body_avail_ == 0);
1239 113 st_ = state::complete;
1240 113 break;
1241 }
1242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
13 BOOST_ASSERT(body_avail_ == 0);
1243 13 break;
1244 }
1245
1246 // VFALCO TODO
1247 detail::throw_logic_error();
1248 }
1249
1250 211 case state::set_body:
1251 {
1252 // transfer in_place data into set body
1253
1254
1/2
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
211 if(how_ == how::elastic)
1255 {
1256 211 init_dynamic(ec);
1257
1/2
✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
211 if(! ec.failed())
1258 {
1259
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 109 times.
211 if(st_ == state::body)
1260 102 goto do_body;
1261
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 BOOST_ASSERT(
1262 st_ == state::complete);
1263 109 break;
1264 }
1265 st_ = state::reset; // unrecoverable
1266 return;
1267 }
1268
1269 if(how_ == how::sink)
1270 {
1271 auto n = body_buf_->size();
1272 if(h_.md.payload == payload::size)
1273 {
1274 // sink_->size_hint(h_.md.payload_size, ec);
1275
1276 if(n < h_.md.payload_size)
1277 {
1278 auto rv = sink_->write(
1279 body_buf_->data(), false);
1280 BOOST_ASSERT(rv.ec.failed() ||
1281 rv.bytes == body_buf_->size());
1282 BOOST_ASSERT(
1283 rv.bytes >= body_avail_);
1284 BOOST_ASSERT(
1285 rv.bytes < payload_remain_);
1286 body_buf_->consume(rv.bytes);
1287 body_avail_ -= rv.bytes;
1288 body_total_ += rv.bytes;
1289 payload_remain_ -= rv.bytes;
1290 if(rv.ec.failed())
1291 {
1292 ec = rv.ec;
1293 st_ = state::reset; // unrecoverable
1294 return;
1295 }
1296 st_ = state::body;
1297 goto do_body;
1298 }
1299
1300 n = static_cast<std::size_t>(h_.md.payload_size);
1301 }
1302 // complete
1303 BOOST_ASSERT(body_buf_ == &cb0_);
1304 auto rv = sink_->write(
1305 body_buf_->data(), true);
1306 BOOST_ASSERT(rv.ec.failed() ||
1307 rv.bytes == body_buf_->size());
1308 body_buf_->consume(rv.bytes);
1309 if(rv.ec.failed())
1310 {
1311 ec = rv.ec;
1312 st_ = state::reset; // unrecoverable
1313 return;
1314 }
1315 st_ = state::complete;
1316 return;
1317 }
1318
1319 // VFALCO TODO
1320 detail::throw_logic_error();
1321 }
1322
1323 592 case state::complete:
1324 {
1325 // This is a no-op except when set_body
1326 // was called and we have in-place data.
1327
2/4
✓ Branch 0 taken 296 times.
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
592 switch(how_)
1328 {
1329 296 default:
1330 case how::in_place:
1331 296 break;
1332
1333 296 case how::elastic:
1334 {
1335
1/2
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
296 if(body_buf_->size() == 0)
1336 296 break;
1337 BOOST_ASSERT(eb_->size() == 0);
1338 auto n = buffers::buffer_copy(
1339 eb_->prepare(
1340 body_buf_->size()),
1341 body_buf_->data());
1342 body_buf_->consume(n);
1343 break;
1344 }
1345
1346 case how::sink:
1347 {
1348 if(body_buf_->size() == 0)
1349 break;
1350 auto rv = sink_->write(
1351 body_buf_->data(), false);
1352 body_buf_->consume(rv.bytes);
1353 if(rv.ec.failed())
1354 {
1355 ec = rv.ec;
1356 st_ = state::reset; // unrecoverable
1357 return;
1358 }
1359 break;
1360 }
1361
1362 case how::pull:
1363 // VFALCO TODO
1364 detail::throw_logic_error();
1365 }
1366 }
1367 }
1368 }
1369
1370 //------------------------------------------------
1371
1372 auto
1373 parser::
1374 pull_some() ->
1375 const_buffers_type
1376 {
1377 return {};
1378 }
1379
1380 core::string_view
1381 1344 parser::
1382 body() const noexcept
1383 {
1384
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 995 times.
1344 switch(st_)
1385 {
1386 349 default:
1387 case state::reset:
1388 case state::start:
1389 case state::header:
1390 case state::body:
1391 case state::set_body:
1392 // not complete
1393 349 return {};
1394
1395 995 case state::complete:
1396
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 649 times.
995 if(how_ != how::in_place)
1397 {
1398 // not in_place
1399 346 return {};
1400 }
1401 649 auto cbp = body_buf_->data();
1402
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[1].size() == 0);
1403
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[0].size() == body_avail_);
1404 649 return core::string_view(
1405 static_cast<char const*>(
1406 649 cbp[0].data()),
1407 1298 static_cast<std::size_t>(body_avail_));
1408 }
1409 }
1410
1411 core::string_view
1412 parser::
1413 release_buffered_data() noexcept
1414 {
1415 return {};
1416 }
1417
1418 //------------------------------------------------
1419 //
1420 // Implementation
1421 //
1422 //------------------------------------------------
1423
1424 auto
1425 314 parser::
1426 safe_get_header() const ->
1427 detail::header const*
1428 {
1429 // headers must be received
1430
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() ||
1431 314 fb_.size() == 0) // happens on eof
1432 detail::throw_logic_error();
1433
1434 314 return &h_;
1435 }
1436
1437 bool
1438 973 parser::
1439 is_plain() const noexcept
1440 {
1441
1/2
✓ Branch 0 taken 973 times.
✗ Branch 1 not taken.
1946 return ! filt_ &&
1442
2/2
✓ Branch 0 taken 586 times.
✓ Branch 1 taken 387 times.
973 h_.md.payload !=
1443 973 payload::chunked;
1444 }
1445
1446 // Called immediately after complete headers
1447 // are received. We leave fb_ as-is to indicate
1448 // whether any data was received before eof.
1449 //
1450 void
1451 1538 parser::
1452 on_headers(
1453 system::error_code& ec)
1454 {
1455 1538 auto const overread = fb_.size() - h_.size;
1456
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1538 times.
1538 BOOST_ASSERT(
1457 overread <= svc_.max_overread());
1458
1459 // metadata error
1460
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 1418 times.
1538 if(h_.md.payload == payload::error)
1461 {
1462 // VFALCO This needs looking at
1463 240 ec = BOOST_HTTP_PROTO_ERR(
1464 error::bad_payload);
1465 120 st_ = state::reset; // unrecoverable
1466 120 return;
1467 }
1468
1469 // reserve headers + table
1470 1418 ws_.reserve_front(h_.size);
1471 1418 ws_.reserve_back(h_.table_space());
1472
1473 // no payload
1474
2/2
✓ Branch 0 taken 574 times.
✓ Branch 1 taken 844 times.
1418 if( h_.md.payload == payload::none ||
1475
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 574 times.
574 head_response_)
1476 {
1477 // set cb0_ to overread
1478 1688 cb0_ = {
1479 844 ws_.data(),
1480 844 fb_.capacity() - h_.size,
1481 overread };
1482 844 body_avail_ = 0;
1483 844 body_total_ = 0;
1484 844 body_buf_ = &cb0_;
1485 844 st_ = state::complete;
1486 844 return;
1487 }
1488
1489 // calculate filter
1490 574 filt_ = nullptr; // VFALCO TODO
1491
1492
2/2
✓ Branch 1 taken 485 times.
✓ Branch 2 taken 89 times.
574 if(is_plain())
1493 {
1494 // plain payload
1495
2/2
✓ Branch 0 taken 250 times.
✓ Branch 1 taken 235 times.
485 if(h_.md.payload == payload::size)
1496 {
1497 250 if(h_.md.payload_size >
1498
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 250 times.
250 svc_.cfg.body_limit)
1499 {
1500 ec = BOOST_HTTP_PROTO_ERR(
1501 error::body_too_large);
1502 st_ = state::reset; // unrecoverable
1503 return;
1504 }
1505
1506 auto n0 =
1507 250 fb_.capacity() - h_.size +
1508 250 svc_.cfg.min_buffer +
1509 250 svc_.max_codec;
1510 // limit the capacity of cb0_ so
1511 // that going over max_overread
1512 // is impossible.
1513
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 &&
1514 249 n0 - h_.md.payload_size >=
1515 249 svc_.max_overread())
1516 14 n0 = static_cast<std::size_t>(h_.md.payload_size) +
1517 14 svc_.max_overread();
1518
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 250 times.
250 BOOST_ASSERT(n0 <= ws_.size());
1519 250 cb0_ = { ws_.data(), n0, overread };
1520 250 body_buf_ = &cb0_;
1521 250 body_avail_ = cb0_.size();
1522
2/2
✓ Branch 0 taken 225 times.
✓ Branch 1 taken 25 times.
250 if( body_avail_ >= h_.md.payload_size)
1523 225 body_avail_ = h_.md.payload_size;
1524 250 body_total_ = body_avail_;
1525 250 payload_remain_ =
1526 250 h_.md.payload_size - body_total_;
1527 250 st_ = state::body;
1528 250 return;
1529 }
1530
1531 // overread is not applicable
1532
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
235 BOOST_ASSERT(
1533 h_.md.payload == payload::to_eof);
1534 auto const n0 =
1535 235 fb_.capacity() - h_.size +
1536 235 svc_.cfg.min_buffer +
1537 235 svc_.max_codec;
1538
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 235 times.
235 BOOST_ASSERT(n0 <= ws_.size());
1539 235 cb0_ = { ws_.data(), n0, overread };
1540 235 body_buf_ = &cb0_;
1541 235 body_avail_ = cb0_.size();
1542 235 body_total_ = body_avail_;
1543 235 st_ = state::body;
1544 235 return;
1545 }
1546
1547
1/2
✓ Branch 0 taken 89 times.
✗ Branch 1 not taken.
89 if( h_.md.payload == payload::chunked )
1548 {
1549 auto const n0 =
1550 89 fb_.capacity() - fb_.size();
1551
1552 89 cb0_ = { ws_.data(), n0 / 2, overread };
1553 89 cb1_ = { ws_.data() + n0 / 2, n0 - (n0 / 2), 0 };
1554 89 body_buf_ = &cb1_;
1555 89 st_ = state::body;
1556 89 return;
1557 }
1558
1559 // buffered payload
1560 auto const n0 = fb_.capacity() - h_.size;
1561 BOOST_ASSERT(n0 <= svc_.max_overread());
1562 auto n1 = svc_.cfg.min_buffer;
1563 if(! filt_)
1564 n1 += svc_.max_codec;
1565 BOOST_ASSERT(n0 + n1 <= ws_.size());
1566 cb0_ = { ws_.data(), n0, overread };
1567 cb1_ = { ws_.data() + n0, n1 };
1568 body_buf_ = &cb1_;
1569 body_avail_ = 0;
1570 body_total_ = 0;
1571 st_ = state::body;
1572 }
1573
1574 // Called at the end of set_body
1575 void
1576 299 parser::
1577 on_set_body()
1578 {
1579 // This function is called after all
1580 // limit checking and calculation of
1581 // chunked or filter.
1582
1583
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
299 BOOST_ASSERT(got_header());
1584
1585 299 nprepare_ = 0; // invalidate
1586
1587
1/2
✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
299 if(how_ == how::elastic)
1588 {
1589
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 241 times.
299 if(h_.md.payload == payload::none)
1590 {
1591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 BOOST_ASSERT(st_ == state::complete);
1592 58 return;
1593 }
1594
1595 241 st_ = state::set_body;
1596 241 return;
1597 }
1598
1599 if(how_ == how::sink)
1600 {
1601 if(h_.md.payload == payload::none)
1602 {
1603 BOOST_ASSERT(st_ == state::complete);
1604 // force a trip through parse so
1605 // we can calculate any error.
1606 st_ = state::set_body;
1607 return;
1608 }
1609
1610 st_ = state::set_body;
1611 return;
1612 }
1613
1614 // VFALCO TODO
1615 detail::throw_logic_error();
1616 }
1617
1618 void
1619 238 parser::
1620 init_dynamic(
1621 system::error_code& ec)
1622 {
1623 // attempt to transfer in-place
1624 // body into the dynamic buffer.
1625
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
238 BOOST_ASSERT(
1626 body_avail_ == body_buf_->size());
1627
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 BOOST_ASSERT(
1628 body_total_ == body_avail_);
1629 auto const space_left =
1630 238 eb_->max_size() - eb_->size();
1631
1632
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 117 times.
238 if(h_.md.payload == payload::size)
1633 {
1634
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 120 times.
121 if(space_left < h_.md.payload_size)
1635 {
1636 2 ec = BOOST_HTTP_PROTO_ERR(
1637 error::buffer_overflow);
1638 1 return;
1639 }
1640 // reserve the full size
1641 120 eb_->prepare(static_cast<std::size_t>(h_.md.payload_size));
1642 // transfer in-place body
1643 120 auto n = static_cast<std::size_t>(body_avail_);
1644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if( n > h_.md.payload_size)
1645 n = static_cast<std::size_t>(h_.md.payload_size);
1646
1/2
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
120 eb_->commit(
1647 buffers::buffer_copy(
1648
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 eb_->prepare(n),
1649 120 body_buf_->data()));
1650
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_avail_ == n);
1651
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_total_ == n);
1652
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(payload_remain_ ==
1653 h_.md.payload_size - n);
1654 120 body_buf_->consume(n);
1655 120 body_avail_ = 0;
1656
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 111 times.
120 if(n < h_.md.payload_size)
1657 {
1658
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(
1659 body_buf_->size() == 0);
1660 9 st_ = state::body;
1661 9 return;
1662 }
1663 // complete
1664 111 st_ = state::complete;
1665 111 return;
1666 }
1667
1668
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 BOOST_ASSERT(h_.md.payload ==
1669 payload::to_eof);
1670
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if(space_left < body_avail_)
1671 {
1672 ec = BOOST_HTTP_PROTO_ERR(
1673 error::buffer_overflow);
1674 return;
1675 }
1676
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 eb_->commit(
1677 buffers::buffer_copy(
1678
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 eb_->prepare(static_cast<std::size_t>(body_avail_)),
1679 117 body_buf_->data()));
1680 117 body_buf_->consume(static_cast<std::size_t>(body_avail_));
1681 117 body_avail_ = 0;
1682
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
117 BOOST_ASSERT(
1683 body_buf_->size() == 0);
1684 117 st_ = state::body;
1685 }
1686
1687 } // http_proto
1688 } // boost
1689