GCC Code Coverage Report


Directory: libs/http_proto/include/boost/http_proto/
File: boost/http_proto/impl/parser.ipp
Date: 2023-03-01 04:29:23
Exec Total Coverage
Lines: 316 503 62.8%
Functions: 19 28 67.9%
Branches: 154 331 46.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 #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
11 #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
12
13 #include <boost/http_proto/parser.hpp>
14 #include <boost/http_proto/context.hpp>
15 #include <boost/http_proto/error.hpp>
16 #include <boost/http_proto/service/zlib_service.hpp>
17 #include <boost/http_proto/detail/except.hpp>
18 #include <boost/buffers/buffer_copy.hpp>
19 #include <boost/url/grammar/ci_string.hpp>
20 #include <boost/assert.hpp>
21 #include <memory>
22
23 namespace boost {
24 namespace http_proto {
25
26 /*
27 Principles for fixed-size buffer design
28
29 axiom 1:
30 To read data you must have a buffer.
31
32 axiom 2:
33 The size of the HTTP header is not
34 known in advance.
35
36 conclusion 3:
37 A single I/O can produce a complete
38 HTTP header and additional payload
39 data.
40
41 conclusion 4:
42 A single I/O can produce multiple
43 complete HTTP headers, complete
44 payloads, and a partial header or
45 payload.
46
47 axiom 5:
48 A process is in one of two states:
49 1. at or below capacity
50 2. above capacity
51
52 axiom 6:
53 A program which can allocate an
54 unbounded number of resources can
55 go above capacity.
56
57 conclusion 7:
58 A program can guarantee never going
59 above capacity if all resources are
60 provisioned at program startup.
61
62 corollary 8:
63 `parser` and `serializer` should each
64 allocate a single buffer of calculated
65 size, and never resize it.
66
67 axiom #:
68 A parser and a serializer are always
69 used in pairs.
70 */
71
72 //------------------------------------------------
73 /*
74 Buffer Usage
75
76 | | begin
77 | H | p | | f | read headers
78 | H | p | | T | f | set T body
79 | H | p | | C | T | f | make codec C
80 | H | p | b | C | T | f | decode p into b
81 | H | p | b | C | T | f | read/parse loop
82 | H | | T | f | destroy codec
83 | H | | T | f | finished
84
85 H headers
86 C codec
87 T body
88 f table
89 p partial payload
90 b body data
91
92 "payload" is the bytes coming in from
93 the stream.
94
95 "body" is the logical body, after transfer
96 encoding is removed. This can be the
97 same as the payload.
98
99 A "plain payload" is when the payload and
100 body are identical (no transfer encodings).
101
102 A "buffered payload" is any payload which is
103 not plain. A second buffer is required
104 for reading.
105 */
106 //------------------------------------------------
107
108 class parser_service
109 : public service
110 {
111 public:
112 parser::config_base cfg;
113 std::size_t space_needed = 0;
114 std::size_t max_codec = 0;
115 zlib::deflate_decoder_service const*
116 deflate_svc = nullptr;
117
118 parser_service(
119 context& ctx,
120 parser::config_base const& cfg_);
121
122 std::size_t
123 83721 max_overread() const noexcept
124 {
125 return
126 83721 cfg.headers.max_size +
127 83721 cfg.min_buffer;
128 }
129 };
130
131 6 parser_service::
132 parser_service(
133 context& ctx,
134 6 parser::config_base const& cfg_)
135 6 : cfg(cfg_)
136 {
137 /*
138 | fb | cb0 | cb1 | C | T | f |
139
140 fb flat_buffer headers.max_size
141 cb0 circular_buffer min_buffer
142 cb1 circular_buffer min_buffer
143 C codec max_codec
144 T body max_type_erase
145 f table max_table_space
146
147 */
148 // validate
149 //if(cfg.min_prepare > cfg.max_prepare)
150 //detail::throw_invalid_argument();
151
152
1/2
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
6 if( cfg.min_buffer < 1 ||
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 cfg.min_buffer > cfg.body_limit)
154 detail::throw_invalid_argument();
155
156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(cfg.max_prepare < 1)
157 detail::throw_invalid_argument();
158
159 // VFALCO TODO OVERFLOW CHECING
160 {
161 //fb_.size() - h_.size +
162 //svc_.cfg.min_buffer +
163 //svc_.cfg.min_buffer +
164 //svc_.max_codec;
165 }
166
167 // VFALCO OVERFLOW CHECKING ON THIS
168 6 space_needed +=
169
1/2
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
6 cfg.headers.valid_space_needed();
170
171 // cb0_, cb1_
172 // VFALCO OVERFLOW CHECKING ON THIS
173 6 space_needed +=
174 6 cfg.min_buffer +
175 cfg.min_buffer;
176
177 // T
178 6 space_needed += cfg.max_type_erase;
179
180 // max_codec
181 {
182
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
6 if(cfg.apply_deflate_decoder)
183 {
184 deflate_svc = &ctx.get_service<
185 zlib::deflate_decoder_service>();
186 auto const n =
187 deflate_svc->space_needed();
188 if( max_codec < n)
189 max_codec = n;
190 }
191 }
192 6 space_needed += max_codec;
193 6 }
194
195 void
196 6 install_parser_service(
197 context& ctx,
198 parser::config_base const& cfg)
199 {
200 ctx.make_service<
201 6 parser_service>(cfg);
202 6 }
203
204 //------------------------------------------------
205
206 738 parser::
207 parser(
208 context& ctx,
209 738 detail::kind k)
210 : ctx_(ctx)
211 , svc_(ctx.get_service<
212 1476 parser_service>())
213 , h_(detail::empty{k})
214 738 , st_(state::reset)
215 {
216 738 auto const n =
217 738 svc_.space_needed;
218
1/2
✓ Branch 1 taken 738 times.
✗ Branch 2 not taken.
738 ws_.allocate(n);
219 738 h_.cap = n;
220 738 }
221
222 //------------------------------------------------
223 //
224 // Special Members
225 //
226 //------------------------------------------------
227
228 738 parser::
229 738 ~parser()
230 {
231 738 }
232
233 parser::
234 parser(
235 parser&&) noexcept = default;
236
237 //------------------------------------------------
238 //
239 // Modifiers
240 //
241 //------------------------------------------------
242
243 // prepare for a new stream
244 void
245 5355 parser::
246 reset() noexcept
247 {
248 5355 ws_.clear();
249 5355 st_ = state::start;
250 5355 got_eof_ = false;
251 5355 }
252
253 void
254 14280 parser::
255 start_impl(
256 bool head_response)
257 {
258 14280 std::size_t leftover = 0;
259
2/5
✗ Branch 0 not taken.
✓ Branch 1 taken 5355 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8925 times.
14280 switch(st_)
260 {
261 default:
262 case state::reset:
263 // reset must be called first
264 detail::throw_logic_error();
265
266 5355 case state::start:
267 // reset required on eof
268
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5355 times.
5355 if(got_eof_)
269 detail::throw_logic_error();
270 5355 break;
271
272 case state::header:
273 // start() called twice
274 detail::throw_logic_error();
275
276 case state::body:
277 case state::body_set:
278 // current message is incomplete
279 detail::throw_logic_error();
280
281 8925 case state::complete:
282 {
283 // remove partial body.
284
1/2
✓ Branch 0 taken 8925 times.
✗ Branch 1 not taken.
8925 if(body_buf_ == &cb0_)
285 8925 cb0_.consume(body_avail_);
286
287
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8925 times.
8925 if(cb0_.size() > 0)
288 {
289
290 // headers with no body
291 BOOST_ASSERT(h_.size > 0);
292 fb_.consume(h_.size);
293 leftover = fb_.size();
294 // move unused octets to front
295 buffers::buffer_copy(
296 buffers::mutable_buffer(
297 ws_.data(),
298 leftover),
299 fb_.data());
300 }
301 else
302 {
303 // leftover data after body
304 }
305 8925 break;
306 }
307 }
308
309 14280 ws_.clear();
310
311 28560 fb_ = {
312 14280 ws_.data(),
313 14280 svc_.cfg.headers.max_size +
314 14280 svc_.cfg.min_buffer,
315 leftover };
316
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 14280 times.
14280 BOOST_ASSERT(
317 fb_.capacity() == svc_.max_overread());
318
319 28560 h_ = detail::header(
320 14280 detail::empty{h_.kind});
321 14280 h_.buf = reinterpret_cast<
322 14280 char*>(ws_.data());
323 14280 h_.cbuf = h_.buf;
324 14280 h_.cap = ws_.size();
325
326
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 14280 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
14280 BOOST_ASSERT(! head_response ||
327 h_.kind == detail::kind::response);
328 14280 head_response_ = head_response;
329
330 14280 how_ = how::in_place;
331 14280 st_ = state::header;
332 14280 }
333
334 auto
335 42962 parser::
336 prepare() ->
337 mutable_buffers_type
338 {
339
2/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
✓ Branch 3 taken 2565 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
42962 switch(st_)
340 {
341 default:
342 case state::reset:
343 // reset must be called first
344 detail::throw_logic_error();
345
346 case state::start:
347 // start must be called first
348 detail::throw_logic_error();
349
350 40397 case state::header:
351 {
352
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40397 times.
40397 BOOST_ASSERT(h_.size <
353 svc_.cfg.headers.max_size);
354 40397 auto n = fb_.capacity() - fb_.size();
355
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
40397 BOOST_ASSERT(n <= svc_.max_overread());
356
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40397 times.
40397 if( n > svc_.cfg.max_prepare)
357 n = svc_.cfg.max_prepare;
358 40397 mbp_[0] = fb_.prepare(n);
359 40397 return mutable_buffers_type(
360 80794 &mbp_[0], 1);
361 }
362
363 2565 case state::body:
364 {
365 // buffered payload
366
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2565 times.
2565 if(body_buf_ != &cb0_)
367 {
368 auto n = cb0_.capacity() -
369 cb0_.size();
370 if( n > svc_.cfg.max_prepare)
371 n = svc_.cfg.max_prepare;
372 mbp_ = cb0_.prepare(n);
373 return mutable_buffers_type(mbp_);
374 }
375
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2565 times.
2565 BOOST_ASSERT(is_plain());
376
377 // plain payload
378
379
2/2
✓ Branch 0 taken 1191 times.
✓ Branch 1 taken 1374 times.
2565 if(how_ == how::in_place)
380 {
381 auto n =
382 1191 body_buf_->capacity() -
383 1191 body_buf_->size();
384
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1191 times.
1191 if( n > svc_.cfg.max_prepare)
385 n = svc_.cfg.max_prepare;
386 1191 mbp_ = body_buf_->prepare(n);
387 1191 return mutable_buffers_type(mbp_);
388 }
389
390
1/2
✓ Branch 0 taken 1374 times.
✗ Branch 1 not taken.
1374 if(how_ == how::dynamic)
391 {
392 // Overreads are not allowed, or
393 // else the caller will see extra
394 // unrelated data.
395
396
2/2
✓ Branch 0 taken 615 times.
✓ Branch 1 taken 759 times.
1374 if(h_.md.payload == payload::size)
397 {
398 // set_body moves avail to dyn
399
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 615 times.
615 BOOST_ASSERT(body_buf_->size() == 0);
400
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 615 times.
615 BOOST_ASSERT(body_avail_ == 0);
401 615 auto n = payload_remain_;
402
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 615 times.
615 if( n > svc_.cfg.max_prepare)
403 n = svc_.cfg.max_prepare;
404 615 return dyn_->prepare(n);
405 }
406
407 // heuristic size
408 759 std::size_t n = 0;
409
1/2
✓ Branch 0 taken 759 times.
✗ Branch 1 not taken.
759 if(! got_eof_)
410 {
411 759 n = svc_.cfg.min_buffer;
412
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 759 times.
759 if( n > svc_.cfg.max_prepare)
413 n = svc_.cfg.max_prepare;
414 std::size_t avail;
415 759 avail =
416 759 dyn_->max_size() - dyn_->size();
417
1/2
✓ Branch 0 taken 759 times.
✗ Branch 1 not taken.
759 if( n > avail)
418 759 n = avail;
419 759 avail =
420 759 dyn_->capacity() - dyn_->size();
421
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 759 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
759 if( n > avail &&
422 avail != 0)
423 n = avail;
424
2/2
✓ Branch 0 taken 308 times.
✓ Branch 1 taken 451 times.
759 if(n == 0)
425 {
426 // dynamic buffer is full
427
2/2
✓ Branch 0 taken 273 times.
✓ Branch 1 taken 35 times.
308 if(body_avail_ == 0)
428 {
429 // attempt a 1 byte read so
430 // we can detect overflow
431
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 273 times.
273 BOOST_ASSERT(
432 body_buf_->size() == 0);
433 273 mbp_ = body_buf_->prepare(1);
434 return
435 273 mutable_buffers_type(mbp_);
436 }
437 return
438 35 mutable_buffers_type{};
439 }
440 }
441 451 return dyn_->prepare(n);
442 }
443
444 // VFALCO TODO
445 if(how_ == how::pull)
446 detail::throw_logic_error();
447
448 // VFALCO TODO
449 detail::throw_logic_error();
450 }
451
452 case state::body_set:
453 {
454 BOOST_ASSERT(how_ != how::in_place);
455 BOOST_ASSERT(how_ != how::dynamic);
456 if(how_ == how::sink)
457 {
458 // this is a no-op, to get the
459 // caller to call parse next.
460 return mutable_buffers_type{};
461 }
462 detail::throw_logic_error();
463 }
464
465 case state::complete:
466 // We allow the call for callers
467 // who want normalized usage, but
468 // just return a 0-sized sequence.
469 return mutable_buffers_type{};
470 }
471 }
472
473 void
474 42962 parser::
475 commit(
476 std::size_t n)
477 {
478 // Can't commit after eof
479
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 42962 times.
42962 if(got_eof_)
480 detail::throw_logic_error();
481
482
2/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
✓ Branch 3 taken 2565 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
42962 switch(st_)
483 {
484 default:
485 case state::reset:
486 // reset must be called first
487 detail::throw_logic_error();
488
489 case state::start:
490 // forgot to call start()
491 detail::throw_logic_error();
492
493 40397 case state::header:
494 40397 fb_.commit(n);
495 40397 break;
496
497 2565 case state::body:
498
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2565 times.
2565 if(! is_plain())
499 {
500 // buffered payload
501 cb0_.commit(n);
502 break;
503 }
504
505 // plain payload
506
507
2/2
✓ Branch 0 taken 1191 times.
✓ Branch 1 taken 1374 times.
2565 if(how_ == how::in_place)
508 {
509 1191 cb0_.commit(n);
510 1191 break;
511 }
512
513
1/2
✓ Branch 0 taken 1374 times.
✗ Branch 1 not taken.
1374 if(how_ == how::dynamic)
514 {
515
2/2
✓ Branch 2 taken 1066 times.
✓ Branch 3 taken 308 times.
1374 if(dyn_->size() < dyn_->max_size())
516 {
517
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1066 times.
1066 BOOST_ASSERT(body_avail_ == 0);
518
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1066 times.
1066 BOOST_ASSERT(
519 body_buf_->size() == 0);
520 1066 dyn_->commit(n);
521 }
522 else
523 {
524 308 body_buf_->commit(n);
525 308 body_avail_ += n;
526 }
527 1374 body_total_ += n;
528
2/2
✓ Branch 0 taken 615 times.
✓ Branch 1 taken 759 times.
1374 if(h_.md.payload == payload::size)
529 {
530
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 615 times.
615 BOOST_ASSERT(
531 n <= payload_remain_);
532 615 payload_remain_ -= n;
533
2/2
✓ Branch 0 taken 585 times.
✓ Branch 1 taken 30 times.
615 if(payload_remain_ == 0)
534 585 st_ = state::complete;
535 }
536 1374 break;
537 }
538 if(how_ == how::sink)
539 {
540 cb0_.commit(n);
541 break;
542 }
543 if(how_ == how::pull)
544 {
545 // VFALCO TODO
546 detail::throw_logic_error();
547 }
548 break;
549
550 case state::body_set:
551 BOOST_ASSERT(n == 0);
552 break;
553
554 case state::complete:
555 // intended no-op
556 break;
557 }
558 42962 }
559
560 void
561 4356 parser::
562 commit_eof()
563 {
564
1/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 4356 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
4356 switch(st_)
565 {
566 default:
567 case state::reset:
568 // reset must be called first
569 detail::throw_logic_error();
570
571 case state::start:
572 // forgot to call prepare()
573 detail::throw_logic_error();
574
575 case state::header:
576 got_eof_ = true;
577 break;
578
579 4356 case state::body:
580 4356 got_eof_ = true;
581 4356 break;
582
583 case state::body_set:
584 got_eof_ = true;
585 break;
586
587 case state::complete:
588 // can't commit eof when complete
589 detail::throw_logic_error();
590 }
591 4356 }
592
593 //-----------------------------------------------
594
595 // process input data then
596 // eof if input data runs out.
597 void
598 60007 parser::
599 parse(
600 system::error_code& ec)
601 {
602 60007 ec = {};
603
3/6
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
✓ Branch 3 taken 6336 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 13274 times.
60007 switch(st_)
604 {
605 default:
606 case state::reset:
607 // reset must be called first
608 detail::throw_logic_error();
609
610 case state::start:
611 // start must be called first
612 detail::throw_logic_error();
613
614 40397 case state::header:
615 {
616
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
40397 BOOST_ASSERT(h_.buf == static_cast<
617 void const*>(ws_.data()));
618
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 40397 times.
40397 BOOST_ASSERT(h_.cbuf == static_cast<
619 void const*>(ws_.data()));
620 40397 auto const new_size = fb_.size();
621 40397 h_.parse(new_size, svc_.cfg.headers, ec);
622
2/2
✓ Branch 2 taken 26117 times.
✓ Branch 3 taken 14280 times.
40397 if(ec == condition::need_more_input)
623 {
624
1/2
✓ Branch 0 taken 26117 times.
✗ Branch 1 not taken.
26117 if(! got_eof_)
625 {
626 // headers incomplete
627 26117 return;
628 }
629 if(h_.size == 0)
630 {
631 // stream closed cleanly
632 ec = BOOST_HTTP_PROTO_ERR(
633 error::end_of_stream);
634 return;
635 }
636
637 // stream closed with a
638 // partial message received
639 ec = BOOST_HTTP_PROTO_ERR(
640 error::incomplete);
641 return;
642 }
643
2/2
✓ Branch 1 taken 128 times.
✓ Branch 2 taken 14152 times.
14280 if(ec.failed())
644 {
645 // other error,
646 //
647 // VFALCO map this to a bad
648 // request or bad response error?
649 //
650 128 st_ = state::reset; // unrecoverable
651 128 return;
652 }
653
654 // headers are complete
655 14152 on_headers(ec);
656
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14152 times.
14152 if(ec.failed())
657 return;
658
2/2
✓ Branch 0 taken 2084 times.
✓ Branch 1 taken 12068 times.
14152 if(st_ == state::complete)
659 2084 break;
660 BOOST_FALLTHROUGH;
661 }
662
663 case state::body:
664 {
665 12068 do_body:
666
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18404 times.
18404 BOOST_ASSERT(st_ == state::body);
667
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18404 times.
18404 BOOST_ASSERT(
668 h_.md.payload != payload::none);
669
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18404 times.
18404 BOOST_ASSERT(
670 h_.md.payload != payload::error);
671
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18404 times.
18404 if(h_.md.payload == payload::chunked)
672 {
673 // VFALCO parse chunked
674 detail::throw_logic_error();
675 }
676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18404 times.
18404 else if(filt_)
677 {
678 // VFALCO TODO apply filter
679 detail::throw_logic_error();
680 }
681
682
2/2
✓ Branch 0 taken 15570 times.
✓ Branch 1 taken 2834 times.
18404 if(how_ == how::in_place)
683 {
684
2/2
✓ Branch 0 taken 8061 times.
✓ Branch 1 taken 7509 times.
15570 if(h_.md.payload == payload::size)
685 {
686 8061 if(cb0_.size() <
687
2/2
✓ Branch 0 taken 1200 times.
✓ Branch 1 taken 6861 times.
8061 h_.md.payload_size)
688 {
689 1200 body_avail_ = cb0_.size();
690
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1200 times.
1200 if(cb0_.capacity() == 0)
691 {
692 // in_place buffer limit
693 ec = BOOST_HTTP_PROTO_ERR(
694 error::in_place_overflow);
695 return;
696 }
697 2400 ec = BOOST_HTTP_PROTO_ERR(
698 error::need_data);
699 1200 return;
700 }
701 6861 body_avail_ = h_.md.payload_size;
702 6861 st_ = state::complete;
703 6861 break;
704 }
705 7509 body_avail_ = cb0_.size();
706
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7509 times.
7509 if(body_avail_ > svc_.cfg.body_limit)
707 {
708 ec = BOOST_HTTP_PROTO_ERR(
709 error::body_too_large);
710 st_ = state::reset; // unrecoverable
711 return;
712 }
713
1/2
✓ Branch 0 taken 7509 times.
✗ Branch 1 not taken.
7509 if( h_.md.payload == payload::chunked ||
714
2/2
✓ Branch 0 taken 5198 times.
✓ Branch 1 taken 2311 times.
7509 ! got_eof_)
715 {
716 10396 ec = BOOST_HTTP_PROTO_ERR(
717 error::need_data);
718 5198 return;
719 }
720 2311 st_ = state::complete;
721 2311 break;
722 }
723
724
1/2
✓ Branch 0 taken 2834 times.
✗ Branch 1 not taken.
2834 if(how_ == how::dynamic)
725 {
726 // state already updated in commit
727
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2804 times.
2834 if(h_.md.payload == payload::size)
728 {
729
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 BOOST_ASSERT(body_avail_ == 0);
730
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 BOOST_ASSERT(body_total_ <
731 h_.md.payload_size);
732
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 BOOST_ASSERT(
733 payload_remain_ > 0);
734
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
30 if(got_eof_)
735 {
736 ec = BOOST_HTTP_PROTO_ERR(
737 error::incomplete);
738 return;
739 }
740 30 return;
741 }
742
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2804 times.
2804 BOOST_ASSERT(
743 h_.md.payload == payload::to_eof);
744
4/4
✓ Branch 2 taken 2108 times.
✓ Branch 3 taken 696 times.
✓ Branch 4 taken 861 times.
✓ Branch 5 taken 1943 times.
4912 if( dyn_->size() == dyn_->max_size() &&
745
2/2
✓ Branch 0 taken 861 times.
✓ Branch 1 taken 1247 times.
2108 body_avail_ > 0)
746 {
747 1722 ec = BOOST_HTTP_PROTO_ERR(
748 error::buffer_overflow);
749 861 st_ = state::reset; // unrecoverable
750 861 return;
751 }
752
2/2
✓ Branch 0 taken 1450 times.
✓ Branch 1 taken 493 times.
1943 if(got_eof_)
753 {
754
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1450 times.
1450 BOOST_ASSERT(body_avail_ == 0);
755 1450 st_ = state::complete;
756 1450 break;
757 }
758
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 493 times.
493 BOOST_ASSERT(body_avail_ == 0);
759 493 break;
760 }
761
762 // VFALCO TODO
763 detail::throw_logic_error();
764 }
765
766 case state::body_set:
767 {
768 // transfer in_place data into set body
769
770 // this is handled in on_set_body
771 BOOST_ASSERT(how_ != how::dynamic);
772
773 if(how_ == how::sink)
774 {
775 auto n = body_buf_->size();
776 if(h_.md.payload == payload::size)
777 {
778 // sink_->size_hint(h_.md.payload_size, ec);
779
780 if(n < h_.md.payload_size)
781 {
782 auto rv = sink_->write(
783 body_buf_->data(), false);
784 BOOST_ASSERT(rv.ec.failed() ||
785 rv.bytes == body_buf_->size());
786 BOOST_ASSERT(
787 rv.bytes >= body_avail_);
788 BOOST_ASSERT(
789 rv.bytes < payload_remain_);
790 body_buf_->consume(rv.bytes);
791 body_avail_ -= rv.bytes;
792 body_total_ += rv.bytes;
793 payload_remain_ -= rv.bytes;
794 if(rv.ec.failed())
795 {
796 ec = rv.ec;
797 st_ = state::reset; // unrecoverable
798 return;
799 }
800 st_ = state::body;
801 goto do_body;
802 }
803
804 n = h_.md.payload_size;
805 }
806 // complete
807 BOOST_ASSERT(body_buf_ == &cb0_);
808 auto rv = sink_->write(
809 body_buf_->data(), true);
810 BOOST_ASSERT(rv.ec.failed() ||
811 rv.bytes == body_buf_->size());
812 body_buf_->consume(rv.bytes);
813 if(rv.ec.failed())
814 {
815 ec = rv.ec;
816 st_ = state::reset; // unrecoverable
817 return;
818 }
819 st_ = state::complete;
820 return;
821 }
822
823 // VFALCO TODO
824 detail::throw_logic_error();
825 }
826
827 13274 case state::complete:
828 {
829 // This is a no-op except when set_body
830 // was called and we have in-place data.
831
2/4
✓ Branch 0 taken 6775 times.
✓ Branch 1 taken 6499 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
13274 switch(how_)
832 {
833 6775 default:
834 case how::in_place:
835 6775 break;
836
837 6499 case how::dynamic:
838 {
839
1/2
✓ Branch 1 taken 6499 times.
✗ Branch 2 not taken.
6499 if(body_buf_->size() == 0)
840 6499 break;
841 BOOST_ASSERT(dyn_->size() == 0);
842 auto n = buffers::buffer_copy(
843 dyn_->prepare(
844 body_buf_->size()),
845 body_buf_->data());
846 body_buf_->consume(n);
847 break;
848 }
849
850 case how::sink:
851 {
852 if(body_buf_->size() == 0)
853 break;
854 auto rv = sink_->write(
855 body_buf_->data(), false);
856 body_buf_->consume(rv.bytes);
857 if(rv.ec.failed())
858 {
859 ec = rv.ec;
860 st_ = state::reset; // unrecoverable
861 return;
862 }
863 break;
864 }
865
866 case how::pull:
867 // VFALCO TODO
868 detail::throw_logic_error();
869 }
870 }
871 }
872 }
873
874 //------------------------------------------------
875
876 auto
877 parser::
878 pull_some() ->
879 const_buffers_type
880 {
881 return {};
882 }
883
884 core::string_view
885 29135 parser::
886 body() const noexcept
887 {
888
2/2
✓ Branch 0 taken 5792 times.
✓ Branch 1 taken 23343 times.
29135 switch(st_)
889 {
890 5792 default:
891 case state::reset:
892 case state::start:
893 case state::header:
894 case state::body:
895 case state::body_set:
896 // not complete
897 5792 return {};
898
899 23343 case state::complete:
900
2/2
✓ Branch 0 taken 9793 times.
✓ Branch 1 taken 13550 times.
23343 if(how_ != how::in_place)
901 {
902 // not in_place
903 9793 return {};
904 }
905 13550 auto cbp = body_buf_->data();
906
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 13550 times.
13550 BOOST_ASSERT(cbp[1].size() == 0);
907
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 13550 times.
13550 BOOST_ASSERT(cbp[0].size() >= body_avail_);
908 13550 return core::string_view(
909 static_cast<char const*>(
910 13550 cbp[0].data()),
911 27100 body_avail_);
912 }
913 }
914
915 core::string_view
916 parser::
917 release_buffered_data() noexcept
918 {
919 return {};
920 }
921
922 //------------------------------------------------
923 //
924 // Implementation
925 //
926 //------------------------------------------------
927
928 auto
929 55 parser::
930 safe_get_header() const ->
931 detail::header const*
932 {
933
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 55 times.
55 switch(st_)
934 {
935 default:
936 case state::start:
937 case state::header:
938 // Headers not received yet
939 detail::throw_logic_error();
940
941 55 case state::body:
942 case state::complete:
943 // VFALCO check if headers are discarded?
944 55 break;
945 }
946 55 return &h_;
947 }
948
949 // return true if payload is plain
950 bool
951 23973 parser::
952 is_plain() const noexcept
953 {
954 return
955
1/2
✓ Branch 0 taken 23973 times.
✗ Branch 1 not taken.
47946 h_.md.payload != payload::chunked &&
956
2/2
✓ Branch 0 taken 23232 times.
✓ Branch 1 taken 741 times.
47946 ! filt_;
957 }
958
959 // Called immediately after
960 // complete headers are received
961 void
962 14152 parser::
963 on_headers(
964 system::error_code& ec)
965 {
966 auto const overread =
967 14152 fb_.size() - h_.size;
968
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14152 times.
14152 BOOST_ASSERT(
969 overread <= svc_.max_overread());
970
971 // reserve headers + table
972 14152 ws_.reserve_front(h_.size);
973 14152 ws_.reserve_back(h_.table_space());
974
975 // no payload
976
2/2
✓ Branch 0 taken 12068 times.
✓ Branch 1 taken 2084 times.
14152 if( h_.md.payload == payload::none ||
977
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12068 times.
12068 head_response_)
978 {
979 // set cb0_ to overread
980 4168 cb0_ = {
981 2084 ws_.data(),
982 2084 fb_.capacity() - h_.size,
983 overread };
984 2084 body_avail_ = 0;
985 2084 body_total_ = 0;
986 2084 body_buf_ = &cb0_;
987 2084 st_ = state::complete;
988 2084 return;
989 }
990
991 // metadata error
992
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 12068 times.
12068 if(h_.md.payload == payload::error)
993 {
994 // VFALCO This needs looking at
995 ec = BOOST_HTTP_PROTO_ERR(
996 error::bad_payload);
997 st_ = state::reset; // unrecoverable
998 return;
999 }
1000
1001 // calculate filter
1002 12068 filt_ = nullptr;
1003
1004 // plain payload
1005
1/2
✓ Branch 1 taken 12068 times.
✗ Branch 2 not taken.
12068 if(is_plain())
1006 {
1007 // payload size is known
1008
2/2
✓ Branch 0 taken 7446 times.
✓ Branch 1 taken 4622 times.
12068 if(h_.md.payload == payload::size)
1009 {
1010 7446 if(h_.md.payload_size >
1011
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 7446 times.
7446 svc_.cfg.body_limit)
1012 {
1013 ec = BOOST_HTTP_PROTO_ERR(
1014 error::body_too_large);
1015 st_ = state::reset; // unrecoverable
1016 return;
1017 }
1018 auto n0 =
1019 7446 fb_.capacity() - h_.size +
1020 7446 svc_.cfg.min_buffer +
1021 7446 svc_.max_codec;
1022 // limit the capacity of cb0_ so
1023 // that going over max_overread
1024 // is impossible.
1025
3/6
✓ Branch 0 taken 7446 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 7446 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7446 times.
✗ Branch 5 not taken.
14892 if( n0 > h_.md.payload_size &&
1026 7446 n0 - h_.md.payload_size >=
1027 7446 svc_.max_overread())
1028 7446 n0 =
1029 7446 h_.md.payload_size +
1030 7446 svc_.max_overread();
1031
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 7446 times.
7446 BOOST_ASSERT(n0 <= ws_.size());
1032 7446 cb0_ = {
1033 7446 ws_.data(),
1034 n0,
1035 overread };
1036 7446 body_buf_ = &cb0_;
1037 7446 body_avail_ = cb0_.size();
1038
2/2
✓ Branch 0 taken 6276 times.
✓ Branch 1 taken 1170 times.
7446 if( body_avail_ >= h_.md.payload_size)
1039 6276 body_avail_ = h_.md.payload_size;
1040 7446 body_total_ = body_avail_;
1041 7446 payload_remain_ =
1042 7446 h_.md.payload_size - body_total_;
1043 7446 st_ = state::body;
1044 7446 return;
1045 }
1046
1047 // payload to eof
1048 // overread is not applicable
1049 auto const n0 =
1050 4622 fb_.capacity() - h_.size +
1051 4622 svc_.cfg.min_buffer +
1052 4622 svc_.max_codec;
1053
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4622 times.
4622 BOOST_ASSERT(n0 <= ws_.size());
1054 4622 cb0_ = {
1055 4622 ws_.data(),
1056 n0,
1057 overread };
1058 4622 body_buf_ = &cb0_;
1059 4622 body_avail_ = cb0_.size();
1060 4622 body_total_ = body_avail_;
1061 4622 st_ = state::body;
1062 4622 return;
1063 }
1064
1065 // buffered payload
1066 auto const n0 =
1067 fb_.capacity() - h_.size;
1068 BOOST_ASSERT(
1069 n0 <= svc_.max_overread());
1070 auto n1 = svc_.cfg.min_buffer;
1071 if(! filt_)
1072 n1 += svc_.max_codec;
1073 BOOST_ASSERT(
1074 n0 + n1 <= ws_.size());
1075 cb0_ = {
1076 ws_.data(),
1077 n0,
1078 overread };
1079 cb1_ = {
1080 ws_.data() + n0,
1081 n1 };
1082 body_buf_ = &cb1_;
1083 body_avail_ = 0;
1084 body_total_ = 0;
1085 st_ = state::body;
1086 return;
1087 }
1088
1089 // Called at the end of set_body
1090 void
1091 6775 parser::
1092 on_set_body()
1093 {
1094 // This function is called after all
1095 // limit checking and calculation of
1096 // chunked or filter.
1097
1098
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6775 times.
6775 BOOST_ASSERT(got_header());
1099
1100 // VFALCO review this hack
1101
2/2
✓ Branch 1 taken 741 times.
✓ Branch 2 taken 6034 times.
6775 if(! is_plain())
1102 741 return;
1103
1104 // plain payload
1105
1106
1/2
✓ Branch 0 taken 6034 times.
✗ Branch 1 not taken.
6034 if(how_ == how::dynamic)
1107 {
1108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 6034 times.
6034 if(h_.md.payload == payload::none)
1109 {
1110 st_ = state::complete;
1111 return;
1112 }
1113 6034 auto n = body_avail_;
1114
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6034 times.
6034 BOOST_ASSERT(n <= body_buf_->size());
1115 auto const space_left =
1116 6034 dyn_->max_size() - dyn_->size();
1117
2/2
✓ Branch 0 taken 630 times.
✓ Branch 1 taken 5404 times.
6034 if( n > space_left)
1118 630 n = space_left;
1119
2/2
✓ Branch 0 taken 3723 times.
✓ Branch 1 taken 2311 times.
6034 if(h_.md.payload == payload::size)
1120 {
1121
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3723 times.
3723 if( n > h_.md.payload_size)
1122 n = h_.md.payload_size;
1123 // reserve space
1124
1/2
✓ Branch 0 taken 3723 times.
✗ Branch 1 not taken.
3723 if(space_left >= h_.md.payload_size)
1125 3723 dyn_->prepare(h_.md.payload_size);
1126 else
1127 dyn_->prepare(space_left);
1128 }
1129
1/2
✓ Branch 2 taken 6034 times.
✗ Branch 3 not taken.
6034 dyn_->commit(
1130 buffers::buffer_copy(
1131
1/2
✓ Branch 1 taken 6034 times.
✗ Branch 2 not taken.
6034 dyn_->prepare(n),
1132 6034 body_buf_->data()));
1133 6034 body_buf_->consume(n);
1134 6034 body_avail_ -= n;
1135 // VFALCO maybe update payload_remain_ here
1136
2/2
✓ Branch 0 taken 3723 times.
✓ Branch 1 taken 2311 times.
6034 if( h_.md.payload == payload::size)
1137 {
1138
2/2
✓ Branch 0 taken 585 times.
✓ Branch 1 taken 3138 times.
3723 if(n < h_.md.payload_size)
1139 {
1140
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 585 times.
585 BOOST_ASSERT(
1141 payload_remain_ > 0);
1142 585 return;
1143 }
1144
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3138 times.
3138 BOOST_ASSERT(
1145 n == h_.md.payload_size);
1146 // complete
1147
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3138 times.
3138 BOOST_ASSERT(
1148 payload_remain_ == 0);
1149
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3138 times.
3138 BOOST_ASSERT(body_avail_ == 0);
1150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3138 times.
3138 BOOST_ASSERT(body_total_ == n);
1151 3138 st_ = state::complete;
1152 3138 return;
1153 }
1154
1155 2311 st_ = state::body;
1156 2311 return;
1157 }
1158
1159 if(how_ == how::sink)
1160 {
1161 // handle this in prepare() where
1162 // we can return an error_code.
1163 st_ = state::body_set;
1164 return;
1165 }
1166
1167 // VFALCO TODO
1168 st_ = state::body_set;
1169 detail::throw_logic_error();
1170 }
1171
1172 } // http_proto
1173 } // boost
1174
1175 #endif
1176