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