Line data 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_proto
9 : //
10 :
11 : #include <boost/http_proto/detail/except.hpp>
12 : #include <boost/http_proto/error.hpp>
13 : #include <boost/http_proto/parser.hpp>
14 :
15 : #include <boost/assert.hpp>
16 : #include <boost/buffers/circular_buffer.hpp>
17 : #include <boost/buffers/copy.hpp>
18 : #include <boost/buffers/flat_buffer.hpp>
19 : #include <boost/buffers/front.hpp>
20 : #include <boost/buffers/slice.hpp>
21 : #include <boost/capy/brotli/decode.hpp>
22 : #include <boost/capy/polystore.hpp>
23 : #include <boost/capy/zlib/error.hpp>
24 : #include <boost/capy/zlib/inflate.hpp>
25 : #include <boost/url/grammar/ci_string.hpp>
26 : #include <boost/url/grammar/error.hpp>
27 : #include <boost/url/grammar/hexdig_chars.hpp>
28 :
29 : #include "src/detail/brotli_filter_base.hpp"
30 : #include "src/detail/buffer_utils.hpp"
31 : #include "src/detail/zlib_filter_base.hpp"
32 :
33 : namespace boost {
34 : namespace http_proto {
35 :
36 : /*
37 : Principles for fixed-size buffer design
38 :
39 : axiom 1:
40 : To read data you must have a buffer.
41 :
42 : axiom 2:
43 : The size of the HTTP header is not
44 : known in advance.
45 :
46 : conclusion 3:
47 : A single I/O can produce a complete
48 : HTTP header and additional payload
49 : data.
50 :
51 : conclusion 4:
52 : A single I/O can produce multiple
53 : complete HTTP headers, complete
54 : payloads, and a partial header or
55 : payload.
56 :
57 : axiom 5:
58 : A process is in one of two states:
59 : 1. at or below capacity
60 : 2. above capacity
61 :
62 : axiom 6:
63 : A program which can allocate an
64 : unbounded number of resources can
65 : go above capacity.
66 :
67 : conclusion 7:
68 : A program can guarantee never going
69 : above capacity if all resources are
70 : provisioned at program startup.
71 :
72 : corollary 8:
73 : `parser` and `serializer` should each
74 : allocate a single buffer of calculated
75 : size, and never resize it.
76 :
77 : axiom #:
78 : A parser and a serializer are always
79 : used in pairs.
80 :
81 : Buffer Usage
82 :
83 : | | begin
84 : | H | p | | f | read headers
85 : | H | p | | T | f | set T body
86 : | H | p | | C | T | f | make codec C
87 : | H | p | b | C | T | f | decode p into b
88 : | H | p | b | C | T | f | read/parse loop
89 : | H | | T | f | destroy codec
90 : | H | | T | f | finished
91 :
92 : H headers
93 : C codec
94 : T body
95 : f table
96 : p partial payload
97 : b body data
98 :
99 : "payload" is the bytes coming in from
100 : the stream.
101 :
102 : "body" is the logical body, after transfer
103 : encoding is removed. This can be the
104 : same as the payload.
105 :
106 : A "plain payload" is when the payload and
107 : body are identical (no transfer encodings).
108 :
109 : A "buffered payload" is any payload which is
110 : not plain. A second buffer is required
111 : for reading.
112 :
113 : "overread" is additional data received past
114 : the end of the headers when reading headers,
115 : or additional data received past the end of
116 : the message payload.
117 : */
118 :
119 : namespace {
120 :
121 : class chained_sequence
122 : {
123 : char const* pos_;
124 : char const* end_;
125 : char const* begin_b_;
126 : char const* end_b_;
127 :
128 : public:
129 120197 : chained_sequence(buffers::const_buffer_pair const& cbp)
130 120197 : : pos_(static_cast<char const*>(cbp[0].data()))
131 120197 : , end_(pos_ + cbp[0].size())
132 120197 : , begin_b_(static_cast<char const*>(cbp[1].data()))
133 120197 : , end_b_(begin_b_ + cbp[1].size())
134 : {
135 120197 : }
136 :
137 : char const*
138 618747 : next() noexcept
139 : {
140 618747 : ++pos_;
141 : // most frequently taken branch
142 618747 : if(pos_ < end_)
143 597426 : return pos_;
144 :
145 : // bring the second range
146 21321 : if(begin_b_ != end_b_)
147 : {
148 38 : pos_ = begin_b_;
149 38 : end_ = end_b_;
150 38 : begin_b_ = end_b_;
151 38 : return pos_;
152 : }
153 :
154 : // undo the increament
155 21283 : pos_ = end_;
156 21283 : return nullptr;
157 : }
158 :
159 : bool
160 410999 : is_empty() const noexcept
161 : {
162 410999 : return pos_ == end_;
163 : }
164 :
165 : char
166 604240 : value() const noexcept
167 : {
168 604240 : return *pos_;
169 : }
170 :
171 : std::size_t
172 424974 : size() const noexcept
173 : {
174 424974 : return (end_ - pos_) + (end_b_ - begin_b_);
175 : }
176 : };
177 :
178 : std::uint64_t
179 115957 : parse_hex(
180 : chained_sequence& cs,
181 : system::error_code& ec) noexcept
182 : {
183 115957 : std::uint64_t v = 0;
184 115957 : std::size_t init_size = cs.size();
185 302778 : while(!cs.is_empty())
186 : {
187 283496 : auto n = grammar::hexdig_value(cs.value());
188 283496 : if(n < 0)
189 : {
190 96674 : if(init_size == cs.size())
191 : {
192 2 : ec = BOOST_HTTP_PROTO_ERR(
193 : error::bad_payload);
194 1 : return 0;
195 : }
196 96673 : return v;
197 : }
198 :
199 : // at least 4 significant bits are free
200 186822 : if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
201 : {
202 2 : ec = BOOST_HTTP_PROTO_ERR(
203 : error::bad_payload);
204 1 : return 0;
205 : }
206 :
207 186821 : v = (v << 4) | static_cast<std::uint64_t>(n);
208 186821 : cs.next();
209 : }
210 38564 : ec = BOOST_HTTP_PROTO_ERR(
211 : error::need_data);
212 19282 : return 0;
213 : }
214 :
215 : void
216 97025 : find_eol(
217 : chained_sequence& cs,
218 : system::error_code& ec) noexcept
219 : {
220 103714 : while(!cs.is_empty())
221 : {
222 103626 : if(cs.value() == '\r')
223 : {
224 96937 : if(!cs.next())
225 10 : break;
226 96927 : if(cs.value() != '\n')
227 : {
228 4 : ec = BOOST_HTTP_PROTO_ERR(
229 : error::bad_payload);
230 2 : return;
231 : }
232 96925 : cs.next();
233 96925 : return;
234 : }
235 6689 : cs.next();
236 : }
237 196 : ec = BOOST_HTTP_PROTO_ERR(
238 : error::need_data);
239 : }
240 :
241 : void
242 111557 : parse_eol(
243 : chained_sequence& cs,
244 : system::error_code& ec) noexcept
245 : {
246 111557 : if(cs.size() >= 2)
247 : {
248 : // we are sure size is at least 2
249 111543 : if(cs.value() == '\r' && *cs.next() == '\n')
250 : {
251 111540 : cs.next();
252 111540 : return;
253 : }
254 6 : ec = BOOST_HTTP_PROTO_ERR(
255 : error::bad_payload);
256 3 : return;
257 : }
258 28 : ec = BOOST_HTTP_PROTO_ERR(
259 : error::need_data);
260 : }
261 :
262 : void
263 4223 : skip_trailer_headers(
264 : chained_sequence& cs,
265 : system::error_code& ec) noexcept
266 : {
267 4507 : while(!cs.is_empty())
268 : {
269 4501 : if(cs.value() == '\r')
270 : {
271 4149 : if(!cs.next())
272 2 : break;
273 4147 : if(cs.value() != '\n')
274 : {
275 4 : ec = BOOST_HTTP_PROTO_ERR(
276 : error::bad_payload);
277 2 : return;
278 : }
279 4145 : cs.next();
280 4145 : return;
281 : }
282 : // skip to the end of field
283 352 : find_eol(cs, ec);
284 352 : if(ec)
285 68 : return;
286 : }
287 16 : ec = BOOST_HTTP_PROTO_ERR(
288 : error::need_data);
289 : }
290 :
291 : template<class UInt>
292 : std::size_t
293 360930 : clamp(
294 : UInt x,
295 : std::size_t limit = (std::numeric_limits<
296 : std::size_t>::max)()) noexcept
297 : {
298 360930 : if(x >= limit)
299 101553 : return limit;
300 259377 : return static_cast<std::size_t>(x);
301 : }
302 :
303 : class zlib_filter
304 : : public detail::zlib_filter_base
305 : {
306 : capy::zlib::inflate_service& svc_;
307 :
308 : public:
309 72 : zlib_filter(
310 : const capy::polystore& ctx,
311 : http_proto::detail::workspace& ws,
312 : int window_bits)
313 72 : : zlib_filter_base(ws)
314 72 : , svc_(ctx.get<capy::zlib::inflate_service>())
315 : {
316 : system::error_code ec = static_cast<capy::zlib::error>(
317 72 : svc_.init2(strm_, window_bits));
318 72 : if(ec != capy::zlib::error::ok)
319 0 : detail::throw_system_error(ec);
320 72 : }
321 :
322 : private:
323 : virtual
324 : results
325 56581 : do_process(
326 : buffers::mutable_buffer out,
327 : buffers::const_buffer in,
328 : bool more) noexcept override
329 : {
330 56581 : strm_.next_out = static_cast<unsigned char*>(out.data());
331 56581 : strm_.avail_out = saturate_cast(out.size());
332 56581 : strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
333 56581 : strm_.avail_in = saturate_cast(in.size());
334 :
335 : auto rs = static_cast<capy::zlib::error>(
336 56581 : svc_.inflate(
337 56581 : strm_,
338 : more ? capy::zlib::no_flush : capy::zlib::finish));
339 :
340 56581 : results rv;
341 56581 : rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
342 56581 : rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
343 56581 : rv.finished = (rs == capy::zlib::error::stream_end);
344 :
345 56581 : if(rs < capy::zlib::error::ok && rs != capy::zlib::error::buf_err)
346 0 : rv.ec = rs;
347 :
348 56581 : return rv;
349 : }
350 : };
351 :
352 : class brotli_filter
353 : : public detail::brotli_filter_base
354 : {
355 : capy::brotli::decode_service& svc_;
356 : capy::brotli::decoder_state* state_;
357 :
358 : public:
359 0 : brotli_filter(
360 : const capy::polystore& ctx,
361 : http_proto::detail::workspace&)
362 0 : : svc_(ctx.get<capy::brotli::decode_service>())
363 : {
364 : // TODO: use custom allocator
365 0 : state_ = svc_.create_instance(nullptr, nullptr, nullptr);
366 :
367 0 : if(!state_)
368 0 : detail::throw_bad_alloc();
369 0 : }
370 :
371 0 : ~brotli_filter()
372 0 : {
373 0 : svc_.destroy_instance(state_);
374 0 : }
375 :
376 : private:
377 : virtual
378 : results
379 0 : do_process(
380 : buffers::mutable_buffer out,
381 : buffers::const_buffer in,
382 : bool more) noexcept override
383 : {
384 0 : auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
385 0 : auto available_in = in.size();
386 0 : auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
387 0 : auto available_out = out.size();
388 :
389 0 : auto rs = svc_.decompress_stream(
390 : state_,
391 : &available_in,
392 : &next_in,
393 : &available_out,
394 : &next_out,
395 : nullptr);
396 :
397 0 : results rv;
398 0 : rv.in_bytes = in.size() - available_in;
399 0 : rv.out_bytes = out.size() - available_out;
400 0 : rv.finished = svc_.is_finished(state_);
401 :
402 0 : if(!more && rs == capy::brotli::decoder_result::needs_more_input)
403 0 : rv.ec = BOOST_HTTP_PROTO_ERR(error::bad_payload);
404 :
405 0 : if(rs == capy::brotli::decoder_result::error)
406 0 : rv.ec = BOOST_HTTP_PROTO_ERR(
407 : svc_.get_error_code(state_));
408 :
409 0 : return rv;
410 : }
411 : };
412 :
413 : class parser_service
414 : {
415 : public:
416 : parser::config_base cfg;
417 : std::size_t space_needed = 0;
418 : std::size_t max_codec = 0;
419 :
420 43 : parser_service(
421 : parser::config_base const& cfg_)
422 43 : : cfg(cfg_)
423 : {
424 : /*
425 : | fb | cb0 | cb1 | C | T | f |
426 :
427 : fb flat_buffer headers.max_size
428 : cb0 circular_buffer min_buffer
429 : cb1 circular_buffer min_buffer
430 : C codec max_codec
431 : T body max_type_erase
432 : f table max_table_space
433 :
434 : */
435 : // validate
436 : //if(cfg.min_prepare > cfg.max_prepare)
437 : //detail::throw_invalid_argument();
438 :
439 43 : if(cfg.max_prepare < 1)
440 0 : detail::throw_invalid_argument();
441 :
442 : // VFALCO TODO OVERFLOW CHECING
443 : {
444 : //fb_.size() - h_.size +
445 : //svc_.cfg.min_buffer +
446 : //svc_.cfg.min_buffer +
447 : //svc_.max_codec;
448 : }
449 :
450 : // VFALCO OVERFLOW CHECKING ON THIS
451 43 : space_needed +=
452 43 : cfg.headers.valid_space_needed();
453 :
454 : // cb0_, cb1_
455 : // VFALCO OVERFLOW CHECKING ON THIS
456 43 : space_needed +=
457 43 : cfg.min_buffer +
458 : cfg.min_buffer;
459 :
460 : // T
461 43 : space_needed += cfg.max_type_erase;
462 :
463 : // max_codec
464 43 : if(cfg.apply_deflate_decoder || cfg.apply_gzip_decoder)
465 : {
466 : // TODO: Account for the number of allocations and
467 : // their overhead in the workspace.
468 :
469 : // https://www.zlib.net/zlib_tech.html
470 : std::size_t n =
471 1 : (1 << cfg.zlib_window_bits) +
472 : (7 * 1024) +
473 : #ifdef __s390x__
474 : 5768 +
475 : #endif
476 : detail::workspace::space_needed<
477 1 : zlib_filter>();
478 :
479 1 : if(max_codec < n)
480 1 : max_codec = n;
481 : }
482 43 : space_needed += max_codec;
483 :
484 : // round up to alignof(detail::header::entry)
485 43 : auto const al = alignof(
486 : detail::header::entry);
487 43 : space_needed = al * ((
488 43 : space_needed + al - 1) / al);
489 43 : }
490 :
491 : std::size_t
492 55255 : max_overread() const noexcept
493 : {
494 : return
495 55255 : cfg.headers.max_size +
496 55255 : cfg.min_buffer;
497 : }
498 : };
499 :
500 : } // namespace
501 :
502 : //------------------------------------------------
503 :
504 : void
505 43 : install_parser_service(
506 : capy::polystore& ctx,
507 : parser::config_base const& cfg)
508 : {
509 43 : ctx.emplace<parser_service>(cfg);
510 43 : }
511 :
512 : //------------------------------------------------
513 :
514 : class parser::impl
515 : {
516 : enum class state
517 : {
518 : reset,
519 : start,
520 : header,
521 : header_done,
522 : body,
523 : set_body,
524 : complete_in_place,
525 : complete
526 : };
527 :
528 : enum class style
529 : {
530 : in_place,
531 : sink,
532 : elastic,
533 : };
534 :
535 : const capy::polystore& ctx_;
536 : parser_service& svc_;
537 :
538 : detail::workspace ws_;
539 : detail::header h_;
540 : std::uint64_t body_limit_;
541 : std::uint64_t body_total_;
542 : std::uint64_t payload_remain_;
543 : std::uint64_t chunk_remain_;
544 : std::size_t body_avail_;
545 : std::size_t nprepare_;
546 :
547 : buffers::flat_buffer fb_;
548 : buffers::circular_buffer cb0_;
549 : buffers::circular_buffer cb1_;
550 :
551 : buffers::mutable_buffer_pair mbp_;
552 : buffers::const_buffer_pair cbp_;
553 :
554 : detail::filter* filter_;
555 : buffers::any_dynamic_buffer* eb_;
556 : sink* sink_;
557 :
558 : state state_;
559 : style style_;
560 : bool got_header_;
561 : bool got_eof_;
562 : bool head_response_;
563 : bool needs_chunk_close_;
564 : bool trailer_headers_;
565 : bool chunked_body_ended;
566 :
567 : public:
568 1056 : impl(const capy::polystore& ctx, detail::kind k)
569 1056 : : ctx_(ctx)
570 1056 : , svc_(ctx.get<parser_service>())
571 1056 : , ws_(svc_.space_needed)
572 1056 : , h_(detail::empty{ k })
573 1056 : , state_(state::reset)
574 1056 : , got_header_(false)
575 : {
576 1056 : }
577 :
578 : bool
579 11938 : got_header() const noexcept
580 : {
581 11938 : return got_header_;
582 : }
583 :
584 : bool
585 51344 : is_complete() const noexcept
586 : {
587 51344 : return state_ >= state::complete_in_place;
588 : }
589 :
590 : detail::header const&
591 316 : safe_get_header() const
592 : {
593 : // headers must be received
594 316 : if(! got_header_)
595 0 : detail::throw_logic_error();
596 :
597 316 : return h_;
598 : }
599 :
600 : bool
601 755 : is_body_set() const noexcept
602 : {
603 755 : return style_ != style::in_place;
604 : }
605 :
606 : void
607 2480 : reset() noexcept
608 : {
609 2480 : ws_.clear();
610 2480 : state_ = state::start;
611 2480 : got_header_ = false;
612 2480 : got_eof_ = false;
613 2480 : }
614 :
615 : void
616 10307 : start(
617 : bool head_response)
618 : {
619 10307 : std::size_t leftover = 0;
620 10307 : switch(state_)
621 : {
622 1 : default:
623 : case state::reset:
624 : // reset must be called first
625 1 : detail::throw_logic_error();
626 :
627 2255 : case state::start:
628 : // reset required on eof
629 2255 : if(got_eof_)
630 0 : detail::throw_logic_error();
631 2255 : break;
632 :
633 3 : case state::header:
634 3 : if(fb_.size() == 0)
635 : {
636 : // start() called twice
637 2 : detail::throw_logic_error();
638 : }
639 : BOOST_FALLTHROUGH;
640 :
641 : case state::header_done:
642 : case state::body:
643 : case state::set_body:
644 : // current message is incomplete
645 2 : detail::throw_logic_error();
646 :
647 8015 : case state::complete_in_place:
648 : // remove available body.
649 8015 : if(is_plain())
650 4000 : cb0_.consume(body_avail_);
651 : BOOST_FALLTHROUGH;
652 :
653 : case state::complete:
654 : {
655 : // move leftovers to front
656 :
657 8047 : ws_.clear();
658 8047 : leftover = cb0_.size();
659 :
660 8047 : auto* dest = reinterpret_cast<char*>(ws_.data());
661 8047 : auto cbp = cb0_.data();
662 8047 : auto* a = static_cast<char const*>(cbp[0].data());
663 8047 : auto* b = static_cast<char const*>(cbp[1].data());
664 8047 : auto an = cbp[0].size();
665 8047 : auto bn = cbp[1].size();
666 :
667 8047 : if(bn == 0)
668 : {
669 7609 : std::memmove(dest, a, an);
670 : }
671 : else
672 : {
673 : // if `a` can fit between `dest` and `b`, shift `b` to the left
674 : // and copy `a` to its position. if `a` fits perfectly, the
675 : // shift will be of size 0.
676 : // if `a` requires more space, shift `b` to the right and
677 : // copy `a` to its position. this process may require multiple
678 : // iterations and should be done chunk by chunk to prevent `b`
679 : // from overlapping with `a`.
680 : do
681 : {
682 : // clamp right shifts to prevent overlap with `a`
683 438 : auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
684 438 : b = static_cast<char const*>(std::memmove(bp, b, bn));
685 :
686 : // a chunk or all of `a` based on available space
687 438 : auto chunk_a = static_cast<std::size_t>(b - dest);
688 438 : std::memcpy(dest, a, chunk_a); // never overlap
689 438 : an -= chunk_a;
690 438 : dest += chunk_a;
691 438 : a += chunk_a;
692 438 : } while(an);
693 : }
694 :
695 8047 : break;
696 : }
697 : }
698 :
699 10302 : ws_.clear();
700 :
701 20604 : fb_ = {
702 10302 : ws_.data(),
703 10302 : svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
704 : leftover };
705 :
706 10302 : BOOST_ASSERT(
707 : fb_.capacity() == svc_.max_overread() - leftover);
708 :
709 10302 : BOOST_ASSERT(
710 : head_response == false ||
711 : h_.kind == detail::kind::response);
712 :
713 10302 : h_ = detail::header(detail::empty{h_.kind});
714 10302 : h_.buf = reinterpret_cast<char*>(ws_.data());
715 10302 : h_.cbuf = h_.buf;
716 10302 : h_.cap = ws_.size();
717 :
718 10302 : state_ = state::header;
719 10302 : style_ = style::in_place;
720 :
721 : // reset to the configured default
722 10302 : body_limit_ = svc_.cfg.body_limit;
723 :
724 10302 : body_total_ = 0;
725 10302 : payload_remain_ = 0;
726 10302 : chunk_remain_ = 0;
727 10302 : body_avail_ = 0;
728 10302 : nprepare_ = 0;
729 :
730 10302 : filter_ = nullptr;
731 10302 : eb_ = nullptr;
732 10302 : sink_ = nullptr;
733 :
734 10302 : got_header_ = false;
735 10302 : head_response_ = head_response;
736 10302 : needs_chunk_close_ = false;
737 10302 : trailer_headers_ = false;
738 10302 : chunked_body_ended = false;
739 10302 : }
740 :
741 : auto
742 51003 : prepare() ->
743 : mutable_buffers_type
744 : {
745 51003 : nprepare_ = 0;
746 :
747 51003 : switch(state_)
748 : {
749 1 : default:
750 : case state::reset:
751 : // reset must be called first
752 1 : detail::throw_logic_error();
753 :
754 1 : case state::start:
755 : // start must be called first
756 1 : detail::throw_logic_error();
757 :
758 10374 : case state::header:
759 : {
760 10374 : BOOST_ASSERT(
761 : h_.size < svc_.cfg.headers.max_size);
762 10374 : std::size_t n = fb_.capacity();
763 10374 : BOOST_ASSERT(n <= svc_.max_overread());
764 10374 : n = clamp(n, svc_.cfg.max_prepare);
765 10374 : mbp_[0] = fb_.prepare(n);
766 10374 : nprepare_ = n;
767 10374 : return mutable_buffers_type(&mbp_[0], 1);
768 : }
769 :
770 0 : case state::header_done:
771 : // forgot to call parse()
772 0 : detail::throw_logic_error();
773 :
774 40626 : case state::body:
775 : {
776 40626 : if(got_eof_)
777 : {
778 : // forgot to call parse()
779 0 : detail::throw_logic_error();
780 : }
781 :
782 40626 : if(! is_plain())
783 : {
784 : // buffered payload
785 21575 : std::size_t n = cb0_.capacity();
786 21575 : n = clamp(n, svc_.cfg.max_prepare);
787 21575 : nprepare_ = n;
788 21575 : mbp_ = cb0_.prepare(n);
789 21575 : return detail::make_span(mbp_);
790 : }
791 : else
792 : {
793 19051 : switch(style_)
794 : {
795 19030 : default:
796 : case style::in_place:
797 : case style::sink:
798 : {
799 19030 : std::size_t n = cb0_.capacity();
800 19030 : n = clamp(n, svc_.cfg.max_prepare);
801 :
802 19030 : if(h_.md.payload == payload::size)
803 : {
804 19005 : if(n > payload_remain_)
805 : {
806 17798 : std::size_t overread =
807 17798 : n - static_cast<std::size_t>(payload_remain_);
808 17798 : if(overread > svc_.max_overread())
809 7878 : n = static_cast<std::size_t>(payload_remain_) +
810 7878 : svc_.max_overread();
811 : }
812 : }
813 : else
814 : {
815 25 : BOOST_ASSERT(
816 : h_.md.payload == payload::to_eof);
817 : // No more messages can be pipelined, so
818 : // limit the output buffer to the remaining
819 : // body limit plus one byte to detect
820 : // exhaustion.
821 25 : std::uint64_t r = body_limit_remain();
822 25 : if(r != std::uint64_t(-1))
823 25 : r += 1;
824 25 : n = clamp(r, n);
825 : }
826 :
827 19030 : nprepare_ = n;
828 19030 : mbp_ = cb0_.prepare(n);
829 19030 : return detail::make_span(mbp_);
830 : }
831 21 : case style::elastic:
832 : {
833 21 : BOOST_ASSERT(cb0_.size() == 0);
834 21 : BOOST_ASSERT(body_avail_ == 0);
835 :
836 21 : std::size_t n = svc_.cfg.min_buffer;
837 :
838 21 : if(h_.md.payload == payload::size)
839 : {
840 : // Overreads are not allowed, or
841 : // else the caller will see extra
842 : // unrelated data.
843 6 : n = clamp(payload_remain_, n);
844 : }
845 : else
846 : {
847 15 : BOOST_ASSERT(
848 : h_.md.payload == payload::to_eof);
849 : // No more messages can be pipelined, so
850 : // limit the output buffer to the remaining
851 : // body limit plus one byte to detect
852 : // exhaustion.
853 15 : std::uint64_t r = body_limit_remain();
854 15 : if(r != std::uint64_t(-1))
855 15 : r += 1;
856 15 : n = clamp(r, n);
857 15 : n = clamp(n, eb_->max_size() - eb_->size());
858 : // fill capacity first to avoid an allocation
859 : std::size_t avail =
860 15 : eb_->capacity() - eb_->size();
861 15 : if(avail != 0)
862 15 : n = clamp(n, avail);
863 :
864 15 : if(n == 0)
865 : {
866 : // dynamic buffer is full
867 : // attempt a 1 byte read so
868 : // we can detect overflow
869 1 : nprepare_ = 1;
870 1 : mbp_ = cb0_.prepare(1);
871 1 : return detail::make_span(mbp_);
872 : }
873 : }
874 :
875 20 : n = clamp(n, svc_.cfg.max_prepare);
876 20 : BOOST_ASSERT(n != 0);
877 20 : nprepare_ = n;
878 20 : return eb_->prepare(n);
879 : }
880 : }
881 : }
882 : }
883 :
884 0 : case state::set_body:
885 : // forgot to call parse()
886 0 : detail::throw_logic_error();
887 :
888 1 : case state::complete_in_place:
889 : case state::complete:
890 : // already complete
891 1 : detail::throw_logic_error();
892 : }
893 : }
894 :
895 : void
896 51000 : commit(
897 : std::size_t n)
898 : {
899 51000 : switch(state_)
900 : {
901 1 : default:
902 : case state::reset:
903 : {
904 : // reset must be called first
905 1 : detail::throw_logic_error();
906 : }
907 :
908 1 : case state::start:
909 : {
910 : // forgot to call start()
911 1 : detail::throw_logic_error();
912 : }
913 :
914 10374 : case state::header:
915 : {
916 10374 : if(n > nprepare_)
917 : {
918 : // n can't be greater than size of
919 : // the buffers returned by prepare()
920 1 : detail::throw_invalid_argument();
921 : }
922 :
923 10373 : if(got_eof_)
924 : {
925 : // can't commit after EOF
926 1 : detail::throw_logic_error();
927 : }
928 :
929 10372 : nprepare_ = 0; // invalidate
930 10372 : fb_.commit(n);
931 10372 : break;
932 : }
933 :
934 0 : case state::header_done:
935 : {
936 : // forgot to call parse()
937 0 : detail::throw_logic_error();
938 : }
939 :
940 40623 : case state::body:
941 : {
942 40623 : if(n > nprepare_)
943 : {
944 : // n can't be greater than size of
945 : // the buffers returned by prepare()
946 2 : detail::throw_invalid_argument();
947 : }
948 :
949 40621 : if(got_eof_)
950 : {
951 : // can't commit after EOF
952 0 : detail::throw_logic_error();
953 : }
954 :
955 40621 : nprepare_ = 0; // invalidate
956 40621 : if(is_plain() && style_ == style::elastic)
957 : {
958 20 : if(eb_->max_size() == eb_->size())
959 : {
960 : // borrowed 1 byte from
961 : // cb0_ in prepare()
962 1 : BOOST_ASSERT(n <= 1);
963 1 : cb0_.commit(n);
964 : }
965 : else
966 : {
967 19 : eb_->commit(n);
968 19 : payload_remain_ -= n;
969 19 : body_total_ += n;
970 : }
971 : }
972 : else
973 : {
974 40601 : cb0_.commit(n);
975 : }
976 40621 : break;
977 : }
978 :
979 0 : case state::set_body:
980 : {
981 : // forgot to call parse()
982 0 : detail::throw_logic_error();
983 : }
984 :
985 1 : case state::complete_in_place:
986 : case state::complete:
987 : {
988 : // already complete
989 1 : detail::throw_logic_error();
990 : }
991 : }
992 50993 : }
993 :
994 : void
995 401 : commit_eof()
996 : {
997 401 : nprepare_ = 0; // invalidate
998 :
999 401 : switch(state_)
1000 : {
1001 1 : default:
1002 : case state::reset:
1003 : // reset must be called first
1004 1 : detail::throw_logic_error();
1005 :
1006 1 : case state::start:
1007 : // forgot to call start()
1008 1 : detail::throw_logic_error();
1009 :
1010 30 : case state::header:
1011 30 : got_eof_ = true;
1012 30 : break;
1013 :
1014 0 : case state::header_done:
1015 : // forgot to call parse()
1016 0 : detail::throw_logic_error();
1017 :
1018 368 : case state::body:
1019 368 : got_eof_ = true;
1020 368 : break;
1021 :
1022 0 : case state::set_body:
1023 : // forgot to call parse()
1024 0 : detail::throw_logic_error();
1025 :
1026 1 : case state::complete_in_place:
1027 : case state::complete:
1028 : // can't commit eof when complete
1029 1 : detail::throw_logic_error();
1030 : }
1031 398 : }
1032 :
1033 : void
1034 69827 : parse(
1035 : system::error_code& ec)
1036 : {
1037 69827 : ec = {};
1038 69827 : switch(state_)
1039 : {
1040 1 : default:
1041 : case state::reset:
1042 : // reset must be called first
1043 1 : detail::throw_logic_error();
1044 :
1045 1 : case state::start:
1046 : // start must be called first
1047 1 : detail::throw_logic_error();
1048 :
1049 16650 : case state::header:
1050 : {
1051 16650 : BOOST_ASSERT(h_.buf == static_cast<
1052 : void const*>(ws_.data()));
1053 16650 : BOOST_ASSERT(h_.cbuf == static_cast<
1054 : void const*>(ws_.data()));
1055 :
1056 16650 : h_.parse(fb_.size(), svc_.cfg.headers, ec);
1057 :
1058 16650 : if(ec == condition::need_more_input)
1059 : {
1060 6385 : if(! got_eof_)
1061 : {
1062 : // headers incomplete
1063 6358 : return;
1064 : }
1065 :
1066 27 : if(fb_.size() == 0)
1067 : {
1068 : // stream closed cleanly
1069 12 : state_ = state::reset;
1070 24 : ec = BOOST_HTTP_PROTO_ERR(
1071 : error::end_of_stream);
1072 12 : return;
1073 : }
1074 :
1075 : // stream closed with a
1076 : // partial message received
1077 15 : state_ = state::reset;
1078 30 : ec = BOOST_HTTP_PROTO_ERR(
1079 : error::incomplete);
1080 15 : return;
1081 : }
1082 10265 : else if(ec.failed())
1083 : {
1084 : // other error,
1085 : //
1086 : // VFALCO map this to a bad
1087 : // request or bad response error?
1088 : //
1089 259 : state_ = state::reset; // unrecoverable
1090 259 : return;
1091 : }
1092 :
1093 10006 : got_header_ = true;
1094 :
1095 : // reserve headers + table
1096 10006 : ws_.reserve_front(h_.size);
1097 10006 : ws_.reserve_back(h_.table_space());
1098 :
1099 : // no payload
1100 10006 : if(h_.md.payload == payload::none ||
1101 9084 : head_response_)
1102 : {
1103 : // octets of the next message
1104 922 : auto overread = fb_.size() - h_.size;
1105 922 : cb0_ = { ws_.data(), overread, overread };
1106 922 : ws_.reserve_front(overread);
1107 922 : state_ = state::complete_in_place;
1108 922 : return;
1109 : }
1110 :
1111 9084 : state_ = state::header_done;
1112 9084 : break;
1113 : }
1114 :
1115 9083 : case state::header_done:
1116 : {
1117 : // metadata error
1118 9083 : if(h_.md.payload == payload::error)
1119 : {
1120 : // VFALCO This needs looking at
1121 360 : ec = BOOST_HTTP_PROTO_ERR(
1122 : error::bad_payload);
1123 180 : state_ = state::reset; // unrecoverable
1124 180 : return;
1125 : }
1126 :
1127 : // overread currently includes any and all octets that
1128 : // extend beyond the current end of the header
1129 : // this can include associated body octets for the
1130 : // current message or octets of the next message in the
1131 : // stream, e.g. pipelining is being used
1132 8903 : auto const overread = fb_.size() - h_.size;
1133 8903 : BOOST_ASSERT(overread <= svc_.max_overread());
1134 :
1135 8903 : auto cap = fb_.capacity() + overread +
1136 8903 : svc_.cfg.min_buffer;
1137 :
1138 : // reserve body buffers first, as the decoder
1139 : // must be installed after them.
1140 8903 : auto const p = ws_.reserve_front(cap);
1141 :
1142 8903 : switch(h_.md.content_encoding.coding)
1143 : {
1144 36 : case content_coding::deflate:
1145 36 : if(!svc_.cfg.apply_deflate_decoder)
1146 0 : goto no_filter;
1147 72 : filter_ = &ws_.emplace<zlib_filter>(
1148 36 : ctx_, ws_, svc_.cfg.zlib_window_bits);
1149 36 : break;
1150 :
1151 36 : case content_coding::gzip:
1152 36 : if(!svc_.cfg.apply_gzip_decoder)
1153 0 : goto no_filter;
1154 72 : filter_ = &ws_.emplace<zlib_filter>(
1155 36 : ctx_, ws_, svc_.cfg.zlib_window_bits + 16);
1156 36 : break;
1157 :
1158 0 : case content_coding::br:
1159 0 : if(!svc_.cfg.apply_brotli_decoder)
1160 0 : goto no_filter;
1161 0 : filter_ = &ws_.emplace<brotli_filter>(
1162 0 : ctx_, ws_);
1163 0 : break;
1164 :
1165 0 : no_filter:
1166 8831 : default:
1167 8831 : cap += svc_.max_codec;
1168 8831 : ws_.reserve_front(svc_.max_codec);
1169 8831 : break;
1170 : }
1171 :
1172 8903 : if(is_plain() || style_ == style::elastic)
1173 : {
1174 4722 : cb0_ = { p, cap, overread };
1175 4722 : cb1_ = {};
1176 : }
1177 : else
1178 : {
1179 : // buffered payload
1180 8362 : std::size_t n0 = (overread > svc_.cfg.min_buffer)
1181 4181 : ? overread
1182 4157 : : svc_.cfg.min_buffer;
1183 4181 : std::size_t n1 = svc_.cfg.min_buffer;
1184 :
1185 4181 : cb0_ = { p , n0, overread };
1186 4181 : cb1_ = { p + n0 , n1 };
1187 : }
1188 :
1189 8903 : if(h_.md.payload == payload::size)
1190 : {
1191 4374 : if(!filter_ &&
1192 4350 : body_limit_ < h_.md.payload_size)
1193 : {
1194 2 : ec = BOOST_HTTP_PROTO_ERR(
1195 : error::body_too_large);
1196 1 : state_ = state::reset;
1197 1 : return;
1198 : }
1199 4373 : payload_remain_ = h_.md.payload_size;
1200 : }
1201 :
1202 8902 : state_ = state::body;
1203 : BOOST_FALLTHROUGH;
1204 : }
1205 :
1206 : case state::body:
1207 : {
1208 50201 : do_body:
1209 50201 : BOOST_ASSERT(state_ == state::body);
1210 50201 : BOOST_ASSERT(h_.md.payload != payload::none);
1211 50201 : BOOST_ASSERT(h_.md.payload != payload::error);
1212 :
1213 8797 : auto set_state_to_complete = [&]()
1214 : {
1215 8797 : if(style_ == style::in_place)
1216 : {
1217 8337 : state_ = state::complete_in_place;
1218 8337 : return;
1219 : }
1220 460 : state_ = state::complete;
1221 50201 : };
1222 :
1223 50201 : if(h_.md.payload == payload::chunked)
1224 : {
1225 : for(;;)
1226 : {
1227 125495 : if(chunk_remain_ == 0
1228 124342 : && !chunked_body_ended)
1229 : {
1230 120197 : auto cs = chained_sequence(cb0_.data());
1231 19411 : auto check_ec = [&]()
1232 : {
1233 19411 : if(ec == condition::need_more_input && got_eof_)
1234 : {
1235 0 : ec = BOOST_HTTP_PROTO_ERR(error::incomplete);
1236 0 : state_ = state::reset;
1237 : }
1238 139608 : };
1239 :
1240 120197 : if(needs_chunk_close_)
1241 : {
1242 111557 : parse_eol(cs, ec);
1243 111557 : if(ec)
1244 : {
1245 17 : check_ec();
1246 19411 : return;
1247 : }
1248 : }
1249 8640 : else if(trailer_headers_)
1250 : {
1251 4223 : skip_trailer_headers(cs, ec);
1252 4223 : if(ec)
1253 : {
1254 78 : check_ec();
1255 78 : return;
1256 : }
1257 4145 : cb0_.consume(cb0_.size() - cs.size());
1258 4145 : chunked_body_ended = true;
1259 8292 : continue;
1260 : }
1261 :
1262 115957 : auto chunk_size = parse_hex(cs, ec);
1263 115957 : if(ec)
1264 : {
1265 19284 : check_ec();
1266 19284 : return;
1267 : }
1268 :
1269 : // skip chunk extensions
1270 96673 : find_eol(cs, ec);
1271 96673 : if(ec)
1272 : {
1273 32 : check_ec();
1274 32 : return;
1275 : }
1276 :
1277 96641 : cb0_.consume(cb0_.size() - cs.size());
1278 96641 : chunk_remain_ = chunk_size;
1279 :
1280 96641 : needs_chunk_close_ = true;
1281 96641 : if(chunk_remain_ == 0)
1282 : {
1283 4147 : needs_chunk_close_ = false;
1284 4147 : trailer_headers_ = true;
1285 4147 : continue;
1286 : }
1287 : }
1288 :
1289 97792 : if(cb0_.size() == 0 && !chunked_body_ended)
1290 : {
1291 340 : if(got_eof_)
1292 : {
1293 2 : ec = BOOST_HTTP_PROTO_ERR(
1294 : error::incomplete);
1295 1 : state_ = state::reset;
1296 1 : return;
1297 : }
1298 :
1299 678 : ec = BOOST_HTTP_PROTO_ERR(
1300 : error::need_data);
1301 339 : return;
1302 : }
1303 :
1304 97452 : if(filter_)
1305 : {
1306 51070 : chunk_remain_ -= apply_filter(
1307 : ec,
1308 : clamp(chunk_remain_, cb0_.size()),
1309 51070 : !chunked_body_ended);
1310 :
1311 51070 : if(ec || chunked_body_ended)
1312 564 : return;
1313 : }
1314 : else
1315 : {
1316 : const std::size_t chunk_avail =
1317 46382 : clamp(chunk_remain_, cb0_.size());
1318 : const auto chunk =
1319 46382 : buffers::prefix(cb0_.data(), chunk_avail);
1320 :
1321 46382 : if(body_limit_remain() < chunk_avail)
1322 : {
1323 0 : ec = BOOST_HTTP_PROTO_ERR(
1324 : error::body_too_large);
1325 0 : state_ = state::reset;
1326 4121 : return;
1327 : }
1328 :
1329 46382 : switch(style_)
1330 : {
1331 46382 : case style::in_place:
1332 : {
1333 46382 : auto copied = buffers::copy(
1334 46382 : cb1_.prepare(cb1_.capacity()),
1335 : chunk);
1336 46382 : chunk_remain_ -= copied;
1337 46382 : body_avail_ += copied;
1338 46382 : body_total_ += copied;
1339 46382 : cb0_.consume(copied);
1340 46382 : cb1_.commit(copied);
1341 46382 : if(cb1_.capacity() == 0
1342 46382 : && !chunked_body_ended)
1343 : {
1344 0 : ec = BOOST_HTTP_PROTO_ERR(
1345 : error::in_place_overflow);
1346 0 : return;
1347 : }
1348 46382 : break;
1349 : }
1350 0 : case style::sink:
1351 : {
1352 0 : auto sink_rs = sink_->write(
1353 0 : chunk, !chunked_body_ended);
1354 0 : chunk_remain_ -= sink_rs.bytes;
1355 0 : body_total_ += sink_rs.bytes;
1356 0 : cb0_.consume(sink_rs.bytes);
1357 0 : if(sink_rs.ec.failed())
1358 : {
1359 0 : body_avail_ +=
1360 0 : chunk_avail - sink_rs.bytes;
1361 0 : ec = sink_rs.ec;
1362 0 : state_ = state::reset;
1363 0 : return;
1364 : }
1365 0 : break;
1366 : }
1367 0 : case style::elastic:
1368 : {
1369 0 : if(eb_->max_size() - eb_->size()
1370 0 : < chunk_avail)
1371 : {
1372 0 : ec = BOOST_HTTP_PROTO_ERR(
1373 : error::buffer_overflow);
1374 0 : state_ = state::reset;
1375 0 : return;
1376 : }
1377 0 : buffers::copy(
1378 0 : eb_->prepare(chunk_avail),
1379 : chunk);
1380 0 : chunk_remain_ -= chunk_avail;
1381 0 : body_total_ += chunk_avail;
1382 0 : cb0_.consume(chunk_avail);
1383 0 : eb_->commit(chunk_avail);
1384 0 : break;
1385 : }
1386 : }
1387 :
1388 46382 : if(chunked_body_ended)
1389 : {
1390 4121 : set_state_to_complete();
1391 4121 : return;
1392 : }
1393 : }
1394 101059 : }
1395 : }
1396 : else
1397 : {
1398 : // non-chunked payload
1399 :
1400 77295 : const std::size_t payload_avail = [&]()
1401 : {
1402 25765 : auto ret = cb0_.size();
1403 25765 : if(!filter_)
1404 24121 : ret -= body_avail_;
1405 25765 : if(h_.md.payload == payload::size)
1406 24159 : return clamp(payload_remain_, ret);
1407 : // payload::eof
1408 1606 : return ret;
1409 25765 : }();
1410 :
1411 77295 : const bool is_complete = [&]()
1412 : {
1413 25765 : if(h_.md.payload == payload::size)
1414 24159 : return payload_avail == payload_remain_;
1415 : // payload::eof
1416 1606 : return got_eof_;
1417 25765 : }();
1418 :
1419 25765 : if(filter_)
1420 : {
1421 3288 : payload_remain_ -= apply_filter(
1422 1644 : ec, payload_avail, !is_complete);
1423 1644 : if(ec || is_complete)
1424 1128 : return;
1425 : }
1426 : else
1427 : {
1428 : // plain body
1429 :
1430 24121 : if(h_.md.payload == payload::to_eof)
1431 : {
1432 764 : if(body_limit_remain() < payload_avail)
1433 : {
1434 2 : ec = BOOST_HTTP_PROTO_ERR(
1435 : error::body_too_large);
1436 1 : state_ = state::reset;
1437 1 : return;
1438 : }
1439 : }
1440 :
1441 24120 : switch(style_)
1442 : {
1443 23363 : case style::in_place:
1444 : {
1445 23363 : payload_remain_ -= payload_avail;
1446 23363 : body_avail_ += payload_avail;
1447 23363 : body_total_ += payload_avail;
1448 23363 : if(cb0_.capacity() == 0 && !is_complete)
1449 : {
1450 2 : ec = BOOST_HTTP_PROTO_ERR(
1451 : error::in_place_overflow);
1452 1 : return;
1453 : }
1454 23362 : break;
1455 : }
1456 371 : case style::sink:
1457 : {
1458 371 : payload_remain_ -= payload_avail;
1459 371 : body_total_ += payload_avail;
1460 371 : auto sink_rs = sink_->write(
1461 371 : buffers::prefix(cb0_.data(), payload_avail),
1462 371 : !is_complete);
1463 371 : cb0_.consume(sink_rs.bytes);
1464 371 : if(sink_rs.ec.failed())
1465 : {
1466 0 : body_avail_ +=
1467 0 : payload_avail - sink_rs.bytes;
1468 0 : ec = sink_rs.ec;
1469 0 : state_ = state::reset;
1470 0 : return;
1471 : }
1472 371 : break;
1473 : }
1474 386 : case style::elastic:
1475 : {
1476 : // payload_remain_ and body_total_
1477 : // are already updated in commit()
1478 :
1479 : // cb0_ contains data
1480 386 : if(payload_avail != 0)
1481 : {
1482 193 : if(eb_->max_size() - eb_->size()
1483 193 : < payload_avail)
1484 : {
1485 2 : ec = BOOST_HTTP_PROTO_ERR(
1486 : error::buffer_overflow);
1487 1 : state_ = state::reset;
1488 1 : return;
1489 : }
1490 : // only happens when an elastic body
1491 : // is attached in header_done state
1492 192 : buffers::copy(
1493 192 : eb_->prepare(payload_avail),
1494 192 : cb0_.data());
1495 192 : cb0_.consume(payload_avail);
1496 192 : eb_->commit(payload_avail);
1497 192 : payload_remain_ -= payload_avail;
1498 192 : body_total_ += payload_avail;
1499 : }
1500 385 : break;
1501 : }
1502 : }
1503 :
1504 24118 : if(is_complete)
1505 : {
1506 4676 : set_state_to_complete();
1507 4676 : return;
1508 : }
1509 : }
1510 :
1511 19958 : if(h_.md.payload == payload::size && got_eof_)
1512 : {
1513 2 : ec = BOOST_HTTP_PROTO_ERR(
1514 : error::incomplete);
1515 1 : state_ = state::reset;
1516 1 : return;
1517 : }
1518 :
1519 39914 : ec = BOOST_HTTP_PROTO_ERR(
1520 : error::need_data);
1521 19957 : return;
1522 : }
1523 :
1524 : break;
1525 : }
1526 :
1527 2333 : case state::set_body:
1528 : case state::complete_in_place:
1529 : {
1530 2333 : auto& body_buf = is_plain() ? cb0_ : cb1_;
1531 :
1532 2333 : switch(style_)
1533 : {
1534 2216 : case style::in_place:
1535 2216 : return; // no-op
1536 58 : case style::sink:
1537 : {
1538 58 : auto rs = sink_->write(
1539 58 : buffers::prefix(body_buf.data(), body_avail_),
1540 58 : state_ == state::set_body);
1541 58 : body_buf.consume(rs.bytes);
1542 58 : body_avail_ -= rs.bytes;
1543 58 : if(rs.ec.failed())
1544 : {
1545 0 : ec = rs.ec;
1546 0 : state_ = state::reset;
1547 0 : return;
1548 : }
1549 58 : break;
1550 : }
1551 59 : case style::elastic:
1552 : {
1553 59 : if(eb_->max_size() - eb_->size()
1554 59 : < body_avail_)
1555 : {
1556 0 : ec = BOOST_HTTP_PROTO_ERR(
1557 : error::buffer_overflow);
1558 0 : return;
1559 : }
1560 59 : buffers::copy(
1561 59 : eb_->prepare(body_avail_),
1562 59 : body_buf.data());
1563 59 : body_buf.consume(body_avail_);
1564 59 : eb_->commit(body_avail_);
1565 59 : body_avail_ = 0;
1566 : // TODO: expand cb0_ when possible?
1567 59 : break;
1568 : }
1569 : }
1570 :
1571 117 : if(state_ == state::set_body)
1572 : {
1573 0 : state_ = state::body;
1574 0 : goto do_body;
1575 : }
1576 :
1577 117 : state_ = state::complete;
1578 117 : break;
1579 : }
1580 :
1581 460 : case state::complete:
1582 460 : break;
1583 : }
1584 : }
1585 :
1586 : auto
1587 41250 : pull_body() ->
1588 : const_buffers_type
1589 : {
1590 41250 : switch(state_)
1591 : {
1592 0 : case state::header_done:
1593 0 : return {};
1594 41250 : case state::body:
1595 : case state::complete_in_place:
1596 41250 : cbp_ = buffers::prefix(
1597 41250 : (is_plain() ? cb0_ : cb1_).data(),
1598 : body_avail_);
1599 41250 : return detail::make_span(cbp_);
1600 0 : default:
1601 0 : detail::throw_logic_error();
1602 : }
1603 : }
1604 :
1605 : void
1606 39606 : consume_body(std::size_t n)
1607 : {
1608 39606 : switch(state_)
1609 : {
1610 0 : case state::header_done:
1611 0 : return;
1612 39606 : case state::body:
1613 : case state::complete_in_place:
1614 39606 : n = clamp(n, body_avail_);
1615 39606 : (is_plain() ? cb0_ : cb1_).consume(n);
1616 39606 : body_avail_ -= n;
1617 39606 : return;
1618 0 : default:
1619 0 : detail::throw_logic_error();
1620 : }
1621 : }
1622 :
1623 : core::string_view
1624 700 : body() const
1625 : {
1626 : // Precondition violation
1627 700 : if(state_ != state::complete_in_place)
1628 0 : detail::throw_logic_error();
1629 :
1630 : // Precondition violation
1631 700 : if(body_avail_ != body_total_)
1632 0 : detail::throw_logic_error();
1633 :
1634 700 : auto cbp = (is_plain() ? cb0_ : cb1_).data();
1635 700 : BOOST_ASSERT(cbp[1].size() == 0);
1636 700 : BOOST_ASSERT(cbp[0].size() == body_avail_);
1637 700 : return core::string_view(
1638 700 : static_cast<char const*>(cbp[0].data()),
1639 1400 : body_avail_);
1640 : }
1641 :
1642 : void
1643 77 : set_body_limit(std::uint64_t n)
1644 : {
1645 77 : switch(state_)
1646 : {
1647 73 : case state::header:
1648 : case state::header_done:
1649 73 : body_limit_ = n;
1650 73 : break;
1651 2 : case state::complete_in_place:
1652 : // only allowed for empty bodies
1653 2 : if(body_total_ == 0)
1654 1 : break;
1655 : BOOST_FALLTHROUGH;
1656 : default:
1657 : // set body_limit before parsing the body
1658 3 : detail::throw_logic_error();
1659 : }
1660 74 : }
1661 :
1662 : void
1663 383 : set_body(
1664 : buffers::any_dynamic_buffer& eb) noexcept
1665 : {
1666 383 : eb_ = &eb;
1667 383 : style_ = style::elastic;
1668 383 : nprepare_ = 0; // invalidate
1669 383 : if(state_ == state::body)
1670 0 : state_ = state::set_body;
1671 383 : }
1672 :
1673 : void
1674 372 : set_body(sink& s) noexcept
1675 : {
1676 372 : sink_ = &s;
1677 372 : style_ = style::sink;
1678 372 : nprepare_ = 0; // invalidate
1679 372 : if(state_ == state::body)
1680 0 : state_ = state::set_body;
1681 372 : }
1682 :
1683 : detail::workspace&
1684 755 : ws() noexcept
1685 : {
1686 755 : return ws_;
1687 : }
1688 :
1689 : private:
1690 : bool
1691 182054 : is_plain() const noexcept
1692 : {
1693 354400 : return ! filter_ &&
1694 354400 : h_.md.payload != payload::chunked;
1695 : }
1696 :
1697 : std::uint64_t
1698 157918 : body_limit_remain() const noexcept
1699 : {
1700 157918 : return body_limit_ - body_total_;
1701 : }
1702 :
1703 : std::size_t
1704 52714 : apply_filter(
1705 : system::error_code& ec,
1706 : std::size_t payload_avail,
1707 : bool more)
1708 : {
1709 52714 : std::size_t p0 = payload_avail;
1710 : for(;;)
1711 : {
1712 107150 : if(payload_avail == 0 && more)
1713 51094 : break;
1714 :
1715 0 : auto f_rs = [&](){
1716 56176 : BOOST_ASSERT(filter_ != nullptr);
1717 56176 : if(style_ == style::elastic)
1718 : {
1719 18143 : std::size_t n = clamp(body_limit_remain());
1720 18143 : n = clamp(n, svc_.cfg.min_buffer);
1721 18143 : n = clamp(n, eb_->max_size() - eb_->size());
1722 :
1723 : // fill capacity first to avoid
1724 : // an allocation
1725 : std::size_t avail =
1726 18143 : eb_->capacity() - eb_->size();
1727 18143 : if(avail != 0)
1728 18143 : n = clamp(n, avail);
1729 :
1730 36286 : return filter_->process(
1731 18143 : eb_->prepare(n),
1732 18143 : buffers::prefix(cb0_.data(), payload_avail),
1733 36286 : more);
1734 : }
1735 : else // in-place and sink
1736 : {
1737 38033 : std::size_t n = clamp(body_limit_remain());
1738 38033 : n = clamp(n, cb1_.capacity());
1739 :
1740 76066 : return filter_->process(
1741 38033 : detail::make_span(cb1_.prepare(n)),
1742 38033 : buffers::prefix(cb0_.data(), payload_avail),
1743 76066 : more);
1744 : }
1745 56176 : }();
1746 :
1747 56176 : cb0_.consume(f_rs.in_bytes);
1748 56176 : payload_avail -= f_rs.in_bytes;
1749 56176 : body_total_ += f_rs.out_bytes;
1750 :
1751 56176 : switch(style_)
1752 : {
1753 20026 : case style::in_place:
1754 : {
1755 20026 : cb1_.commit(f_rs.out_bytes);
1756 20026 : body_avail_ += f_rs.out_bytes;
1757 20026 : if(cb1_.capacity() == 0 &&
1758 20026 : !f_rs.finished && f_rs.in_bytes == 0)
1759 : {
1760 3240 : ec = BOOST_HTTP_PROTO_ERR(
1761 : error::in_place_overflow);
1762 1620 : goto done;
1763 : }
1764 18406 : break;
1765 : }
1766 18007 : case style::sink:
1767 : {
1768 18007 : cb1_.commit(f_rs.out_bytes);
1769 18007 : auto sink_rs = sink_->write(
1770 18007 : cb1_.data(), !f_rs.finished || more);
1771 18007 : cb1_.consume(sink_rs.bytes);
1772 18007 : if(sink_rs.ec.failed())
1773 : {
1774 0 : ec = sink_rs.ec;
1775 0 : state_ = state::reset;
1776 0 : goto done;
1777 : }
1778 18007 : break;
1779 : }
1780 18143 : case style::elastic:
1781 : {
1782 18143 : eb_->commit(f_rs.out_bytes);
1783 18143 : if(eb_->max_size() - eb_->size() == 0 &&
1784 18143 : !f_rs.finished && f_rs.in_bytes == 0)
1785 : {
1786 0 : ec = BOOST_HTTP_PROTO_ERR(
1787 : error::buffer_overflow);
1788 0 : state_ = state::reset;
1789 0 : goto done;
1790 : }
1791 18143 : break;
1792 : }
1793 : }
1794 :
1795 54556 : if(f_rs.ec.failed())
1796 : {
1797 0 : ec = f_rs.ec;
1798 0 : state_ = state::reset;
1799 0 : break;
1800 : }
1801 :
1802 54556 : if(body_limit_remain() == 0 &&
1803 54556 : !f_rs.finished && f_rs.in_bytes == 0)
1804 : {
1805 0 : ec = BOOST_HTTP_PROTO_ERR(
1806 : error::body_too_large);
1807 0 : state_ = state::reset;
1808 0 : break;
1809 : }
1810 :
1811 54556 : if(f_rs.finished)
1812 : {
1813 120 : if(!more)
1814 : {
1815 72 : state_ = (style_ == style::in_place)
1816 72 : ? state::complete_in_place
1817 : : state::complete;
1818 : }
1819 120 : break;
1820 : }
1821 54436 : }
1822 :
1823 52714 : done:
1824 52714 : return p0 - payload_avail;
1825 : }
1826 : };
1827 :
1828 : //------------------------------------------------
1829 : //
1830 : // Special Members
1831 : //
1832 : //------------------------------------------------
1833 :
1834 1070 : parser::
1835 : ~parser()
1836 : {
1837 1070 : delete impl_;
1838 1070 : }
1839 :
1840 11 : parser::
1841 11 : parser() noexcept
1842 11 : : impl_(nullptr)
1843 : {
1844 11 : }
1845 :
1846 3 : parser::
1847 3 : parser(parser&& other) noexcept
1848 3 : : impl_(other.impl_)
1849 : {
1850 3 : other.impl_ = nullptr;
1851 3 : }
1852 :
1853 1056 : parser::
1854 : parser(
1855 : capy::polystore& ctx,
1856 1056 : detail::kind k)
1857 1056 : : impl_(new impl(ctx, k))
1858 : {
1859 : // TODO: use a single allocation for
1860 : // impl and workspace buffer.
1861 1056 : }
1862 :
1863 : void
1864 4 : parser::
1865 : assign(parser&& other) noexcept
1866 : {
1867 4 : if(this == &other)
1868 0 : return;
1869 4 : delete impl_;
1870 4 : impl_ = other.impl_;
1871 4 : other.impl_ = nullptr;
1872 : }
1873 :
1874 : //--------------------------------------------
1875 : //
1876 : // Observers
1877 : //
1878 : //--------------------------------------------
1879 :
1880 : bool
1881 11938 : parser::got_header() const noexcept
1882 : {
1883 11938 : BOOST_ASSERT(impl_);
1884 11938 : return impl_->got_header();
1885 : }
1886 :
1887 : bool
1888 51344 : parser::is_complete() const noexcept
1889 : {
1890 51344 : BOOST_ASSERT(impl_);
1891 51344 : return impl_->is_complete();
1892 : }
1893 :
1894 : //------------------------------------------------
1895 : //
1896 : // Modifiers
1897 : //
1898 : //------------------------------------------------
1899 :
1900 : void
1901 2480 : parser::
1902 : reset() noexcept
1903 : {
1904 2480 : BOOST_ASSERT(impl_);
1905 2480 : impl_->reset();
1906 2480 : }
1907 :
1908 : void
1909 10307 : parser::start()
1910 : {
1911 10307 : BOOST_ASSERT(impl_);
1912 10307 : impl_->start(false);
1913 10302 : }
1914 :
1915 : auto
1916 51003 : parser::
1917 : prepare() ->
1918 : mutable_buffers_type
1919 : {
1920 51003 : BOOST_ASSERT(impl_);
1921 51003 : return impl_->prepare();
1922 : }
1923 :
1924 : void
1925 51000 : parser::
1926 : commit(
1927 : std::size_t n)
1928 : {
1929 51000 : BOOST_ASSERT(impl_);
1930 51000 : impl_->commit(n);
1931 50993 : }
1932 :
1933 : void
1934 401 : parser::
1935 : commit_eof()
1936 : {
1937 401 : BOOST_ASSERT(impl_);
1938 401 : impl_->commit_eof();
1939 398 : }
1940 :
1941 : void
1942 69827 : parser::
1943 : parse(
1944 : system::error_code& ec)
1945 : {
1946 69827 : BOOST_ASSERT(impl_);
1947 69827 : impl_->parse(ec);
1948 69825 : }
1949 :
1950 : auto
1951 41250 : parser::
1952 : pull_body() ->
1953 : const_buffers_type
1954 : {
1955 41250 : BOOST_ASSERT(impl_);
1956 41250 : return impl_->pull_body();
1957 : }
1958 :
1959 : void
1960 39606 : parser::
1961 : consume_body(std::size_t n)
1962 : {
1963 39606 : BOOST_ASSERT(impl_);
1964 39606 : impl_->consume_body(n);
1965 39606 : }
1966 :
1967 : core::string_view
1968 700 : parser::
1969 : body() const
1970 : {
1971 700 : BOOST_ASSERT(impl_);
1972 700 : return impl_->body();
1973 : }
1974 :
1975 : core::string_view
1976 0 : parser::
1977 : release_buffered_data() noexcept
1978 : {
1979 : // TODO
1980 0 : return {};
1981 : }
1982 :
1983 : void
1984 77 : parser::
1985 : set_body_limit(std::uint64_t n)
1986 : {
1987 77 : BOOST_ASSERT(impl_);
1988 77 : impl_->set_body_limit(n);
1989 74 : }
1990 :
1991 : //------------------------------------------------
1992 : //
1993 : // Implementation
1994 : //
1995 : //------------------------------------------------
1996 :
1997 : void
1998 0 : parser::
1999 : start_impl(bool head_response)
2000 : {
2001 0 : BOOST_ASSERT(impl_);
2002 0 : impl_->start(head_response);
2003 0 : }
2004 :
2005 : detail::header const&
2006 316 : parser::
2007 : safe_get_header() const
2008 : {
2009 316 : BOOST_ASSERT(impl_);
2010 316 : return impl_->safe_get_header();
2011 : }
2012 :
2013 : detail::workspace&
2014 755 : parser::
2015 : ws() noexcept
2016 : {
2017 755 : BOOST_ASSERT(impl_);
2018 755 : return impl_->ws();
2019 : }
2020 :
2021 : bool
2022 755 : parser::
2023 : is_body_set() const noexcept
2024 : {
2025 755 : BOOST_ASSERT(impl_);
2026 755 : return impl_->is_body_set();
2027 : }
2028 :
2029 : void
2030 383 : parser::
2031 : set_body_impl(
2032 : buffers::any_dynamic_buffer& eb) noexcept
2033 : {
2034 383 : BOOST_ASSERT(impl_);
2035 383 : impl_->set_body(eb);
2036 383 : }
2037 :
2038 : void
2039 372 : parser::
2040 : set_body_impl(sink& s) noexcept
2041 : {
2042 372 : BOOST_ASSERT(impl_);
2043 372 : impl_->set_body(s);
2044 372 : }
2045 :
2046 : } // http_proto
2047 : } // boost
|