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