libs/http/src/parser.cpp

77.1% Lines (512/664) 81.2% Functions (69/85) 60.0% Branches (234/390)
libs/http/src/parser.cpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 // Copyright (c) 2024 Mohammad Nejati
4 //
5 // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 //
8 // Official repository: https://github.com/cppalliance/http
9 //
10
11 #include <boost/http/detail/except.hpp>
12 #include <boost/http/detail/workspace.hpp>
13 #include <boost/http/error.hpp>
14 #include <boost/http/parser.hpp>
15 #include <boost/http/static_request.hpp>
16 #include <boost/http/static_response.hpp>
17
18 #include <boost/assert.hpp>
19 #include <boost/capy/buffers/circular_dynamic_buffer.hpp>
20 #include <boost/capy/buffers/buffer_copy.hpp>
21 #include <boost/capy/buffers/flat_dynamic_buffer.hpp>
22 #include <boost/capy/buffers/front.hpp>
23 #include <boost/capy/buffers/slice.hpp>
24 #include <boost/capy/ex/system_context.hpp>
25 #include <boost/http/brotli/decode.hpp>
26 #include <boost/http/zlib/error.hpp>
27 #include <boost/http/zlib/inflate.hpp>
28 #include <boost/url/grammar/ci_string.hpp>
29 #include <boost/url/grammar/error.hpp>
30 #include <boost/url/grammar/hexdig_chars.hpp>
31
32 #include "src/detail/brotli_filter_base.hpp"
33 #include "src/detail/buffer_utils.hpp"
34 #include "src/detail/zlib_filter_base.hpp"
35
36 #include <memory>
37
38 namespace boost {
39 namespace http {
40
41 /*
42 Principles for fixed-size buffer design
43
44 axiom 1:
45 To read data you must have a buffer.
46
47 axiom 2:
48 The size of the HTTP header is not
49 known in advance.
50
51 conclusion 3:
52 A single I/O can produce a complete
53 HTTP header and additional payload
54 data.
55
56 conclusion 4:
57 A single I/O can produce multiple
58 complete HTTP headers, complete
59 payloads, and a partial header or
60 payload.
61
62 axiom 5:
63 A process is in one of two states:
64 1. at or below capacity
65 2. above capacity
66
67 axiom 6:
68 A program which can allocate an
69 unbounded number of resources can
70 go above capacity.
71
72 conclusion 7:
73 A program can guarantee never going
74 above capacity if all resources are
75 provisioned at program startup.
76
77 corollary 8:
78 `parser` and `serializer` should each
79 allocate a single buffer of calculated
80 size, and never resize it.
81
82 axiom #:
83 A parser and a serializer are always
84 used in pairs.
85
86 Buffer Usage
87
88 | | begin
89 | H | p | | f | read headers
90 | H | p | | T | f | set T body
91 | H | p | | C | T | f | make codec C
92 | H | p | b | C | T | f | decode p into b
93 | H | p | b | C | T | f | read/parse loop
94 | H | | T | f | destroy codec
95 | H | | T | f | finished
96
97 H headers
98 C codec
99 T body
100 f table
101 p partial payload
102 b body data
103
104 "payload" is the bytes coming in from
105 the stream.
106
107 "body" is the logical body, after transfer
108 encoding is removed. This can be the
109 same as the payload.
110
111 A "plain payload" is when the payload and
112 body are identical (no transfer encodings).
113
114 A "buffered payload" is any payload which is
115 not plain. A second buffer is required
116 for reading.
117
118 "overread" is additional data received past
119 the end of the headers when reading headers,
120 or additional data received past the end of
121 the message payload.
122 */
123
124 namespace {
125
126 class chained_sequence
127 {
128 char const* pos_;
129 char const* end_;
130 char const* begin_b_;
131 char const* end_b_;
132
133 public:
134 71111 chained_sequence(capy::const_buffer_pair const& cbp)
135 71111 : pos_(static_cast<char const*>(cbp[0].data()))
136 71111 , end_(pos_ + cbp[0].size())
137 71111 , begin_b_(static_cast<char const*>(cbp[1].data()))
138 71111 , end_b_(begin_b_ + cbp[1].size())
139 {
140 71111 }
141
142 char const*
143 319026 next() noexcept
144 {
145 319026 ++pos_;
146 // most frequently taken branch
147
2/2
✓ Branch 0 taken 296983 times.
✓ Branch 1 taken 22043 times.
319026 if(pos_ < end_)
148 296983 return pos_;
149
150 // bring the second range
151
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 22043 times.
22043 if(begin_b_ != end_b_)
152 {
153 pos_ = begin_b_;
154 end_ = end_b_;
155 begin_b_ = end_b_;
156 return pos_;
157 }
158
159 // undo the increament
160 22043 pos_ = end_;
161 22043 return nullptr;
162 }
163
164 bool
165 211814 is_empty() const noexcept
166 {
167 211814 return pos_ == end_;
168 }
169
170 char
171 304559 value() const noexcept
172 {
173 304559 return *pos_;
174 }
175
176 std::size_t
177 225998 size() const noexcept
178 {
179 225998 return (end_ - pos_) + (end_b_ - begin_b_);
180 }
181 };
182
183 std::uint64_t
184 66555 parse_hex(
185 chained_sequence& cs,
186 system::error_code& ec) noexcept
187 {
188 66555 std::uint64_t v = 0;
189 66555 std::size_t init_size = cs.size();
190
2/2
✓ Branch 1 taken 133707 times.
✓ Branch 2 taken 19746 times.
153453 while(!cs.is_empty())
191 {
192 133707 auto n = grammar::hexdig_value(cs.value());
193
2/2
✓ Branch 0 taken 46808 times.
✓ Branch 1 taken 86899 times.
133707 if(n < 0)
194 {
195
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 46807 times.
46808 if(init_size == cs.size())
196 {
197 2 ec = BOOST_HTTP_ERR(
198 error::bad_payload);
199 1 return 0;
200 }
201 46807 return v;
202 }
203
204 // at least 4 significant bits are free
205
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 86898 times.
86899 if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
206 {
207 2 ec = BOOST_HTTP_ERR(
208 error::bad_payload);
209 1 return 0;
210 }
211
212 86898 v = (v << 4) | static_cast<std::uint64_t>(n);
213 86898 cs.next();
214 }
215 39492 ec = BOOST_HTTP_ERR(
216 error::need_data);
217 19746 return 0;
218 }
219
220 void
221 47159 find_eol(
222 chained_sequence& cs,
223 system::error_code& ec) noexcept
224 {
225
2/2
✓ Branch 1 taken 53760 times.
✓ Branch 2 taken 88 times.
53848 while(!cs.is_empty())
226 {
227
2/2
✓ Branch 1 taken 47071 times.
✓ Branch 2 taken 6689 times.
53760 if(cs.value() == '\r')
228 {
229
2/2
✓ Branch 1 taken 238 times.
✓ Branch 2 taken 46833 times.
47071 if(!cs.next())
230 238 break;
231
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 46831 times.
46833 if(cs.value() != '\n')
232 {
233 4 ec = BOOST_HTTP_ERR(
234 error::bad_payload);
235 2 return;
236 }
237 46831 cs.next();
238 46831 return;
239 }
240 6689 cs.next();
241 }
242 652 ec = BOOST_HTTP_ERR(
243 error::need_data);
244 }
245
246 void
247 61961 parse_eol(
248 chained_sequence& cs,
249 system::error_code& ec) noexcept
250 {
251
2/2
✓ Branch 1 taken 61637 times.
✓ Branch 2 taken 324 times.
61961 if(cs.size() >= 2)
252 {
253 // we are sure size is at least 2
254
6/6
✓ Branch 1 taken 61635 times.
✓ Branch 2 taken 2 times.
✓ Branch 4 taken 61634 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 61634 times.
✓ Branch 7 taken 3 times.
61637 if(cs.value() == '\r' && *cs.next() == '\n')
255 {
256 61634 cs.next();
257 61634 return;
258 }
259 6 ec = BOOST_HTTP_ERR(
260 error::bad_payload);
261 3 return;
262 }
263 648 ec = BOOST_HTTP_ERR(
264 error::need_data);
265 }
266
267 void
268 4229 skip_trailer_headers(
269 chained_sequence& cs,
270 system::error_code& ec) noexcept
271 {
272
2/2
✓ Branch 1 taken 4493 times.
✓ Branch 2 taken 20 times.
4513 while(!cs.is_empty())
273 {
274
2/2
✓ Branch 1 taken 4141 times.
✓ Branch 2 taken 352 times.
4493 if(cs.value() == '\r')
275 {
276
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 4129 times.
4141 if(!cs.next())
277 12 break;
278
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 4127 times.
4129 if(cs.value() != '\n')
279 {
280 4 ec = BOOST_HTTP_ERR(
281 error::bad_payload);
282 2 return;
283 }
284 4127 cs.next();
285 4127 return;
286 }
287 // skip to the end of field
288 352 find_eol(cs, ec);
289
2/2
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 284 times.
352 if(ec)
290 68 return;
291 }
292 64 ec = BOOST_HTTP_ERR(
293 error::need_data);
294 }
295
296 template<class UInt>
297 std::size_t
298 179401 clamp(
299 UInt x,
300 std::size_t limit = (std::numeric_limits<
301 std::size_t>::max)()) noexcept
302 {
303
2/2
✓ Branch 0 taken 45506 times.
✓ Branch 1 taken 133895 times.
179401 if(x >= limit)
304 45506 return limit;
305 133895 return static_cast<std::size_t>(x);
306 }
307
308 class zlib_filter
309 : public detail::zlib_filter_base
310 {
311 http::zlib::inflate_service& svc_;
312
313 public:
314 zlib_filter(
315 http::zlib::inflate_service& svc,
316 int window_bits)
317 : svc_(svc)
318 {
319 system::error_code ec = static_cast<http::zlib::error>(
320 svc_.init2(strm_, window_bits));
321 if(ec != http::zlib::error::ok)
322 detail::throw_system_error(ec);
323 }
324
325 private:
326 virtual
327 results
328 do_process(
329 capy::mutable_buffer out,
330 capy::const_buffer in,
331 bool more) noexcept override
332 {
333 strm_.next_out = static_cast<unsigned char*>(out.data());
334 strm_.avail_out = saturate_cast(out.size());
335 strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
336 strm_.avail_in = saturate_cast(in.size());
337
338 auto rs = static_cast<http::zlib::error>(
339 svc_.inflate(
340 strm_,
341 more ? http::zlib::no_flush : http::zlib::finish));
342
343 results rv;
344 rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
345 rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
346 rv.finished = (rs == http::zlib::error::stream_end);
347
348 if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
349 rv.ec = rs;
350
351 return rv;
352 }
353 };
354
355 class brotli_filter
356 : public detail::brotli_filter_base
357 {
358 http::brotli::decode_service& svc_;
359 http::brotli::decoder_state* state_;
360
361 public:
362 brotli_filter(http::brotli::decode_service& svc)
363 : svc_(svc)
364 {
365 state_ = svc_.create_instance(nullptr, nullptr, nullptr);
366 if(!state_)
367 detail::throw_bad_alloc();
368 }
369
370 ~brotli_filter()
371 {
372 svc_.destroy_instance(state_);
373 }
374
375 private:
376 virtual
377 results
378 do_process(
379 capy::mutable_buffer out,
380 capy::const_buffer in,
381 bool more) noexcept override
382 {
383 auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
384 auto available_in = in.size();
385 auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
386 auto available_out = out.size();
387
388 auto rs = svc_.decompress_stream(
389 state_,
390 &available_in,
391 &next_in,
392 &available_out,
393 &next_out,
394 nullptr);
395
396 results rv;
397 rv.in_bytes = in.size() - available_in;
398 rv.out_bytes = out.size() - available_out;
399 rv.finished = svc_.is_finished(state_);
400
401 if(!more && rs == http::brotli::decoder_result::needs_more_input)
402 rv.ec = BOOST_HTTP_ERR(error::bad_payload);
403
404 if(rs == http::brotli::decoder_result::error)
405 rv.ec = BOOST_HTTP_ERR(
406 svc_.get_error_code(state_));
407
408 return rv;
409 }
410 };
411
412 } // namespace
413
414 //------------------------------------------------
415
416 class parser::impl
417 {
418 enum class state
419 {
420 reset,
421 start,
422 header,
423 header_done,
424 body,
425 complete,
426 };
427
428 std::shared_ptr<parser_config_impl const> cfg_;
429
430 detail::workspace ws_;
431 static_request m_;
432 std::uint64_t body_limit_;
433 std::uint64_t body_total_;
434 std::uint64_t payload_remain_;
435 std::uint64_t chunk_remain_;
436 std::size_t body_avail_;
437 std::size_t nprepare_;
438
439 capy::flat_dynamic_buffer fb_;
440 capy::circular_dynamic_buffer cb0_;
441 capy::circular_dynamic_buffer cb1_;
442
443 capy::mutable_buffer_pair mbp_;
444 capy::const_buffer_pair cbp_;
445
446 std::unique_ptr<detail::filter> filter_;
447
448 state state_;
449 bool got_header_;
450 bool got_eof_;
451 bool head_response_;
452 bool needs_chunk_close_;
453 bool trailer_headers_;
454 bool chunked_body_ended;
455
456 public:
457 1689 impl(std::shared_ptr<parser_config_impl const> cfg, detail::kind k)
458 1689 : cfg_(std::move(cfg))
459
1/1
✓ Branch 2 taken 1689 times.
1689 , ws_(cfg_->space_needed)
460 1689 , m_(ws_.data(), ws_.size())
461 1689 , state_(state::reset)
462 1689 , got_header_(false)
463 {
464 1689 m_.h_ = detail::header(detail::empty{ k });
465 1689 }
466
467 bool
468 31380 got_header() const noexcept
469 {
470 31380 return got_header_;
471 }
472
473 bool
474 50092 is_complete() const noexcept
475 {
476 50092 return state_ == state::complete;
477 }
478
479 static_request const&
480 315 safe_get_request() const
481 {
482 // headers must be received
483
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
315 if(! got_header_)
484 detail::throw_logic_error();
485
486 315 return m_;
487 }
488
489 static_response const&
490 3 safe_get_response() const
491 {
492 // headers must be received
493
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if(! got_header_)
494 detail::throw_logic_error();
495
496 // TODO: use a union
497 3 return reinterpret_cast<static_response const&>(m_);
498 }
499
500 void
501 2235 reset() noexcept
502 {
503 2235 ws_.clear();
504 2235 state_ = state::start;
505 2235 got_header_ = false;
506 2235 got_eof_ = false;
507 2235 }
508
509 void
510 10164 start(
511 bool head_response)
512 {
513 10164 std::size_t leftover = 0;
514
5/5
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2160 times.
✓ Branch 2 taken 3 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 7999 times.
10164 switch(state_)
515 {
516 1 default:
517 case state::reset:
518 // reset must be called first
519 1 detail::throw_logic_error();
520
521 2160 case state::start:
522 // reset required on eof
523
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2160 times.
2160 if(got_eof_)
524 detail::throw_logic_error();
525 2160 break;
526
527 3 case state::header:
528
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 time.
3 if(fb_.size() == 0)
529 {
530 // start() called twice
531 2 detail::throw_logic_error();
532 }
533 BOOST_FALLTHROUGH;
534
535 case state::header_done:
536 case state::body:
537 // current message is incomplete
538 2 detail::throw_logic_error();
539
540 7999 case state::complete:
541 {
542 // remove available body.
543
2/2
✓ Branch 1 taken 4000 times.
✓ Branch 2 taken 3999 times.
7999 if(is_plain())
544 4000 cb0_.consume(body_avail_);
545 // move leftovers to front
546
547 7999 ws_.clear();
548 7999 leftover = cb0_.size();
549
550 7999 auto* dest = reinterpret_cast<char*>(ws_.data());
551 7999 auto cbp = cb0_.data();
552 7999 auto* a = static_cast<char const*>(cbp[0].data());
553 7999 auto* b = static_cast<char const*>(cbp[1].data());
554 7999 auto an = cbp[0].size();
555 7999 auto bn = cbp[1].size();
556
557
2/2
✓ Branch 0 taken 7561 times.
✓ Branch 1 taken 438 times.
7999 if(bn == 0)
558 {
559 7561 std::memmove(dest, a, an);
560 }
561 else
562 {
563 // if `a` can fit between `dest` and `b`, shift `b` to the left
564 // and copy `a` to its position. if `a` fits perfectly, the
565 // shift will be of size 0.
566 // if `a` requires more space, shift `b` to the right and
567 // copy `a` to its position. this process may require multiple
568 // iterations and should be done chunk by chunk to prevent `b`
569 // from overlapping with `a`.
570 do
571 {
572 // clamp right shifts to prevent overlap with `a`
573 438 auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
574 438 b = static_cast<char const*>(std::memmove(bp, b, bn));
575
576 // a chunk or all of `a` based on available space
577 438 auto chunk_a = static_cast<std::size_t>(b - dest);
578 438 std::memcpy(dest, a, chunk_a); // never overlap
579 438 an -= chunk_a;
580 438 dest += chunk_a;
581 438 a += chunk_a;
582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 438 times.
438 } while(an);
583 }
584
585 7999 break;
586 }
587 }
588
589 10159 ws_.clear();
590
591 20318 fb_ = {
592 10159 ws_.data(),
593
1/1
✓ Branch 3 taken 10159 times.
10159 cfg_->headers.max_size + cfg_->min_buffer,
594 leftover };
595
596
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 10159 times.
10159 BOOST_ASSERT(
597 fb_.capacity() == cfg_->max_overread() - leftover);
598
599
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 10159 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
10159 BOOST_ASSERT(
600 head_response == false ||
601 m_.h_.kind == detail::kind::response);
602
603 10159 m_.h_ = detail::header(detail::empty{m_.h_.kind});
604 10159 m_.h_.buf = reinterpret_cast<char*>(ws_.data());
605 10159 m_.h_.cbuf = m_.h_.buf;
606 10159 m_.h_.cap = ws_.size();
607
608 10159 state_ = state::header;
609
610 // reset to the configured default
611 10159 body_limit_ = cfg_->body_limit;
612
613 10159 body_total_ = 0;
614 10159 payload_remain_ = 0;
615 10159 chunk_remain_ = 0;
616 10159 body_avail_ = 0;
617 10159 nprepare_ = 0;
618
619 10159 filter_.reset();
620
621 10159 got_header_ = false;
622 10159 head_response_ = head_response;
623 10159 needs_chunk_close_ = false;
624 10159 trailer_headers_ = false;
625 10159 chunked_body_ended = false;
626 10159 }
627
628 auto
629 68721 prepare() ->
630 mutable_buffers_type
631 {
632 68721 nprepare_ = 0;
633
634
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 27874 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40844 times.
✓ Branch 5 taken 1 time.
68721 switch(state_)
635 {
636 1 default:
637 case state::reset:
638 // reset must be called first
639 1 detail::throw_logic_error();
640
641 1 case state::start:
642 // start must be called first
643 1 detail::throw_logic_error();
644
645 27874 case state::header:
646 {
647
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 27874 times.
27874 BOOST_ASSERT(
648 m_.h_.size < cfg_->headers.max_size);
649 27874 std::size_t n = fb_.capacity();
650
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 27874 times.
27874 BOOST_ASSERT(n <= cfg_->max_overread());
651 27874 n = clamp(n, cfg_->max_prepare);
652 27874 mbp_[0] = fb_.prepare(n);
653 27874 nprepare_ = n;
654 27874 return mutable_buffers_type(&mbp_[0], 1);
655 }
656
657 case state::header_done:
658 // forgot to call parse()
659 detail::throw_logic_error();
660
661 40844 case state::body:
662 {
663
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40844 times.
40844 if(got_eof_)
664 {
665 // forgot to call parse()
666 detail::throw_logic_error();
667 }
668
669
2/2
✓ Branch 1 taken 21207 times.
✓ Branch 2 taken 19637 times.
40844 if(! is_plain())
670 {
671 // buffered payload
672 21207 std::size_t n = cb0_.capacity();
673 21207 n = clamp(n, cfg_->max_prepare);
674 21207 nprepare_ = n;
675 21207 mbp_ = cb0_.prepare(n);
676 21207 return detail::make_span(mbp_);
677 }
678 else
679 {
680 // plain payload
681 19637 std::size_t n = cb0_.capacity();
682 19637 n = clamp(n, cfg_->max_prepare);
683
684
2/2
✓ Branch 1 taken 19623 times.
✓ Branch 2 taken 14 times.
19637 if(m_.payload() == payload::size)
685 {
686
2/2
✓ Branch 0 taken 18416 times.
✓ Branch 1 taken 1207 times.
19623 if(n > payload_remain_)
687 {
688 18416 std::size_t overread =
689 18416 n - static_cast<std::size_t>(payload_remain_);
690
2/2
✓ Branch 2 taken 8504 times.
✓ Branch 3 taken 9912 times.
18416 if(overread > cfg_->max_overread())
691 8504 n = static_cast<std::size_t>(payload_remain_) +
692 8504 cfg_->max_overread();
693 }
694 }
695 else
696 {
697
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 BOOST_ASSERT(
698 m_.payload() == payload::to_eof);
699 // No more messages can be pipelined, so
700 // limit the output buffer to the remaining
701 // body limit plus one byte to detect
702 // exhaustion.
703 14 std::uint64_t r = body_limit_remain();
704
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if(r != std::uint64_t(-1))
705 14 r += 1;
706 14 n = clamp(r, n);
707 }
708
709 19637 nprepare_ = n;
710 19637 mbp_ = cb0_.prepare(n);
711 19637 return detail::make_span(mbp_);
712 }
713 }
714
715 1 case state::complete:
716 // already complete
717 1 detail::throw_logic_error();
718 }
719 }
720
721 void
722 68128 commit(
723 std::size_t n)
724 {
725
4/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 27444 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 40682 times.
✗ Branch 5 not taken.
68128 switch(state_)
726 {
727 1 default:
728 case state::reset:
729 {
730 // reset must be called first
731 1 detail::throw_logic_error();
732 }
733
734 1 case state::start:
735 {
736 // forgot to call start()
737 1 detail::throw_logic_error();
738 }
739
740 27444 case state::header:
741 {
742
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 27443 times.
27444 if(n > nprepare_)
743 {
744 // n can't be greater than size of
745 // the buffers returned by prepare()
746 1 detail::throw_invalid_argument();
747 }
748
749
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 27442 times.
27443 if(got_eof_)
750 {
751 // can't commit after EOF
752 1 detail::throw_logic_error();
753 }
754
755 27442 nprepare_ = 0; // invalidate
756 27442 fb_.commit(n);
757 27442 break;
758 }
759
760 case state::header_done:
761 {
762 // forgot to call parse()
763 detail::throw_logic_error();
764 }
765
766 40682 case state::body:
767 {
768
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 40680 times.
40682 if(n > nprepare_)
769 {
770 // n can't be greater than size of
771 // the buffers returned by prepare()
772 2 detail::throw_invalid_argument();
773 }
774
775
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 40680 times.
40680 if(got_eof_)
776 {
777 // can't commit after EOF
778 detail::throw_logic_error();
779 }
780
781 40680 nprepare_ = 0; // invalidate
782 40680 cb0_.commit(n);
783 40680 break;
784 }
785
786 case state::complete:
787 {
788 // already complete
789 detail::throw_logic_error();
790 }
791 }
792 68122 }
793
794 void
795 132 commit_eof()
796 {
797 132 nprepare_ = 0; // invalidate
798
799
5/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 117 times.
✓ Branch 5 taken 1 time.
132 switch(state_)
800 {
801 1 default:
802 case state::reset:
803 // reset must be called first
804 1 detail::throw_logic_error();
805
806 1 case state::start:
807 // forgot to call start()
808 1 detail::throw_logic_error();
809
810 12 case state::header:
811 12 got_eof_ = true;
812 12 break;
813
814 case state::header_done:
815 // forgot to call parse()
816 detail::throw_logic_error();
817
818 117 case state::body:
819 117 got_eof_ = true;
820 117 break;
821
822 1 case state::complete:
823 // can't commit eof when complete
824 1 detail::throw_logic_error();
825 }
826 129 }
827
828 void
829 85415 parse(
830 system::error_code& ec)
831 {
832 85415 ec = {};
833
6/6
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 32948 times.
✓ Branch 3 taken 8642 times.
✓ Branch 4 taken 41613 times.
✓ Branch 5 taken 2210 times.
85415 switch(state_)
834 {
835 1 default:
836 case state::reset:
837 // reset must be called first
838 1 detail::throw_logic_error();
839
840 1 case state::start:
841 // start must be called first
842 1 detail::throw_logic_error();
843
844 32948 case state::header:
845 {
846
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32948 times.
32948 BOOST_ASSERT(m_.h_.buf == static_cast<
847 void const*>(ws_.data()));
848
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 32948 times.
32948 BOOST_ASSERT(m_.h_.cbuf == static_cast<
849 void const*>(ws_.data()));
850
851 32948 m_.h_.parse(fb_.size(), cfg_->headers, ec);
852
853
2/2
✓ Branch 1 taken 23238 times.
✓ Branch 2 taken 9710 times.
32948 if(ec == condition::need_more_input)
854 {
855
2/2
✓ Branch 0 taken 23229 times.
✓ Branch 1 taken 9 times.
23238 if(! got_eof_)
856 {
857 // headers incomplete
858 23229 return;
859 }
860
861
2/2
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
9 if(fb_.size() == 0)
862 {
863 // stream closed cleanly
864 4 state_ = state::reset;
865 8 ec = BOOST_HTTP_ERR(
866 error::end_of_stream);
867 4 return;
868 }
869
870 // stream closed with a
871 // partial message received
872 5 state_ = state::reset;
873 10 ec = BOOST_HTTP_ERR(
874 error::incomplete);
875 5 return;
876 }
877
2/2
✓ Branch 1 taken 259 times.
✓ Branch 2 taken 9451 times.
9710 else if(ec)
878 {
879 // other error,
880 //
881 // VFALCO map this to a bad
882 // request or bad response error?
883 //
884 259 state_ = state::reset; // unrecoverable
885 259 return;
886 }
887
888 9451 got_header_ = true;
889
890 // reserve headers + table
891 9451 ws_.reserve_front(m_.h_.size);
892 9451 ws_.reserve_back(m_.h_.table_space());
893
894 // no payload
895
4/4
✓ Branch 1 taken 8645 times.
✓ Branch 2 taken 806 times.
✓ Branch 3 taken 806 times.
✓ Branch 4 taken 8645 times.
18096 if(m_.payload() == payload::none ||
896
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 8645 times.
8645 head_response_)
897 {
898 // octets of the next message
899 806 auto overread = fb_.size() - m_.h_.size;
900
1/1
✓ Branch 2 taken 806 times.
806 cb0_ = { ws_.data(), overread, overread };
901 806 ws_.reserve_front(overread);
902 806 state_ = state::complete;
903 806 return;
904 }
905
906 8645 state_ = state::header_done;
907 8645 break;
908 }
909
910 8642 case state::header_done:
911 {
912 // metadata error
913
2/2
✓ Branch 1 taken 60 times.
✓ Branch 2 taken 8582 times.
8642 if(m_.payload() == payload::error)
914 {
915 // VFALCO This needs looking at
916 120 ec = BOOST_HTTP_ERR(
917 error::bad_payload);
918 60 state_ = state::reset; // unrecoverable
919 60 return;
920 }
921
922 // overread currently includes any and all octets that
923 // extend beyond the current end of the header
924 // this can include associated body octets for the
925 // current message or octets of the next message in the
926 // stream, e.g. pipelining is being used
927 8582 auto const overread = fb_.size() - m_.h_.size;
928
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 8582 times.
8582 BOOST_ASSERT(overread <= cfg_->max_overread());
929
930 8582 auto cap = fb_.capacity() + overread +
931 8582 cfg_->min_buffer;
932
933 // reserve body buffers first, as the decoder
934 // must be installed after them.
935 8582 auto const p = ws_.reserve_front(cap);
936
937 // Content-Encoding
938
1/4
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8582 times.
8582 switch(m_.metadata().content_encoding.coding)
939 {
940 case content_coding::deflate:
941 if(!cfg_->apply_deflate_decoder)
942 goto no_filter;
943 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
944 {
945 filter_.reset(new zlib_filter(
946 *svc,
947 cfg_->zlib_window_bits));
948 }
949 break;
950
951 case content_coding::gzip:
952 if(!cfg_->apply_gzip_decoder)
953 goto no_filter;
954 if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
955 {
956 filter_.reset(new zlib_filter(
957 *svc,
958 cfg_->zlib_window_bits + 16));
959 }
960 break;
961
962 case content_coding::br:
963 if(!cfg_->apply_brotli_decoder)
964 goto no_filter;
965 if(auto* svc = capy::get_system_context().find_service<http::brotli::decode_service>())
966 {
967 filter_.reset(new brotli_filter(*svc));
968 }
969 break;
970
971 no_filter:
972 8582 default:
973 8582 break;
974 }
975
976
2/2
✓ Branch 1 taken 4311 times.
✓ Branch 2 taken 4271 times.
8582 if(is_plain())
977 {
978
1/1
✓ Branch 1 taken 4311 times.
4311 cb0_ = { p, cap, overread };
979 4311 cb1_ = {};
980 }
981 else
982 {
983 // buffered payload
984 4271 std::size_t n0 = (overread > cfg_->min_buffer)
985
1/2
✓ Branch 0 taken 4271 times.
✗ Branch 1 not taken.
8542 ? overread
986 4271 : cfg_->min_buffer;
987 4271 std::size_t n1 = cfg_->min_buffer;
988
989
1/1
✓ Branch 1 taken 4271 times.
4271 cb0_ = { p , n0, overread };
990 4271 cb1_ = { p + n0 , n1 };
991 }
992
993
2/2
✓ Branch 1 taken 4194 times.
✓ Branch 2 taken 4388 times.
8582 if(m_.payload() == payload::size)
994 {
995
5/6
✓ Branch 1 taken 4194 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 4193 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 4193 times.
8388 if(!filter_ &&
996 4194 body_limit_ < m_.payload_size())
997 {
998 2 ec = BOOST_HTTP_ERR(
999 error::body_too_large);
1000 1 state_ = state::reset;
1001 1 return;
1002 }
1003 4193 payload_remain_ = m_.payload_size();
1004 }
1005
1006 8581 state_ = state::body;
1007 BOOST_FALLTHROUGH;
1008 }
1009
1010 50194 case state::body:
1011 {
1012
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50194 times.
50194 BOOST_ASSERT(state_ == state::body);
1013
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50194 times.
50194 BOOST_ASSERT(m_.payload() != payload::none);
1014
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 50194 times.
50194 BOOST_ASSERT(m_.payload() != payload::error);
1015
1016 8349 auto set_state_to_complete = [&]()
1017 {
1018 8349 state_ = state::complete;
1019 58543 };
1020
1021
2/2
✓ Branch 1 taken 25996 times.
✓ Branch 2 taken 24198 times.
50194 if(m_.payload() == payload::chunked)
1022 {
1023 for(;;)
1024 {
1025
2/2
✓ Branch 0 taken 75238 times.
✓ Branch 1 taken 2211 times.
77449 if(chunk_remain_ == 0
1026
2/2
✓ Branch 0 taken 71111 times.
✓ Branch 1 taken 4127 times.
75238 && !chunked_body_ended)
1027 {
1028 71111 auto cs = chained_sequence(cb0_.data());
1029 20437 auto check_ec = [&]()
1030 {
1031
4/6
✓ Branch 1 taken 20428 times.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 20428 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 20437 times.
20437 if(ec == condition::need_more_input && got_eof_)
1032 {
1033 ec = BOOST_HTTP_ERR(error::incomplete);
1034 state_ = state::reset;
1035 }
1036 91548 };
1037
1038
2/2
✓ Branch 0 taken 61961 times.
✓ Branch 1 taken 9150 times.
71111 if(needs_chunk_close_)
1039 {
1040 61961 parse_eol(cs, ec);
1041
2/2
✓ Branch 1 taken 327 times.
✓ Branch 2 taken 61634 times.
61961 if(ec)
1042 {
1043 327 check_ec();
1044 20437 return;
1045 }
1046 }
1047
2/2
✓ Branch 0 taken 4229 times.
✓ Branch 1 taken 4921 times.
9150 else if(trailer_headers_)
1048 {
1049 4229 skip_trailer_headers(cs, ec);
1050
2/2
✓ Branch 1 taken 102 times.
✓ Branch 2 taken 4127 times.
4229 if(ec)
1051 {
1052 102 check_ec();
1053 102 return;
1054 }
1055 4127 cb0_.consume(cb0_.size() - cs.size());
1056 4127 chunked_body_ended = true;
1057 8264 continue;
1058 }
1059
1060 66555 auto chunk_size = parse_hex(cs, ec);
1061
2/2
✓ Branch 1 taken 19748 times.
✓ Branch 2 taken 46807 times.
66555 if(ec)
1062 {
1063 19748 check_ec();
1064 19748 return;
1065 }
1066
1067 // skip chunk extensions
1068 46807 find_eol(cs, ec);
1069
2/2
✓ Branch 1 taken 260 times.
✓ Branch 2 taken 46547 times.
46807 if(ec)
1070 {
1071 260 check_ec();
1072 260 return;
1073 }
1074
1075 46547 cb0_.consume(cb0_.size() - cs.size());
1076 46547 chunk_remain_ = chunk_size;
1077
1078 46547 needs_chunk_close_ = true;
1079
2/2
✓ Branch 0 taken 4137 times.
✓ Branch 1 taken 42410 times.
46547 if(chunk_remain_ == 0)
1080 {
1081 4137 needs_chunk_close_ = false;
1082 4137 trailer_headers_ = true;
1083 4137 continue;
1084 }
1085 }
1086
1087
6/6
✓ Branch 1 taken 3559 times.
✓ Branch 2 taken 45189 times.
✓ Branch 3 taken 1432 times.
✓ Branch 4 taken 2127 times.
✓ Branch 5 taken 1432 times.
✓ Branch 6 taken 47316 times.
48748 if(cb0_.size() == 0 && !chunked_body_ended)
1088 {
1089
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1431 times.
1432 if(got_eof_)
1090 {
1091 2 ec = BOOST_HTTP_ERR(
1092 error::incomplete);
1093 1 state_ = state::reset;
1094 1 return;
1095 }
1096
1097 2862 ec = BOOST_HTTP_ERR(
1098 error::need_data);
1099 1431 return;
1100 }
1101
1102
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 47316 times.
47316 if(filter_)
1103 {
1104 chunk_remain_ -= apply_filter(
1105 ec,
1106 clamp(chunk_remain_, cb0_.size()),
1107 !chunked_body_ended);
1108
1109 if(ec || chunked_body_ended)
1110 return;
1111 }
1112 else
1113 {
1114 const std::size_t chunk_avail =
1115 47316 clamp(chunk_remain_, cb0_.size());
1116 const auto chunk =
1117 47316 capy::prefix(cb0_.data(), chunk_avail);
1118
1119
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 47316 times.
47316 if(body_limit_remain() < chunk_avail)
1120 {
1121 ec = BOOST_HTTP_ERR(
1122 error::body_too_large);
1123 state_ = state::reset;
1124 4127 return;
1125 }
1126
1127 // in_place style
1128 47316 auto copied = capy::buffer_copy(
1129
1/1
✓ Branch 2 taken 47316 times.
47316 cb1_.prepare(cb1_.capacity()),
1130 chunk);
1131 47316 chunk_remain_ -= copied;
1132 47316 body_avail_ += copied;
1133 47316 body_total_ += copied;
1134 47316 cb0_.consume(copied);
1135 47316 cb1_.commit(copied);
1136 47316 if(cb1_.capacity() == 0
1137
2/6
✗ Branch 0 not taken.
✓ Branch 1 taken 47316 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 47316 times.
47316 && !chunked_body_ended)
1138 {
1139 ec = BOOST_HTTP_ERR(
1140 error::in_place_overflow);
1141 return;
1142 }
1143
1144
2/2
✓ Branch 0 taken 4127 times.
✓ Branch 1 taken 43189 times.
47316 if(chunked_body_ended)
1145 {
1146 4127 set_state_to_complete();
1147 4127 return;
1148 }
1149 }
1150 51453 }
1151 }
1152 else
1153 {
1154 // non-chunked payload
1155
1156 72594 const std::size_t payload_avail = [&]()
1157 {
1158 24198 auto ret = cb0_.size();
1159
1/2
✓ Branch 1 taken 24198 times.
✗ Branch 2 not taken.
24198 if(!filter_)
1160 24198 ret -= body_avail_;
1161
2/2
✓ Branch 1 taken 23941 times.
✓ Branch 2 taken 257 times.
24198 if(m_.payload() == payload::size)
1162 23941 return clamp(payload_remain_, ret);
1163 // payload::eof
1164 257 return ret;
1165 24198 }();
1166
1167 72594 const bool is_complete = [&]()
1168 {
1169
2/2
✓ Branch 1 taken 23941 times.
✓ Branch 2 taken 257 times.
24198 if(m_.payload() == payload::size)
1170 23941 return payload_avail == payload_remain_;
1171 // payload::eof
1172 257 return got_eof_;
1173 24198 }();
1174
1175
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 24198 times.
24198 if(filter_)
1176 {
1177 payload_remain_ -= apply_filter(
1178 ec, payload_avail, !is_complete);
1179 if(ec || is_complete)
1180 return;
1181 }
1182 else
1183 {
1184 // plain body
1185
1186
2/2
✓ Branch 1 taken 257 times.
✓ Branch 2 taken 23941 times.
24198 if(m_.payload() == payload::to_eof)
1187 {
1188
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 256 times.
257 if(body_limit_remain() < payload_avail)
1189 {
1190 2 ec = BOOST_HTTP_ERR(
1191 error::body_too_large);
1192 1 state_ = state::reset;
1193 1 return;
1194 }
1195 }
1196
1197 // in_place style
1198 24197 payload_remain_ -= payload_avail;
1199 24197 body_avail_ += payload_avail;
1200 24197 body_total_ += payload_avail;
1201
5/6
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 24196 times.
✓ Branch 3 taken 1 time.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 24196 times.
24197 if(cb0_.capacity() == 0 && !is_complete)
1202 {
1203 2 ec = BOOST_HTTP_ERR(
1204 error::in_place_overflow);
1205 1 return;
1206 }
1207
1208
2/2
✓ Branch 0 taken 4222 times.
✓ Branch 1 taken 19974 times.
24196 if(is_complete)
1209 {
1210 4222 set_state_to_complete();
1211 4222 return;
1212 }
1213 }
1214
1215
6/6
✓ Branch 1 taken 19832 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 19831 times.
✓ Branch 5 taken 1 time.
✓ Branch 6 taken 19973 times.
19974 if(m_.payload() == payload::size && got_eof_)
1216 {
1217 2 ec = BOOST_HTTP_ERR(
1218 error::incomplete);
1219 1 state_ = state::reset;
1220 1 return;
1221 }
1222
1223 39946 ec = BOOST_HTTP_ERR(
1224 error::need_data);
1225 19973 return;
1226 }
1227
1228 break;
1229 }
1230
1231 2210 case state::complete:
1232 2210 break;
1233 }
1234 }
1235
1236 auto
1237 41176 pull_body() ->
1238 const_buffers_type
1239 {
1240
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 41176 times.
✗ Branch 2 not taken.
41176 switch(state_)
1241 {
1242 case state::header_done:
1243 return {};
1244 41176 case state::body:
1245 case state::complete:
1246 41176 cbp_ = capy::prefix(
1247
2/2
✓ Branch 1 taken 19819 times.
✓ Branch 2 taken 21357 times.
41176 (is_plain() ? cb0_ : cb1_).data(),
1248 body_avail_);
1249 41176 return detail::make_span(cbp_);
1250 default:
1251 detail::throw_logic_error();
1252 }
1253 }
1254
1255 void
1256 39412 consume_body(std::size_t n)
1257 {
1258
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 39412 times.
✗ Branch 2 not taken.
39412 switch(state_)
1259 {
1260 case state::header_done:
1261 return;
1262 39412 case state::body:
1263 case state::complete:
1264 39412 n = clamp(n, body_avail_);
1265
2/2
✓ Branch 1 taken 19527 times.
✓ Branch 2 taken 19885 times.
39412 (is_plain() ? cb0_ : cb1_).consume(n);
1266 39412 body_avail_ -= n;
1267 39412 return;
1268 default:
1269 detail::throw_logic_error();
1270 }
1271 }
1272
1273 core::string_view
1274 700 body() const
1275 {
1276 // Precondition violation
1277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 if(state_ != state::complete)
1278 detail::throw_logic_error();
1279
1280 // Precondition violation
1281
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 if(body_avail_ != body_total_)
1282 detail::throw_logic_error();
1283
1284
2/2
✓ Branch 1 taken 579 times.
✓ Branch 2 taken 121 times.
700 auto cbp = (is_plain() ? cb0_ : cb1_).data();
1285
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 700 times.
700 BOOST_ASSERT(cbp[1].size() == 0);
1286
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 700 times.
700 BOOST_ASSERT(cbp[0].size() == body_avail_);
1287 700 return core::string_view(
1288 700 static_cast<char const*>(cbp[0].data()),
1289 1400 body_avail_);
1290 }
1291
1292 void
1293 5 set_body_limit(std::uint64_t n)
1294 {
1295
3/3
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
5 switch(state_)
1296 {
1297 1 case state::header:
1298 case state::header_done:
1299 1 body_limit_ = n;
1300 1 break;
1301 2 case state::complete:
1302 // only allowed for empty bodies
1303
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if(body_total_ == 0)
1304 1 break;
1305 BOOST_FALLTHROUGH;
1306 default:
1307 // set body_limit before parsing the body
1308 3 detail::throw_logic_error();
1309 }
1310 2 }
1311
1312 private:
1313 bool
1314 138713 is_plain() const noexcept
1315 {
1316
3/4
✓ Branch 1 taken 138713 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 67873 times.
✓ Branch 4 taken 70840 times.
277426 return ! filter_ &&
1317 277426 m_.payload() != payload::chunked;
1318 }
1319
1320 std::uint64_t
1321 47587 body_limit_remain() const noexcept
1322 {
1323 47587 return body_limit_ - body_total_;
1324 }
1325
1326 std::size_t
1327 apply_filter(
1328 system::error_code& ec,
1329 std::size_t payload_avail,
1330 bool more)
1331 {
1332 std::size_t p0 = payload_avail;
1333 for(;;)
1334 {
1335 if(payload_avail == 0 && more)
1336 break;
1337
1338 auto f_rs = [&](){
1339 BOOST_ASSERT(filter_ != nullptr);
1340 std::size_t n = clamp(body_limit_remain());
1341 n = clamp(n, cb1_.capacity());
1342
1343 return filter_->process(
1344 detail::make_span(cb1_.prepare(n)),
1345 capy::prefix(cb0_.data(), payload_avail),
1346 more);
1347 }();
1348
1349 cb0_.consume(f_rs.in_bytes);
1350 payload_avail -= f_rs.in_bytes;
1351 body_total_ += f_rs.out_bytes;
1352
1353 // in_place style
1354 cb1_.commit(f_rs.out_bytes);
1355 body_avail_ += f_rs.out_bytes;
1356 if(cb1_.capacity() == 0 &&
1357 !f_rs.finished && f_rs.in_bytes == 0)
1358 {
1359 ec = BOOST_HTTP_ERR(
1360 error::in_place_overflow);
1361 goto done;
1362 }
1363
1364 if(f_rs.ec)
1365 {
1366 ec = f_rs.ec;
1367 state_ = state::reset;
1368 break;
1369 }
1370
1371 if(body_limit_remain() == 0 &&
1372 !f_rs.finished && f_rs.in_bytes == 0)
1373 {
1374 ec = BOOST_HTTP_ERR(
1375 error::body_too_large);
1376 state_ = state::reset;
1377 break;
1378 }
1379
1380 if(f_rs.finished)
1381 {
1382 if(!more)
1383 state_ = state::complete;
1384 break;
1385 }
1386 }
1387
1388 done:
1389 return p0 - payload_avail;
1390 }
1391 };
1392
1393 //------------------------------------------------
1394 //
1395 // Special Members
1396 //
1397 //------------------------------------------------
1398
1399 1704 parser::
1400 ~parser()
1401 {
1402
2/2
✓ Branch 0 taken 1689 times.
✓ Branch 1 taken 15 times.
1704 delete impl_;
1403 1704 }
1404
1405 12 parser::
1406 12 parser() noexcept
1407 12 : impl_(nullptr)
1408 {
1409 12 }
1410
1411 3 parser::
1412 3 parser(parser&& other) noexcept
1413 3 : impl_(other.impl_)
1414 {
1415 3 other.impl_ = nullptr;
1416 3 }
1417
1418 1689 parser::
1419 parser(
1420 std::shared_ptr<parser_config_impl const> cfg,
1421 1689 detail::kind k)
1422
1/3
✓ Branch 4 taken 1689 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
1689 : impl_(new impl(std::move(cfg), k))
1423 {
1424 // TODO: use a single allocation for
1425 // impl and workspace buffer.
1426 1689 }
1427
1428 void
1429 4 parser::
1430 assign(parser&& other) noexcept
1431 {
1432
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if(this == &other)
1433 return;
1434
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 delete impl_;
1435 4 impl_ = other.impl_;
1436 4 other.impl_ = nullptr;
1437 }
1438
1439 //--------------------------------------------
1440 //
1441 // Observers
1442 //
1443 //--------------------------------------------
1444
1445 bool
1446 31380 parser::got_header() const noexcept
1447 {
1448
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31380 times.
31380 BOOST_ASSERT(impl_);
1449 31380 return impl_->got_header();
1450 }
1451
1452 bool
1453 50092 parser::is_complete() const noexcept
1454 {
1455
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 50092 times.
50092 BOOST_ASSERT(impl_);
1456 50092 return impl_->is_complete();
1457 }
1458
1459 //------------------------------------------------
1460 //
1461 // Modifiers
1462 //
1463 //------------------------------------------------
1464
1465 void
1466 2235 parser::
1467 reset() noexcept
1468 {
1469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2235 times.
2235 BOOST_ASSERT(impl_);
1470 2235 impl_->reset();
1471 2235 }
1472
1473 void
1474 10164 parser::start()
1475 {
1476
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 10164 times.
10164 BOOST_ASSERT(impl_);
1477 10164 impl_->start(false);
1478 10159 }
1479
1480 auto
1481 68721 parser::
1482 prepare() ->
1483 mutable_buffers_type
1484 {
1485
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68721 times.
68721 BOOST_ASSERT(impl_);
1486 68721 return impl_->prepare();
1487 }
1488
1489 void
1490 68128 parser::
1491 commit(
1492 std::size_t n)
1493 {
1494
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 68128 times.
68128 BOOST_ASSERT(impl_);
1495 68128 impl_->commit(n);
1496 68122 }
1497
1498 void
1499 132 parser::
1500 commit_eof()
1501 {
1502
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 132 times.
132 BOOST_ASSERT(impl_);
1503 132 impl_->commit_eof();
1504 129 }
1505
1506 void
1507 85415 parser::
1508 parse(
1509 system::error_code& ec)
1510 {
1511
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 85415 times.
85415 BOOST_ASSERT(impl_);
1512 85415 impl_->parse(ec);
1513 85413 }
1514
1515 auto
1516 41176 parser::
1517 pull_body() ->
1518 const_buffers_type
1519 {
1520
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41176 times.
41176 BOOST_ASSERT(impl_);
1521 41176 return impl_->pull_body();
1522 }
1523
1524 void
1525 39412 parser::
1526 consume_body(std::size_t n)
1527 {
1528
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 39412 times.
39412 BOOST_ASSERT(impl_);
1529 39412 impl_->consume_body(n);
1530 39412 }
1531
1532 core::string_view
1533 700 parser::
1534 body() const
1535 {
1536
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 700 times.
700 BOOST_ASSERT(impl_);
1537 700 return impl_->body();
1538 }
1539
1540 core::string_view
1541 parser::
1542 release_buffered_data() noexcept
1543 {
1544 // TODO
1545 return {};
1546 }
1547
1548 void
1549 5 parser::
1550 set_body_limit(std::uint64_t n)
1551 {
1552
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
5 BOOST_ASSERT(impl_);
1553 5 impl_->set_body_limit(n);
1554 2 }
1555
1556 //------------------------------------------------
1557 //
1558 // Implementation
1559 //
1560 //------------------------------------------------
1561
1562 void
1563 parser::
1564 start_impl(bool head_response)
1565 {
1566 BOOST_ASSERT(impl_);
1567 impl_->start(head_response);
1568 }
1569
1570 static_request const&
1571 315 parser::
1572 safe_get_request() const
1573 {
1574
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 315 times.
315 BOOST_ASSERT(impl_);
1575 315 return impl_->safe_get_request();
1576 }
1577
1578 static_response const&
1579 3 parser::
1580 safe_get_response() const
1581 {
1582
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 BOOST_ASSERT(impl_);
1583 3 return impl_->safe_get_response();
1584 }
1585
1586 } // http
1587 } // boost
1588