GCC Code Coverage Report


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

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