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