GCC Code Coverage Report


Directory: libs/http_proto/
File: libs/http_proto/src/parser.cpp
Date: 2024-08-30 20:13:36
Exec Total Coverage
Lines: 635 750 84.7%
Functions: 36 42 85.7%
Branches: 330 512 64.5%

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 69272 discontiguous_iterator(
130 buffers::const_buffer const* pos_,
131 buffers::const_buffer const* end_)
132 69272 : pos(pos_)
133 69272 , end(end_)
134 {
135 69272 }
136
137 char
138 914376 operator*() const noexcept
139 {
140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 914376 times.
914376 BOOST_ASSERT(pos);
141
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 914376 times.
914376 BOOST_ASSERT(pos->size() > 0);
142
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 914376 times.
914376 BOOST_ASSERT(off < pos->size());
143 auto it =
144 914376 static_cast<char const*>(pos->data()) + off;
145 914376 return *it;
146 }
147
148 discontiguous_iterator&
149 845105 operator++() noexcept
150 {
151 845105 ++off;
152
2/2
✓ Branch 1 taken 23345 times.
✓ Branch 2 taken 821760 times.
845105 if( off >= pos->size() )
153 {
154 23345 ++pos;
155 23345 off = 0;
156
5/6
✓ Branch 0 taken 23345 times.
✓ Branch 1 taken 23345 times.
✓ Branch 3 taken 23345 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 23345 times.
✓ Branch 6 taken 23345 times.
46690 while( pos != end && pos->size() == 0 )
157 23345 ++pos;
158 23345 return *this;
159 }
160 821760 return *this;
161 }
162
163 discontiguous_iterator
164 836889 operator++(int) noexcept
165 {
166 836889 auto old = *this;
167 836889 ++*this;
168 836889 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 919302 done() const noexcept
187 {
188 919302 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 65191 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 65187 times.
65191 if( input.size() == 0 )
206 {
207 4 ec = error::need_data;
208 23079 return;
209 }
210
211 65187 char tmp[max_chunk_header_len] = {};
212 65187 auto* p = tmp;
213 65187 unsigned num_leading_zeros = 0;
214
215 {
216 65187 auto cbs = input.data();
217 65187 discontiguous_iterator pos(cbs.begin(), cbs.end());
218
219
6/6
✓ Branch 1 taken 69314 times.
✓ Branch 2 taken 1 times.
✓ Branch 4 taken 4128 times.
✓ Branch 5 taken 65186 times.
✓ Branch 6 taken 4128 times.
✓ Branch 7 taken 65187 times.
69315 for( ; !pos.done() && *pos == '0'; ++pos )
220 4128 ++num_leading_zeros;
221
222
4/4
✓ Branch 0 taken 841814 times.
✓ Branch 1 taken 43938 times.
✓ Branch 2 taken 820565 times.
✓ Branch 3 taken 65187 times.
1727566 for( ; p < (tmp + max_chunk_header_len) &&
223
2/2
✓ Branch 1 taken 820565 times.
✓ Branch 2 taken 21249 times.
841814 !pos.done(); )
224 820565 *p++ = *pos++;
225 }
226
227 65187 core::string_view sv(tmp, p - tmp);
228
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 65187 times.
65187 BOOST_ASSERT(sv.size() <= input.size());
229
230 65187 auto it = sv.begin();
231 auto rv =
232 65187 grammar::parse(it, sv.end(), detail::hex_rule);
233
234
2/2
✓ Branch 1 taken 4085 times.
✓ Branch 2 taken 61102 times.
65187 if( rv.has_error() )
235 {
236 4085 ec = error::bad_payload;
237 4085 return;
238 }
239
240 auto rv2 =
241 61102 grammar::parse(it, sv.end(), detail::crlf_rule);
242
2/2
✓ Branch 1 taken 18990 times.
✓ Branch 2 taken 42112 times.
61102 if( rv2.has_error() )
243 {
244
2/2
✓ Branch 3 taken 18987 times.
✓ Branch 4 taken 3 times.
18990 if( rv2.error() == condition::need_more_input )
245 18987 ec = error::need_data;
246 else
247 3 ec = error::bad_payload;
248 18990 return;
249 }
250
251
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 42112 times.
42112 if( rv->v == 0 )
252 {
253 ec = error::bad_payload;
254 return;
255 }
256
257 42112 auto n = num_leading_zeros + (it - sv.begin());
258 42112 input.consume(n);
259 42112 chunk_remain_ = rv->v;
260 };
261
262 static
263 void
264 23079 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 18994 times.
✓ Branch 3 taken 4085 times.
23079 if( buffers::buffer_size(input.data()) <
276 last_chunk_len )
277 {
278 18994 ec = error::need_data;
279 19006 return;
280 }
281
282 4085 auto cbs = input.data();
283 4085 discontiguous_iterator pos(cbs.begin(), cbs.end());
284
285 4085 std::size_t len = 0;
286
5/6
✓ Branch 2 taken 8173 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4088 times.
✓ Branch 6 taken 4085 times.
✓ Branch 7 taken 4088 times.
✓ Branch 8 taken 4085 times.
8173 for( ; !pos.done() && *pos == '0'; ++pos, ++len )
287 {
288 }
289
290
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4081 times.
4085 if( len == 0 )
291 {
292 4 ec = error::bad_payload;
293 4 return;
294 }
295
296 4081 std::size_t const close_len = 4; // for \r\n\r\n
297
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4081 times.
4081 if( buffers::buffer_size(input.data()) - len <
298 close_len )
299 {
300 ec = error::need_data;
301 return;
302 }
303
304 4081 char tmp[close_len] = {};
305
2/2
✓ Branch 0 taken 16324 times.
✓ Branch 1 taken 4081 times.
20405 for( std::size_t i = 0; i < close_len; ++i )
306 16324 tmp[i] = *pos++;
307
308 4081 core::string_view s(tmp, close_len);
309
2/2
✓ Branch 2 taken 8 times.
✓ Branch 3 taken 4073 times.
4081 if( s != "\r\n\r\n" )
310 {
311 8 ec = error::bad_payload;
312 8 return;
313 }
314
315 4073 input.consume(len + close_len);
316 };
317
318 template <class ElasticBuffer>
319 bool
320 23219 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 23147 times.
23219 if( input.size() == 0 )
329 {
330 72 ec = error::need_data;
331 72 return false;
332 }
333
334 84274 for(;;)
335 {
336
2/2
✓ Branch 0 taken 107308 times.
✓ Branch 1 taken 113 times.
107421 if( chunk_remain_ == 0 )
337 {
338
2/2
✓ Branch 0 taken 42117 times.
✓ Branch 1 taken 65191 times.
107308 if( needs_chunk_close_ )
339 {
340
2/2
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 42111 times.
42117 if( input.size() < 2 )
341 {
342 6 ec = error::need_data;
343 9 return false;
344 }
345
346 42111 std::size_t const crlf_len = 2;
347 42111 char tmp[crlf_len] = {};
348
349 42111 buffers::buffer_copy(
350 42111 buffers::mutable_buffer(
351 tmp, crlf_len),
352 42111 input.data());
353
354 42111 core::string_view str(tmp, crlf_len);
355
2/2
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 42108 times.
42111 if( str != "\r\n" )
356 {
357 3 ec = error::bad_payload;
358 3 return false;
359 }
360
361 42108 input.consume(crlf_len);
362 42108 needs_chunk_close_ = false;
363 42108 continue;
364 42108 }
365
366 65191 parse_chunk_header(input, ec, chunk_remain_);
367
2/2
✓ Branch 1 taken 23079 times.
✓ Branch 2 taken 42112 times.
65191 if( ec )
368 {
369 23079 system::error_code ec2;
370 23079 parse_last_chunk(input, ec2);
371
2/2
✓ Branch 1 taken 19006 times.
✓ Branch 2 taken 4073 times.
23079 if( ec2 )
372 {
373
2/2
✓ Branch 2 taken 18994 times.
✓ Branch 3 taken 12 times.
19006 if( ec2 == condition::need_more_input )
374 18994 ec = ec2;
375 19006 return false;
376 }
377
378 // complete
379 4073 ec.clear();
380 4073 return true;
381 }
382
383 42112 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 42166 times.
42225 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 42166 times.
42166 if( output.capacity() == 0 )
398 detail::throw_length_error();
399
400 42166 auto n = (std::min)(chunk_remain_, input.size());
401
402 42166 auto m = buffers::buffer_copy(
403
1/2
✓ Branch 2 taken 42166 times.
✗ Branch 3 not taken.
42166 output.prepare(output.capacity()),
404 42166 buffers::prefix(input.data(), n));
405
406
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42166 times.
42166 BOOST_ASSERT(m <= chunk_remain_);
407 42166 chunk_remain_ -= m;
408 42166 input.consume(m);
409 42166 output.commit(m);
410 42166 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 35095 max_overread() const noexcept
433 {
434 return
435 35095 cfg.headers.max_size +
436 35095 cfg.min_buffer;
437 }
438 };
439
440 35 parser_service::
441 parser_service(
442 context& ctx,
443 35 parser::config_base const& cfg_)
444 35 : 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 35 times.
✗ Branch 1 not taken.
35 if( cfg.min_buffer < 1 ||
462
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 cfg.min_buffer > cfg.body_limit)
463 detail::throw_invalid_argument();
464
465
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 35 times.
35 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 35 space_needed +=
478
1/2
✓ Branch 1 taken 35 times.
✗ Branch 2 not taken.
35 cfg.headers.valid_space_needed();
479
480 // cb0_, cb1_
481 // VFALCO OVERFLOW CHECKING ON THIS
482 35 space_needed +=
483 35 cfg.min_buffer +
484 cfg.min_buffer;
485
486 // T
487 35 space_needed += cfg.max_type_erase;
488
489 // max_codec
490 {
491
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 34 times.
35 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 35 space_needed += max_codec;
502
503 // round up to alignof(detail::header::entry)
504 35 auto const al = alignof(
505 detail::header::entry);
506 35 space_needed = al * ((
507 35 space_needed + al - 1) / al);
508 35 }
509
510 void
511 35 install_parser_service(
512 context& ctx,
513 parser::config_base const& cfg)
514 {
515 ctx.make_service<
516 35 parser_service>(cfg);
517 35 }
518
519 //------------------------------------------------
520 //
521 // Special Members
522 //
523 //------------------------------------------------
524
525 1047 parser::
526 parser(
527 context& ctx,
528 1047 detail::kind k)
529 1047 : ctx_(ctx)
530 1047 , svc_(ctx.get_service<
531 1047 parser_service>())
532 1047 , h_(detail::empty{k})
533 1047 , eb_(nullptr)
534 2094 , st_(state::reset)
535 {
536 1047 auto const n =
537 1047 svc_.space_needed;
538
1/2
✓ Branch 1 taken 1047 times.
✗ Branch 2 not taken.
1047 ws_.allocate(n);
539 1047 h_.cap = n;
540 1047 }
541
542 //------------------------------------------------
543
544 1047 parser::
545 ~parser()
546 {
547 1047 }
548
549 //------------------------------------------------
550 //
551 // Modifiers
552 //
553 //------------------------------------------------
554
555 // prepare for a new stream
556 void
557 1602 parser::
558 reset() noexcept
559 {
560 1602 ws_.clear();
561 1602 eb_ = nullptr;
562 1602 st_ = state::start;
563 1602 got_eof_ = false;
564 1602 }
565
566 void
567 9830 parser::
568 start_impl(
569 bool head_response)
570 {
571 9830 std::size_t leftover = 0;
572
5/5
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1587 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 8238 times.
9830 switch(st_)
573 {
574 1 default:
575 case state::reset:
576 // reset must be called first
577 1 detail::throw_logic_error();
578
579 1587 case state::start:
580 // reset required on eof
581
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1587 times.
1587 if(got_eof_)
582 detail::throw_logic_error();
583 1587 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 8238 case state::complete:
599 {
600 // remove partial body.
601
6/6
✓ Branch 1 taken 4239 times.
✓ Branch 2 taken 3999 times.
✓ Branch 3 taken 4174 times.
✓ Branch 4 taken 65 times.
✓ Branch 5 taken 4174 times.
✓ Branch 6 taken 4064 times.
8238 if(is_plain() && (how_ == how::in_place))
602 4174 cb0_.consume(
603 4174 static_cast<std::size_t>(body_avail_));
604
605
2/2
✓ Branch 1 taken 4000 times.
✓ Branch 2 taken 4238 times.
8238 if(cb0_.size() > 0)
606 {
607 // move unused octets to front
608
609 4000 ws_.clear();
610 4000 leftover = cb0_.size();
611
612 4000 auto* dest = reinterpret_cast<char*>(ws_.data());
613 4000 auto cbp = cb0_.data();
614 4000 auto* a = static_cast<char const*>(cbp[0].data());
615 4000 auto* b = static_cast<char const*>(cbp[1].data());
616 4000 auto an = cbp[0].size();
617 4000 auto bn = cbp[1].size();
618
619
2/2
✓ Branch 0 taken 3847 times.
✓ Branch 1 taken 153 times.
4000 if(bn == 0)
620 {
621 3847 std::memmove(dest, a, an);
622 }
623 else
624 {
625 // if `a` can fit between `dest` and `b`, shift `b` to the left
626 // and copy `a` to its position. if `a` fits perfectly, the
627 // shift will be of size 0.
628 // if `a` requires more space, shift `b` to the right and
629 // copy `a` to its position. this process may require multiple
630 // iterations and should be done chunk by chunk to prevent `b`
631 // from overlapping with `a`.
632 do
633 {
634 // clamp right shifts to prevent overlap with `a`
635 153 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
636 153 b = static_cast<char const*>(std::memmove(bp, b, bn));
637
638 // a chunk or all of `a` based on available space
639 153 auto chunk_a = static_cast<std::size_t>(b - dest);
640 153 std::memcpy(dest, a, chunk_a); // never overlap
641 153 an -= chunk_a;
642 153 dest += chunk_a;
643 153 a += chunk_a;
644
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 153 times.
153 } while(an);
645 }
646 }
647 else
648 {
649 // leftover data after body
650 }
651 8238 break;
652 }
653 }
654
655 9825 ws_.clear();
656
657 19650 fb_ = {
658 9825 ws_.data(),
659 9825 svc_.cfg.headers.max_size +
660 9825 svc_.cfg.min_buffer,
661 leftover };
662
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 9825 times.
9825 BOOST_ASSERT(fb_.capacity() ==
663 svc_.max_overread() - leftover);
664
665 19650 h_ = detail::header(
666 9825 detail::empty{h_.kind});
667 9825 h_.buf = reinterpret_cast<
668 9825 char*>(ws_.data());
669 9825 h_.cbuf = h_.buf;
670 9825 h_.cap = ws_.size();
671
672
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 9825 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
9825 BOOST_ASSERT(! head_response ||
673 h_.kind == detail::kind::response);
674 9825 head_response_ = head_response;
675
676 // begin with in_place mode
677 9825 how_ = how::in_place;
678 9825 st_ = state::header;
679 9825 nprepare_ = 0;
680 9825 chunk_remain_ = 0;
681 9825 needs_chunk_close_ = false;
682 9825 body_avail_ = 0;
683 9825 }
684
685 auto
686 47764 parser::
687 prepare() ->
688 mutable_buffers_type
689 {
690 47764 nprepare_ = 0;
691
692
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9589 times.
✓ Branch 3 taken 38143 times.
✓ Branch 4 taken 27 times.
✓ Branch 5 taken 3 times.
47764 switch(st_)
693 {
694 1 default:
695 case state::reset:
696 // reset must be called first
697 1 detail::throw_logic_error();
698
699 1 case state::start:
700 // start must be called first
701 1 detail::throw_logic_error();
702
703 9589 case state::header:
704 {
705
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9589 times.
9589 BOOST_ASSERT(h_.size <
706 svc_.cfg.headers.max_size);
707 9589 auto n = fb_.capacity() - fb_.size();
708
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9589 times.
9589 BOOST_ASSERT(n <= svc_.max_overread());
709
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 9560 times.
9589 if( n > svc_.cfg.max_prepare)
710 29 n = svc_.cfg.max_prepare;
711 9589 mbp_[0] = fb_.prepare(n);
712 9589 nprepare_ = n;
713 9589 return mutable_buffers_type(
714 19178 &mbp_[0], 1);
715 }
716
717 38143 case state::body:
718 {
719
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 38143 times.
38143 if(got_eof_)
720 return mutable_buffers_type{};
721
722 38143 do_body:
723
2/2
✓ Branch 1 taken 19130 times.
✓ Branch 2 taken 19037 times.
38167 if(! is_plain())
724 {
725 // buffered payload
726 19130 auto n = cb0_.capacity() -
727 19130 cb0_.size();
728
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19130 times.
19130 if( n > svc_.cfg.max_prepare)
729 n = svc_.cfg.max_prepare;
730 19130 mbp_ = cb0_.prepare(n);
731 19130 nprepare_ = n;
732 19130 return mutable_buffers_type(mbp_);
733 }
734
735 // plain payload
736
737
2/2
✓ Branch 0 taken 19010 times.
✓ Branch 1 taken 27 times.
19037 if(how_ == how::in_place)
738 {
739 19010 auto n = cb0_.capacity();
740
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19009 times.
19010 if( n > svc_.cfg.max_prepare)
741 1 n = svc_.cfg.max_prepare;
742 19010 mbp_ = cb0_.prepare(n);
743 19010 nprepare_ = n;
744 19010 return mutable_buffers_type(mbp_);
745 }
746
747
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if(how_ == how::elastic)
748 {
749 // Overreads are not allowed, or
750 // else the caller will see extra
751 // unrelated data.
752
753
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 18 times.
27 if(h_.md.payload == payload::size)
754 {
755 // set_body moves avail to dyn
756
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(body_buf_->size() == 0);
757
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 BOOST_ASSERT(body_avail_ == 0);
758 9 auto n = static_cast<std::size_t>(payload_remain_);
759
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 8 times.
9 if( n > svc_.cfg.max_prepare)
760 1 n = svc_.cfg.max_prepare;
761 9 nprepare_ = n;
762 9 return eb_->prepare(n);
763 }
764
765
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 BOOST_ASSERT(
766 h_.md.payload == payload::to_eof);
767 18 std::size_t n = 0;
768
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if(! got_eof_)
769 {
770 // calculate n heuristically
771 18 n = svc_.cfg.min_buffer;
772
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 if( n > svc_.cfg.max_prepare)
773 1 n = svc_.cfg.max_prepare;
774 {
775 // apply max_size()
776 auto avail =
777 18 eb_->max_size() -
778 18 eb_->size();
779
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 9 times.
18 if( n > avail)
780 9 n = avail;
781 }
782 // fill capacity() first,
783 // to avoid an allocation
784 {
785 auto avail =
786 18 eb_->capacity() -
787 18 eb_->size();
788
3/4
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
18 if( n > avail &&
789 avail != 0)
790 3 n = avail;
791 }
792
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if(n == 0)
793 {
794 // dynamic buffer is full
795 // attempt a 1 byte read so
796 // we can detect overflow
797
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 BOOST_ASSERT(
798 body_buf_->size() == 0);
799 // handled in init_dynamic
800
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
801 body_avail_ == 0);
802 2 mbp_ = body_buf_->prepare(1);
803 2 nprepare_ = 1;
804 return
805 2 mutable_buffers_type(mbp_);
806 }
807 }
808 16 nprepare_ = n;
809 16 return eb_->prepare(n);
810 }
811
812 // VFALCO TODO
813 detail::throw_logic_error();
814 }
815
816 27 case state::set_body:
817 {
818
1/2
✓ Branch 0 taken 27 times.
✗ Branch 1 not taken.
27 if(how_ == how::elastic)
819 {
820 // attempt to transfer in-place
821 // body into the dynamic buffer.
822 27 system::error_code ec;
823
1/2
✓ Branch 1 taken 27 times.
✗ Branch 2 not taken.
27 init_dynamic(ec);
824
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 1 times.
27 if(! ec.failed())
825 {
826
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 2 times.
26 if(st_ == state::body)
827 24 goto do_body;
828
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 BOOST_ASSERT(
829 st_ == state::complete);
830 2 return mutable_buffers_type{};
831 }
832
833 // not enough room, so we
834 // return this error from parse()
835 return
836 1 mutable_buffers_type{};
837 }
838
839 if(how_ == how::sink)
840 {
841 // this is a no-op, to get the
842 // caller to call parse next.
843 return mutable_buffers_type{};
844 }
845
846 // VFALCO TODO
847 detail::throw_logic_error();
848 }
849
850 3 case state::complete:
851 // intended no-op
852 3 return mutable_buffers_type{};
853 }
854 }
855
856 void
857 47755 parser::
858 commit(
859 std::size_t n)
860 {
861
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9589 times.
✓ Branch 3 taken 38158 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 4 times.
47755 switch(st_)
862 {
863 1 default:
864 case state::reset:
865 {
866 // reset must be called first
867 1 detail::throw_logic_error();
868 }
869
870 1 case state::start:
871 {
872 // forgot to call start()
873 1 detail::throw_logic_error();
874 }
875
876 9589 case state::header:
877 {
878
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9588 times.
9589 if(n > nprepare_)
879 {
880 // n can't be greater than size of
881 // the buffers returned by prepare()
882 1 detail::throw_invalid_argument();
883 }
884
885
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9587 times.
9588 if(got_eof_)
886 {
887 // can't commit after EOF
888 1 detail::throw_logic_error();
889 }
890
891 9587 nprepare_ = 0; // invalidate
892 9587 fb_.commit(n);
893 9587 break;
894 }
895
896 38158 case state::body:
897 {
898
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 38157 times.
38158 if(n > nprepare_)
899 {
900 // n can't be greater than size of
901 // the buffers returned by prepare()
902 1 detail::throw_invalid_argument();
903 }
904
905
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 38157 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
38157 BOOST_ASSERT(! got_eof_ || n == 0);
906
907
2/2
✓ Branch 1 taken 19130 times.
✓ Branch 2 taken 19027 times.
38157 if(! is_plain())
908 {
909 // buffered payload
910 19130 cb0_.commit(n);
911 19130 break;
912 }
913
914 // plain payload
915
916
2/2
✓ Branch 0 taken 19007 times.
✓ Branch 1 taken 20 times.
19027 if(how_ == how::in_place)
917 {
918
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19007 times.
19007 BOOST_ASSERT(body_buf_ == &cb0_);
919 19007 cb0_.commit(n);
920
2/2
✓ Branch 0 taken 18993 times.
✓ Branch 1 taken 14 times.
19007 if(h_.md.payload == payload::size)
921 {
922
2/2
✓ Branch 0 taken 17086 times.
✓ Branch 1 taken 1907 times.
18993 if(n < payload_remain_)
923 {
924 17086 body_avail_ += n;
925 17086 payload_remain_ -= n;
926 17086 break;
927 }
928 1907 body_avail_ += payload_remain_;
929 1907 payload_remain_ = 0;
930 1907 st_ = state::complete;
931 1907 break;
932 }
933
934
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 BOOST_ASSERT(
935 h_.md.payload == payload::to_eof);
936 14 body_avail_ += n;
937 14 break;
938 }
939
940
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
20 if(how_ == how::elastic)
941 {
942
2/2
✓ Branch 2 taken 19 times.
✓ Branch 3 taken 1 times.
20 if(eb_->size() < eb_->max_size())
943 {
944
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 19 times.
19 BOOST_ASSERT(body_avail_ == 0);
945
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 19 times.
19 BOOST_ASSERT(
946 body_buf_->size() == 0);
947 19 eb_->commit(n);
948 }
949 else
950 {
951 // If we get here then either
952 // n==0 as a no-op, or n==1 for
953 // an intended one byte read.
954
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n <= 1);
955 1 body_buf_->commit(n);
956 1 body_avail_ += n;
957 }
958 20 body_total_ += n;
959
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 14 times.
20 if(h_.md.payload == payload::size)
960 {
961
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 BOOST_ASSERT(
962 n <= payload_remain_);
963 6 payload_remain_ -= n;
964
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if(payload_remain_ == 0)
965 6 st_ = state::complete;
966 }
967 20 break;
968 }
969
970 if(how_ == how::sink)
971 {
972 cb0_.commit(n);
973 break;
974 }
975 break;
976 }
977
978 2 case state::set_body:
979 {
980
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(n > nprepare_)
981 {
982 // n can't be greater than size of
983 // the buffers returned by prepare()
984 1 detail::throw_invalid_argument();
985 }
986
987
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
1 BOOST_ASSERT(is_plain());
988
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 BOOST_ASSERT(n == 0);
989
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if( how_ == how::elastic ||
990 how_ == how::sink)
991 {
992 // intended no-op
993 break;
994 }
995
996 // VFALCO TODO
997 detail::throw_logic_error();
998 }
999
1000 4 case state::complete:
1001 {
1002
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 BOOST_ASSERT(nprepare_ == 0);
1003
1004
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 3 times.
4 if(n > 0)
1005 {
1006 // n can't be greater than size of
1007 // the buffers returned by prepare()
1008 1 detail::throw_invalid_argument();
1009 }
1010
1011 // intended no-op
1012 3 break;
1013 }
1014 }
1015 47748 }
1016
1017 void
1018 363 parser::
1019 commit_eof()
1020 {
1021 363 nprepare_ = 0; // invalidate
1022
1023
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_)
1024 {
1025 1 default:
1026 case state::reset:
1027 // reset must be called first
1028 1 detail::throw_logic_error();
1029
1030 1 case state::start:
1031 // forgot to call prepare()
1032 1 detail::throw_logic_error();
1033
1034 21 case state::header:
1035 21 got_eof_ = true;
1036 21 break;
1037
1038 127 case state::body:
1039 127 got_eof_ = true;
1040 127 break;
1041
1042 212 case state::set_body:
1043 212 got_eof_ = true;
1044 212 break;
1045
1046 1 case state::complete:
1047 // can't commit eof when complete
1048 1 detail::throw_logic_error();
1049 }
1050 360 }
1051
1052 //-----------------------------------------------
1053
1054 // process input data then
1055 // eof if input data runs out.
1056 void
1057 52663 parser::
1058 parse(
1059 system::error_code& ec)
1060 {
1061 52663 ec = {};
1062
6/6
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13589 times.
✓ Branch 3 taken 36370 times.
✓ Branch 4 taken 211 times.
✓ Branch 5 taken 2491 times.
52663 switch(st_)
1063 {
1064 1 default:
1065 case state::reset:
1066 // reset must be called first
1067 1 detail::throw_logic_error();
1068
1069 1 case state::start:
1070 // start must be called first
1071 1 detail::throw_logic_error();
1072
1073 13589 case state::header:
1074 {
1075
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13589 times.
13589 BOOST_ASSERT(h_.buf == static_cast<
1076 void const*>(ws_.data()));
1077
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 13589 times.
13589 BOOST_ASSERT(h_.cbuf == static_cast<
1078 void const*>(ws_.data()));
1079
1080 13589 h_.parse(fb_.size(), svc_.cfg.headers, ec);
1081
1082
2/2
✓ Branch 2 taken 3792 times.
✓ Branch 3 taken 9797 times.
13589 if(ec == condition::need_more_input)
1083 {
1084
2/2
✓ Branch 0 taken 3774 times.
✓ Branch 1 taken 18 times.
3792 if(! got_eof_)
1085 {
1086 // headers incomplete
1087 3774 return;
1088 }
1089
1090
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
18 if(fb_.size() == 0)
1091 {
1092 // stream closed cleanly
1093 8 st_ = state::complete;
1094 16 ec = BOOST_HTTP_PROTO_ERR(
1095 error::end_of_stream);
1096 8 return;
1097 }
1098
1099 // stream closed with a
1100 // partial message received
1101 10 st_ = state::reset;
1102 20 ec = BOOST_HTTP_PROTO_ERR(
1103 error::incomplete);
1104 10 return;
1105 }
1106
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 9538 times.
9797 if(ec.failed())
1107 {
1108 // other error,
1109 //
1110 // VFALCO map this to a bad
1111 // request or bad response error?
1112 //
1113 259 st_ = state::reset; // unrecoverable
1114 259 return;
1115 }
1116
1117 // headers are complete
1118 9538 on_headers(ec);
1119
2/2
✓ Branch 1 taken 120 times.
✓ Branch 2 taken 9418 times.
9538 if(ec.failed())
1120 120 return;
1121
2/2
✓ Branch 0 taken 865 times.
✓ Branch 1 taken 8553 times.
9418 if(st_ == state::complete)
1122 865 break;
1123
1124 BOOST_FALLTHROUGH;
1125 }
1126
1127 case state::body:
1128 {
1129 8553 do_body:
1130
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45025 times.
45025 BOOST_ASSERT(st_ == state::body);
1131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45025 times.
45025 BOOST_ASSERT(
1132 h_.md.payload != payload::none);
1133
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 45025 times.
45025 BOOST_ASSERT(
1134 h_.md.payload != payload::error);
1135
1136
2/2
✓ Branch 0 taken 23219 times.
✓ Branch 1 taken 21806 times.
45025 if( h_.md.payload == payload::chunked )
1137 {
1138 23219 auto completed = false;
1139 23219 auto& input = cb0_;
1140
1141
1/2
✓ Branch 0 taken 23219 times.
✗ Branch 1 not taken.
23219 if( how_ == how::in_place )
1142 {
1143 23219 auto& output = cb1_;
1144 completed =
1145 23219 parse_chunked(
1146 23219 input, output, ec, chunk_remain_,
1147 23219 body_avail_, needs_chunk_close_);
1148 }
1149 else
1150 detail::throw_logic_error();
1151
1152
2/2
✓ Branch 0 taken 4073 times.
✓ Branch 1 taken 19146 times.
23219 if( completed )
1153 4073 st_ = state::complete;
1154
1155 23219 return;
1156 }
1157
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 21806 times.
21806 else if( filt_ )
1158 {
1159 // VFALCO TODO apply filter
1160 detail::throw_logic_error();
1161 }
1162
1163
2/2
✓ Branch 0 taken 21679 times.
✓ Branch 1 taken 127 times.
21806 if(how_ == how::in_place)
1164 {
1165
2/2
✓ Branch 0 taken 21316 times.
✓ Branch 1 taken 363 times.
21679 if(h_.md.payload == payload::size)
1166 {
1167 21316 if(body_avail_ <
1168
2/2
✓ Branch 0 taken 19011 times.
✓ Branch 1 taken 2305 times.
21316 h_.md.payload_size)
1169 {
1170
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 19010 times.
19011 if(got_eof_)
1171 {
1172 // incomplete
1173 2 ec = BOOST_HTTP_PROTO_ERR(
1174 error::incomplete);
1175 1 return;
1176 }
1177
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 19009 times.
19010 if(body_buf_->capacity() == 0)
1178 {
1179 // in_place buffer limit
1180 2 ec = BOOST_HTTP_PROTO_ERR(
1181 error::in_place_overflow);
1182 1 return;
1183 }
1184 38018 ec = BOOST_HTTP_PROTO_ERR(
1185 error::need_data);
1186 19009 return;
1187 }
1188
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2305 times.
2305 BOOST_ASSERT(body_avail_ ==
1189 h_.md.payload_size);
1190 2305 st_ = state::complete;
1191 2305 break;
1192 }
1193
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 362 times.
363 if(body_avail_ > svc_.cfg.body_limit)
1194 {
1195 2 ec = BOOST_HTTP_PROTO_ERR(
1196 error::body_too_large);
1197 1 st_ = state::reset; // unrecoverable
1198 1 return;
1199 }
1200
1/2
✓ Branch 0 taken 362 times.
✗ Branch 1 not taken.
362 if( h_.md.payload == payload::chunked ||
1201
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 114 times.
362 ! got_eof_)
1202 {
1203 496 ec = BOOST_HTTP_PROTO_ERR(
1204 error::need_data);
1205 248 return;
1206 }
1207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 114 times.
114 BOOST_ASSERT(got_eof_);
1208 114 st_ = state::complete;
1209 114 break;
1210 }
1211
1212
1/2
✓ Branch 0 taken 127 times.
✗ Branch 1 not taken.
127 if(how_ == how::elastic)
1213 {
1214 // state already updated in commit
1215
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127 if(h_.md.payload == payload::size)
1216 {
1217 BOOST_ASSERT(body_total_ <
1218 h_.md.payload_size);
1219 BOOST_ASSERT(payload_remain_ > 0);
1220 if(body_avail_ != 0)
1221 {
1222 BOOST_ASSERT(
1223 eb_->max_size() -
1224 eb_->size() <
1225 payload_remain_);
1226 ec = BOOST_HTTP_PROTO_ERR(
1227 error::buffer_overflow);
1228 st_ = state::reset; // unrecoverable
1229 return;
1230 }
1231 if(got_eof_)
1232 {
1233 ec = BOOST_HTTP_PROTO_ERR(
1234 error::incomplete);
1235 st_ = state::reset; // unrecoverable
1236 return;
1237 }
1238 return;
1239 }
1240
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 127 times.
127 BOOST_ASSERT(
1241 h_.md.payload == payload::to_eof);
1242
3/4
✓ Branch 2 taken 46 times.
✓ Branch 3 taken 81 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 127 times.
173 if( eb_->size() == eb_->max_size() &&
1243
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 46 times.
46 body_avail_ > 0)
1244 {
1245 // got here from the 1-byte read
1246 ec = BOOST_HTTP_PROTO_ERR(
1247 error::buffer_overflow);
1248 st_ = state::reset; // unrecoverable
1249 return;
1250 }
1251
2/2
✓ Branch 0 taken 113 times.
✓ Branch 1 taken 14 times.
127 if(got_eof_)
1252 {
1253
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 113 times.
113 BOOST_ASSERT(body_avail_ == 0);
1254 113 st_ = state::complete;
1255 113 break;
1256 }
1257
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 BOOST_ASSERT(body_avail_ == 0);
1258 14 break;
1259 }
1260
1261 // VFALCO TODO
1262 detail::throw_logic_error();
1263 }
1264
1265 211 case state::set_body:
1266 {
1267 // transfer in_place data into set body
1268
1269
1/2
✓ Branch 0 taken 211 times.
✗ Branch 1 not taken.
211 if(how_ == how::elastic)
1270 {
1271 211 init_dynamic(ec);
1272
1/2
✓ Branch 1 taken 211 times.
✗ Branch 2 not taken.
211 if(! ec.failed())
1273 {
1274
2/2
✓ Branch 0 taken 102 times.
✓ Branch 1 taken 109 times.
211 if(st_ == state::body)
1275 102 goto do_body;
1276
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 109 times.
109 BOOST_ASSERT(
1277 st_ == state::complete);
1278 109 break;
1279 }
1280 st_ = state::reset; // unrecoverable
1281 return;
1282 }
1283
1284 if(how_ == how::sink)
1285 {
1286 auto n = body_buf_->size();
1287 if(h_.md.payload == payload::size)
1288 {
1289 // sink_->size_hint(h_.md.payload_size, ec);
1290
1291 if(n < h_.md.payload_size)
1292 {
1293 auto rv = sink_->write(
1294 body_buf_->data(), false);
1295 BOOST_ASSERT(rv.ec.failed() ||
1296 rv.bytes == body_buf_->size());
1297 BOOST_ASSERT(
1298 rv.bytes >= body_avail_);
1299 BOOST_ASSERT(
1300 rv.bytes < payload_remain_);
1301 body_buf_->consume(rv.bytes);
1302 body_avail_ -= rv.bytes;
1303 body_total_ += rv.bytes;
1304 payload_remain_ -= rv.bytes;
1305 if(rv.ec.failed())
1306 {
1307 ec = rv.ec;
1308 st_ = state::reset; // unrecoverable
1309 return;
1310 }
1311 st_ = state::body;
1312 goto do_body;
1313 }
1314
1315 n = static_cast<std::size_t>(h_.md.payload_size);
1316 }
1317 // complete
1318 BOOST_ASSERT(body_buf_ == &cb0_);
1319 auto rv = sink_->write(
1320 body_buf_->data(), true);
1321 BOOST_ASSERT(rv.ec.failed() ||
1322 rv.bytes == body_buf_->size());
1323 body_buf_->consume(rv.bytes);
1324 if(rv.ec.failed())
1325 {
1326 ec = rv.ec;
1327 st_ = state::reset; // unrecoverable
1328 return;
1329 }
1330 st_ = state::complete;
1331 return;
1332 }
1333
1334 // VFALCO TODO
1335 detail::throw_logic_error();
1336 }
1337
1338 2491 case state::complete:
1339 {
1340 // This is a no-op except when set_body
1341 // was called and we have in-place data.
1342
2/3
✓ Branch 0 taken 2195 times.
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
2491 switch(how_)
1343 {
1344 2195 default:
1345 case how::in_place:
1346 2195 break;
1347
1348 296 case how::elastic:
1349 {
1350
1/2
✓ Branch 1 taken 296 times.
✗ Branch 2 not taken.
296 if(body_buf_->size() == 0)
1351 296 break;
1352 BOOST_ASSERT(eb_->size() == 0);
1353 auto n = buffers::buffer_copy(
1354 eb_->prepare(
1355 body_buf_->size()),
1356 body_buf_->data());
1357 body_buf_->consume(n);
1358 break;
1359 }
1360
1361 case how::sink:
1362 {
1363 if(body_buf_->size() == 0)
1364 break;
1365 auto rv = sink_->write(
1366 body_buf_->data(), false);
1367 body_buf_->consume(rv.bytes);
1368 if(rv.ec.failed())
1369 {
1370 ec = rv.ec;
1371 st_ = state::reset; // unrecoverable
1372 return;
1373 }
1374 break;
1375 }
1376 }
1377 }
1378 }
1379 }
1380
1381 //------------------------------------------------
1382
1383 auto
1384 37962 parser::
1385 pull_body() ->
1386 const_buffers_type
1387 {
1388
1/2
✓ Branch 0 taken 37962 times.
✗ Branch 1 not taken.
37962 switch(st_)
1389 {
1390 37962 case state::body:
1391 case state::complete:
1392
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37962 times.
37962 if(how_ != how::in_place)
1393 detail::throw_logic_error();
1394 37962 cbp_ = buffers::prefix(body_buf_->data(),
1395 37962 static_cast<std::size_t>(body_avail_));
1396 37962 return const_buffers_type{ cbp_ };
1397 default:
1398 detail::throw_logic_error();
1399 }
1400 }
1401
1402 void
1403 37962 parser::
1404 consume_body(std::size_t n)
1405 {
1406
1/2
✓ Branch 0 taken 37962 times.
✗ Branch 1 not taken.
37962 switch(st_)
1407 {
1408 37962 case state::body:
1409 case state::complete:
1410
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37962 times.
37962 if(how_ != how::in_place)
1411 detail::throw_logic_error();
1412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 37962 times.
37962 BOOST_ASSERT(n <= body_avail_);
1413 37962 body_buf_->consume(n);
1414 37962 body_avail_ -= n;
1415 37962 return;
1416 default:
1417 detail::throw_logic_error();
1418 }
1419 }
1420
1421 core::string_view
1422 1344 parser::
1423 body() const noexcept
1424 {
1425
2/2
✓ Branch 0 taken 349 times.
✓ Branch 1 taken 995 times.
1344 switch(st_)
1426 {
1427 349 default:
1428 case state::reset:
1429 case state::start:
1430 case state::header:
1431 case state::body:
1432 case state::set_body:
1433 // not complete
1434 349 return {};
1435
1436 995 case state::complete:
1437
2/2
✓ Branch 0 taken 346 times.
✓ Branch 1 taken 649 times.
995 if(how_ != how::in_place)
1438 {
1439 // not in_place
1440 346 return {};
1441 }
1442 649 auto cbp = body_buf_->data();
1443
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[1].size() == 0);
1444
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 649 times.
649 BOOST_ASSERT(cbp[0].size() == body_avail_);
1445 649 return core::string_view(
1446 static_cast<char const*>(
1447 649 cbp[0].data()),
1448 1298 static_cast<std::size_t>(body_avail_));
1449 }
1450 }
1451
1452 core::string_view
1453 parser::
1454 release_buffered_data() noexcept
1455 {
1456 return {};
1457 }
1458
1459 //------------------------------------------------
1460 //
1461 // Implementation
1462 //
1463 //------------------------------------------------
1464
1465 auto
1466 314 parser::
1467 safe_get_header() const ->
1468 detail::header const*
1469 {
1470 // headers must be received
1471
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() ||
1472 314 fb_.size() == 0) // happens on eof
1473 detail::throw_logic_error();
1474
1475 314 return &h_;
1476 }
1477
1478 bool
1479 93116 parser::
1480 is_plain() const noexcept
1481 {
1482
1/2
✓ Branch 0 taken 93116 times.
✗ Branch 1 not taken.
186232 return ! filt_ &&
1483
2/2
✓ Branch 0 taken 46768 times.
✓ Branch 1 taken 46348 times.
93116 h_.md.payload !=
1484 93116 payload::chunked;
1485 }
1486
1487 // Called immediately after complete headers are received
1488 // to setup the circular buffers for subsequent operations.
1489 // We leave fb_ as-is to indicate whether any data was
1490 // received before eof.
1491 //
1492 void
1493 9538 parser::
1494 on_headers(
1495 system::error_code& ec)
1496 {
1497 // overread currently includes any and all octets that
1498 // extend beyond the current end of the header
1499 // this can include associated body octets for the
1500 // current message or octets of the next message in the
1501 // stream, e.g. pipelining is being used
1502 9538 auto const overread = fb_.size() - h_.size;
1503
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9538 times.
9538 BOOST_ASSERT(
1504 overread <= svc_.max_overread());
1505
1506 // metadata error
1507
2/2
✓ Branch 0 taken 120 times.
✓ Branch 1 taken 9418 times.
9538 if(h_.md.payload == payload::error)
1508 {
1509 // VFALCO This needs looking at
1510 240 ec = BOOST_HTTP_PROTO_ERR(
1511 error::bad_payload);
1512 120 st_ = state::reset; // unrecoverable
1513 5449 return;
1514 }
1515
1516 // reserve headers + table
1517
1/2
✓ Branch 1 taken 9418 times.
✗ Branch 2 not taken.
9418 ws_.reserve_front(h_.size);
1518
1/2
✓ Branch 2 taken 9418 times.
✗ Branch 3 not taken.
9418 ws_.reserve_back(h_.table_space());
1519
1520 // no payload
1521
2/2
✓ Branch 0 taken 8553 times.
✓ Branch 1 taken 865 times.
9418 if( h_.md.payload == payload::none ||
1522
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8553 times.
8553 head_response_ )
1523 {
1524 // set cb0_ to overread
1525 1730 cb0_ = {
1526 865 ws_.data(),
1527
1/2
✓ Branch 2 taken 865 times.
✗ Branch 3 not taken.
865 overread + fb_.capacity(),
1528 overread };
1529 865 body_avail_ = 0;
1530 865 body_total_ = 0;
1531 865 body_buf_ = &cb0_;
1532 865 st_ = state::complete;
1533 865 return;
1534 }
1535
1536 // calculate filter
1537 8553 filt_ = nullptr; // VFALCO TODO
1538
1539
2/2
✓ Branch 1 taken 4464 times.
✓ Branch 2 taken 4089 times.
8553 if(is_plain())
1540 {
1541 // plain payload
1542
2/2
✓ Branch 0 taken 4229 times.
✓ Branch 1 taken 235 times.
4464 if(h_.md.payload == payload::size)
1543 {
1544 4229 if(h_.md.payload_size >
1545
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4229 times.
4229 svc_.cfg.body_limit)
1546 {
1547 ec = BOOST_HTTP_PROTO_ERR(
1548 error::body_too_large);
1549 st_ = state::reset; // unrecoverable
1550 return;
1551 }
1552
1553 // for plain messages with a known size,, we can
1554 // get away with only using cb0_ as our input
1555 // area and leaving cb1_ blank
1556
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4229 times.
4229 BOOST_ASSERT(fb_.max_size() >= h_.size);
1557
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4229 times.
4229 BOOST_ASSERT(
1558 fb_.max_size() - h_.size ==
1559 overread + fb_.capacity());
1560
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 4229 times.
4229 BOOST_ASSERT(fb_.data().data() == h_.buf);
1561
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4229 times.
4229 BOOST_ASSERT(svc_.max_codec == 0);
1562 auto cap =
1563 4229 (overread + fb_.capacity()) + // reuse previously designated storage
1564 4229 svc_.cfg.min_buffer + // minimum buffer size for prepare() calls
1565 4229 svc_.max_codec; // tentatively we can delete this
1566
1567
6/6
✓ Branch 0 taken 3688 times.
✓ Branch 1 taken 541 times.
✓ Branch 2 taken 2455 times.
✓ Branch 3 taken 1233 times.
✓ Branch 4 taken 2455 times.
✓ Branch 5 taken 1774 times.
7917 if( cap > h_.md.payload_size &&
1568 3688 cap - h_.md.payload_size >= svc_.max_overread() )
1569 {
1570 // we eagerly process octets as they arrive,
1571 // so it's important to limit potential
1572 // overread as applying a transformation algo
1573 // can be prohibitively expensive
1574 2455 cap =
1575 2455 static_cast<std::size_t>(h_.md.payload_size) +
1576 2455 svc_.max_overread();
1577 }
1578
1579
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4229 times.
4229 BOOST_ASSERT(cap <= ws_.size());
1580
1581
1/2
✓ Branch 2 taken 4229 times.
✗ Branch 3 not taken.
4229 cb0_ = { ws_.data(), cap, overread };
1582 4229 cb1_ = {};
1583
1584 4229 body_buf_ = &cb0_;
1585 4229 body_avail_ = cb0_.size();
1586
2/2
✓ Branch 0 taken 2305 times.
✓ Branch 1 taken 1924 times.
4229 if( body_avail_ >= h_.md.payload_size)
1587 2305 body_avail_ = h_.md.payload_size;
1588
1589 4229 body_total_ = body_avail_;
1590 4229 payload_remain_ =
1591 4229 h_.md.payload_size - body_total_;
1592
1593 4229 st_ = state::body;
1594 4229 return;
1595 }
1596
1597 // overread is not applicable
1598
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 235 times.
235 BOOST_ASSERT(
1599 h_.md.payload == payload::to_eof);
1600 auto const n0 =
1601 235 fb_.capacity() - h_.size +
1602 235 svc_.cfg.min_buffer +
1603 235 svc_.max_codec;
1604
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 235 times.
235 BOOST_ASSERT(n0 <= ws_.size());
1605
1/2
✓ Branch 2 taken 235 times.
✗ Branch 3 not taken.
235 cb0_ = { ws_.data(), n0, overread };
1606 235 body_buf_ = &cb0_;
1607 235 body_avail_ = cb0_.size();
1608 235 body_total_ = body_avail_;
1609 235 st_ = state::body;
1610 235 return;
1611 }
1612
1613 // buffered payload
1614
1615 // TODO: need to handle the case where we have so much
1616 // overread or such an initially large chunk that we
1617 // don't have enough room in cb1_ for the output
1618 // perhaps we just return with an error and ask the user
1619 // to attach a body style
1620 4089 auto size = ws_.size();
1621
1622 4089 auto n0 = (std::max)(svc_.cfg.min_buffer, overread);
1623 4089 n0 = (std::max)(n0, size / 2);
1624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4089 times.
4089 if( filt_)
1625 n0 += svc_.max_codec;
1626
1627 4089 auto n1 = size - n0;
1628
1629 // BOOST_ASSERT(n0 <= svc_.max_overread());
1630
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4089 times.
4089 BOOST_ASSERT(n0 + n1 <= ws_.size());
1631
1/2
✓ Branch 2 taken 4089 times.
✗ Branch 3 not taken.
4089 cb0_ = { ws_.data(), n0, overread };
1632 4089 cb1_ = { ws_.data() + n0, n1 };
1633 4089 body_buf_ = &cb1_;
1634 // body_buf_ = nullptr;
1635 4089 body_avail_ = 0;
1636 4089 body_total_ = 0;
1637 4089 st_ = state::body;
1638 }
1639
1640 // Called at the end of set_body
1641 void
1642 299 parser::
1643 on_set_body()
1644 {
1645 // This function is called after all
1646 // limit checking and calculation of
1647 // chunked or filter.
1648
1649
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 299 times.
299 BOOST_ASSERT(got_header());
1650
1651 299 nprepare_ = 0; // invalidate
1652
1653
1/2
✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
299 if(how_ == how::elastic)
1654 {
1655
2/2
✓ Branch 0 taken 58 times.
✓ Branch 1 taken 241 times.
299 if(h_.md.payload == payload::none)
1656 {
1657
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 58 times.
58 BOOST_ASSERT(st_ == state::complete);
1658 58 return;
1659 }
1660
1661 241 st_ = state::set_body;
1662 241 return;
1663 }
1664
1665 if(how_ == how::sink)
1666 {
1667 if(h_.md.payload == payload::none)
1668 {
1669 BOOST_ASSERT(st_ == state::complete);
1670 // force a trip through parse so
1671 // we can calculate any error.
1672 st_ = state::set_body;
1673 return;
1674 }
1675
1676 st_ = state::set_body;
1677 return;
1678 }
1679
1680 // VFALCO TODO
1681 detail::throw_logic_error();
1682 }
1683
1684 void
1685 238 parser::
1686 init_dynamic(
1687 system::error_code& ec)
1688 {
1689 // attempt to transfer in-place
1690 // body into the dynamic buffer.
1691
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 238 times.
238 BOOST_ASSERT(
1692 body_avail_ == body_buf_->size());
1693
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 238 times.
238 BOOST_ASSERT(
1694 body_total_ == body_avail_);
1695 auto const space_left =
1696 238 eb_->max_size() - eb_->size();
1697
1698
2/2
✓ Branch 0 taken 121 times.
✓ Branch 1 taken 117 times.
238 if(h_.md.payload == payload::size)
1699 {
1700
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 120 times.
121 if(space_left < h_.md.payload_size)
1701 {
1702 2 ec = BOOST_HTTP_PROTO_ERR(
1703 error::buffer_overflow);
1704 1 return;
1705 }
1706 // reserve the full size
1707 120 eb_->prepare(static_cast<std::size_t>(h_.md.payload_size));
1708 // transfer in-place body
1709 120 auto n = static_cast<std::size_t>(body_avail_);
1710
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 if( n > h_.md.payload_size)
1711 n = static_cast<std::size_t>(h_.md.payload_size);
1712
1/2
✓ Branch 2 taken 120 times.
✗ Branch 3 not taken.
120 eb_->commit(
1713 buffers::buffer_copy(
1714
1/2
✓ Branch 1 taken 120 times.
✗ Branch 2 not taken.
120 eb_->prepare(n),
1715 120 body_buf_->data()));
1716
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_avail_ == n);
1717
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(body_total_ == n);
1718
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 120 times.
120 BOOST_ASSERT(payload_remain_ ==
1719 h_.md.payload_size - n);
1720 120 body_buf_->consume(n);
1721 120 body_avail_ = 0;
1722
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 111 times.
120 if(n < h_.md.payload_size)
1723 {
1724
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
9 BOOST_ASSERT(
1725 body_buf_->size() == 0);
1726 9 st_ = state::body;
1727 9 return;
1728 }
1729 // complete
1730 111 st_ = state::complete;
1731 111 return;
1732 }
1733
1734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 BOOST_ASSERT(h_.md.payload ==
1735 payload::to_eof);
1736
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 117 times.
117 if(space_left < body_avail_)
1737 {
1738 ec = BOOST_HTTP_PROTO_ERR(
1739 error::buffer_overflow);
1740 return;
1741 }
1742
1/2
✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
117 eb_->commit(
1743 buffers::buffer_copy(
1744
1/2
✓ Branch 1 taken 117 times.
✗ Branch 2 not taken.
117 eb_->prepare(static_cast<std::size_t>(body_avail_)),
1745 117 body_buf_->data()));
1746 117 body_buf_->consume(static_cast<std::size_t>(body_avail_));
1747 117 body_avail_ = 0;
1748
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 117 times.
117 BOOST_ASSERT(
1749 body_buf_->size() == 0);
1750 117 st_ = state::body;
1751 }
1752
1753 } // http_proto
1754 } // boost
1755