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