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/context.hpp>
12 : #include <boost/http_proto/detail/except.hpp>
13 : #include <boost/http_proto/error.hpp>
14 : #include <boost/http_proto/parser.hpp>
15 : #include <boost/http_proto/rfc/detail/rules.hpp>
16 : #include <boost/http_proto/service/zlib_service.hpp>
17 :
18 : #include <boost/assert.hpp>
19 : #include <boost/buffers/algorithm.hpp>
20 : #include <boost/buffers/buffer_copy.hpp>
21 : #include <boost/buffers/buffer_size.hpp>
22 : #include <boost/buffers/make_buffer.hpp>
23 : #include <boost/url/grammar/ci_string.hpp>
24 : #include <boost/url/grammar/hexdig_chars.hpp>
25 :
26 : #include "detail/filter.hpp"
27 :
28 : namespace boost {
29 : namespace http_proto {
30 :
31 : /*
32 : Principles for fixed-size buffer design
33 :
34 : axiom 1:
35 : To read data you must have a buffer.
36 :
37 : axiom 2:
38 : The size of the HTTP header is not
39 : known in advance.
40 :
41 : conclusion 3:
42 : A single I/O can produce a complete
43 : HTTP header and additional payload
44 : data.
45 :
46 : conclusion 4:
47 : A single I/O can produce multiple
48 : complete HTTP headers, complete
49 : payloads, and a partial header or
50 : payload.
51 :
52 : axiom 5:
53 : A process is in one of two states:
54 : 1. at or below capacity
55 : 2. above capacity
56 :
57 : axiom 6:
58 : A program which can allocate an
59 : unbounded number of resources can
60 : go above capacity.
61 :
62 : conclusion 7:
63 : A program can guarantee never going
64 : above capacity if all resources are
65 : provisioned at program startup.
66 :
67 : corollary 8:
68 : `parser` and `serializer` should each
69 : allocate a single buffer of calculated
70 : size, and never resize it.
71 :
72 : axiom #:
73 : A parser and a serializer are always
74 : used in pairs.
75 :
76 : Buffer Usage
77 :
78 : | | begin
79 : | H | p | | f | read headers
80 : | H | p | | T | f | set T body
81 : | H | p | | C | T | f | make codec C
82 : | H | p | b | C | T | f | decode p into b
83 : | H | p | b | C | T | f | read/parse loop
84 : | H | | T | f | destroy codec
85 : | H | | T | f | finished
86 :
87 : H headers
88 : C codec
89 : T body
90 : f table
91 : p partial payload
92 : b body data
93 :
94 : "payload" is the bytes coming in from
95 : the stream.
96 :
97 : "body" is the logical body, after transfer
98 : encoding is removed. This can be the
99 : same as the payload.
100 :
101 : A "plain payload" is when the payload and
102 : body are identical (no transfer encodings).
103 :
104 : A "buffered payload" is any payload which is
105 : not plain. A second buffer is required
106 : for reading.
107 :
108 : "overread" is additional data received past
109 : the end of the headers when reading headers,
110 : or additional data received past the end of
111 : the message payload.
112 : */
113 :
114 : namespace {
115 : class inflator_filter
116 : : public http_proto::detail::filter
117 : {
118 : zlib::stream& inflator_;
119 :
120 : public:
121 73 : inflator_filter(
122 : context& ctx,
123 : http_proto::detail::workspace& ws,
124 : bool use_gzip)
125 292 : : inflator_{ ctx.get_service<zlib::service>()
126 73 : .make_inflator(ws, use_gzip ? 31 : 15) }
127 : {
128 73 : }
129 :
130 : virtual filter::results
131 119613 : on_process(
132 : buffers::mutable_buffer out,
133 : buffers::const_buffer in,
134 : bool more) override
135 : {
136 119613 : auto flush =
137 119613 : more ? zlib::flush::none : zlib::flush::finish;
138 119613 : filter::results results;
139 :
140 : for(;;)
141 : {
142 119613 : auto params = zlib::params{in.data(), in.size(),
143 119613 : out.data(), out.size() };
144 119613 : auto ec = inflator_.write(params, flush);
145 :
146 119613 : results.in_bytes += in.size() - params.avail_in;
147 119613 : results.out_bytes += out.size() - params.avail_out;
148 :
149 178637 : if( ec.failed() &&
150 178637 : ec != zlib::error::buf_err )
151 : {
152 1 : results.ec = ec;
153 119613 : return results;
154 : }
155 :
156 119612 : if( ec == zlib::error::stream_end )
157 : {
158 120 : results.finished = true;
159 120 : return results;
160 : }
161 :
162 119492 : in = buffers::suffix(in, params.avail_in);
163 119492 : out = buffers::suffix(out, params.avail_out);
164 :
165 119492 : if( in.size() == 0 || out.size() == 0 )
166 119492 : return results;
167 0 : }
168 : }
169 : };
170 :
171 : class chained_sequence
172 : {
173 : char const* pos_;
174 : char const* end_;
175 : char const* begin_b_;
176 : char const* end_b_;
177 :
178 : public:
179 125355 : chained_sequence(buffers::const_buffer_pair const& cbp)
180 125355 : : pos_(static_cast<char const*>(cbp[0].data()))
181 125355 : , end_(pos_ + cbp[0].size())
182 125355 : , begin_b_(static_cast<char const*>(cbp[1].data()))
183 125355 : , end_b_(begin_b_ + cbp[1].size())
184 : {
185 125355 : }
186 :
187 : char const*
188 650739 : next() noexcept
189 : {
190 650739 : ++pos_;
191 : // most frequently taken branch
192 650739 : if(pos_ < end_)
193 629440 : return pos_;
194 :
195 : // swap with the second range
196 21299 : if(begin_b_ != end_b_)
197 : {
198 58 : pos_ = begin_b_;
199 58 : end_ = end_b_;
200 58 : begin_b_ = end_b_;
201 58 : return pos_;
202 : }
203 :
204 : // undo the increament
205 21241 : pos_ = end_;
206 21241 : return nullptr;
207 : }
208 :
209 : bool
210 431711 : empty() const noexcept
211 : {
212 431711 : return pos_ == end_;
213 : }
214 :
215 : char
216 636159 : value() const noexcept
217 : {
218 636159 : return *pos_;
219 : }
220 :
221 : std::size_t
222 446563 : size() const noexcept
223 : {
224 446563 : return (end_ - pos_) + (end_b_ - begin_b_);
225 : }
226 : };
227 :
228 : std::uint64_t
229 121166 : parse_hex(
230 : chained_sequence& cs,
231 : system::error_code& ec) noexcept
232 : {
233 121166 : std::uint64_t v = 0;
234 121166 : std::size_t init_size = cs.size();
235 318912 : while(!cs.empty())
236 : {
237 299875 : auto n = grammar::hexdig_value(cs.value());
238 299875 : if(n < 0)
239 : {
240 102128 : if(init_size == cs.size())
241 : {
242 2 : ec = BOOST_HTTP_PROTO_ERR(
243 : error::bad_payload);
244 1 : return 0;
245 : }
246 102127 : return v;
247 : }
248 :
249 : // at least 4 significant bits are free
250 197747 : if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
251 : {
252 2 : ec = BOOST_HTTP_PROTO_ERR(
253 : error::bad_payload);
254 1 : return 0;
255 : }
256 :
257 197746 : v = (v << 4) | static_cast<std::uint64_t>(n);
258 197746 : cs.next();
259 : }
260 38074 : ec = BOOST_HTTP_PROTO_ERR(
261 : error::need_data);
262 19037 : return 0;
263 : }
264 :
265 : void
266 102421 : find_eol(
267 : chained_sequence& cs,
268 : system::error_code& ec) noexcept
269 : {
270 108355 : while(!cs.empty())
271 : {
272 108311 : if(cs.value() == '\r')
273 : {
274 102377 : if(!cs.next())
275 5 : break;
276 102372 : if(cs.value() != '\n')
277 : {
278 4 : ec = BOOST_HTTP_PROTO_ERR(
279 : error::bad_payload);
280 2 : return;
281 : }
282 102370 : cs.next();
283 102370 : return;
284 : }
285 5934 : cs.next();
286 : }
287 98 : ec = BOOST_HTTP_PROTO_ERR(
288 : error::need_data);
289 : }
290 :
291 : void
292 117014 : parse_eol(
293 : chained_sequence& cs,
294 : system::error_code& ec) noexcept
295 : {
296 117014 : if(cs.size() >= 2)
297 : {
298 : // we are sure size is at least 2
299 117012 : if(cs.value() == '\r' && *cs.next() == '\n')
300 : {
301 117009 : cs.next();
302 117009 : return;
303 : }
304 6 : ec = BOOST_HTTP_PROTO_ERR(
305 : error::bad_payload);
306 3 : return;
307 : }
308 4 : ec = BOOST_HTTP_PROTO_ERR(
309 : error::need_data);
310 : }
311 :
312 : void
313 4184 : skip_trailer_headers(
314 : chained_sequence& cs,
315 : system::error_code& ec) noexcept
316 : {
317 4444 : while(!cs.empty())
318 : {
319 4442 : if(cs.value() == '\r')
320 : {
321 4148 : if(!cs.next())
322 1 : break;
323 4147 : if(cs.value() != '\n')
324 : {
325 4 : ec = BOOST_HTTP_PROTO_ERR(
326 : error::bad_payload);
327 2 : return;
328 : }
329 4145 : cs.next();
330 4145 : return;
331 : }
332 : // skip to the end of field
333 294 : find_eol(cs, ec);
334 294 : if(ec)
335 34 : return;
336 : }
337 6 : ec = BOOST_HTTP_PROTO_ERR(
338 : error::need_data);
339 : }
340 :
341 : template<class UInt>
342 : std::size_t
343 378049 : clamp(
344 : UInt x,
345 : std::size_t limit = (std::numeric_limits<
346 : std::size_t>::max)()) noexcept
347 : {
348 378049 : if(x >= limit)
349 105787 : return limit;
350 272262 : return static_cast<std::size_t>(x);
351 : }
352 : } // namespace
353 :
354 : class parser_service
355 : : public service
356 : {
357 : public:
358 : parser::config_base cfg;
359 : std::size_t space_needed = 0;
360 : std::size_t max_codec = 0;
361 : zlib::service const* zlib_svc = nullptr;
362 :
363 : parser_service(
364 : context& ctx,
365 : parser::config_base const& cfg_);
366 :
367 : std::size_t
368 56352 : max_overread() const noexcept
369 : {
370 : return
371 56352 : cfg.headers.max_size +
372 56352 : cfg.min_buffer;
373 : }
374 : };
375 :
376 38 : parser_service::
377 : parser_service(
378 : context& ctx,
379 38 : parser::config_base const& cfg_)
380 38 : : cfg(cfg_)
381 : {
382 : /*
383 : | fb | cb0 | cb1 | C | T | f |
384 :
385 : fb flat_buffer headers.max_size
386 : cb0 circular_buffer min_buffer
387 : cb1 circular_buffer min_buffer
388 : C codec max_codec
389 : T body max_type_erase
390 : f table max_table_space
391 :
392 : */
393 : // validate
394 : //if(cfg.min_prepare > cfg.max_prepare)
395 : //detail::throw_invalid_argument();
396 :
397 38 : if(cfg.max_prepare < 1)
398 0 : detail::throw_invalid_argument();
399 :
400 : // VFALCO TODO OVERFLOW CHECING
401 : {
402 : //fb_.size() - h_.size +
403 : //svc_.cfg.min_buffer +
404 : //svc_.cfg.min_buffer +
405 : //svc_.max_codec;
406 : }
407 :
408 : // VFALCO OVERFLOW CHECKING ON THIS
409 38 : space_needed +=
410 38 : cfg.headers.valid_space_needed();
411 :
412 : // cb0_, cb1_
413 : // VFALCO OVERFLOW CHECKING ON THIS
414 38 : space_needed +=
415 38 : cfg.min_buffer +
416 : cfg.min_buffer;
417 :
418 : // T
419 38 : space_needed += cfg.max_type_erase;
420 :
421 : // max_codec
422 : {
423 38 : if(cfg.apply_deflate_decoder)
424 : {
425 : auto const n = ctx.get_service<
426 3 : zlib::service>().inflator_space_needed(15);
427 3 : if( max_codec < n)
428 3 : max_codec = n;
429 : }
430 : }
431 38 : space_needed += max_codec;
432 :
433 : // round up to alignof(detail::header::entry)
434 38 : auto const al = alignof(
435 : detail::header::entry);
436 38 : space_needed = al * ((
437 38 : space_needed + al - 1) / al);
438 38 : }
439 :
440 : void
441 38 : install_parser_service(
442 : context& ctx,
443 : parser::config_base const& cfg)
444 : {
445 : ctx.make_service<
446 38 : parser_service>(cfg);
447 38 : }
448 :
449 : //------------------------------------------------
450 : //
451 : // Special Members
452 : //
453 : //------------------------------------------------
454 :
455 1050 : parser::
456 : parser(
457 : context& ctx,
458 1050 : detail::kind k)
459 1050 : : ctx_(ctx)
460 1050 : , svc_(ctx.get_service<
461 1050 : parser_service>())
462 1050 : , h_(detail::empty{k})
463 1050 : , eb_(nullptr)
464 2100 : , st_(state::reset)
465 : {
466 1050 : auto const n =
467 1050 : svc_.space_needed;
468 1050 : ws_.allocate(n);
469 1050 : h_.cap = n;
470 1050 : }
471 :
472 : //------------------------------------------------
473 :
474 1050 : parser::
475 : ~parser()
476 : {
477 1050 : }
478 :
479 : //------------------------------------------------
480 : //
481 : // Modifiers
482 : //
483 : //------------------------------------------------
484 :
485 : // prepare for a new stream
486 : void
487 1744 : parser::
488 : reset() noexcept
489 : {
490 1744 : ws_.clear();
491 1744 : eb_ = nullptr;
492 1744 : st_ = state::start;
493 1744 : got_eof_ = false;
494 1744 : }
495 :
496 : void
497 10303 : parser::
498 : start_impl(
499 : bool head_response)
500 : {
501 10303 : std::size_t leftover = 0;
502 10303 : switch(st_)
503 : {
504 1 : default:
505 : case state::reset:
506 : // reset must be called first
507 1 : detail::throw_logic_error();
508 :
509 1728 : case state::start:
510 : // reset required on eof
511 1728 : if(got_eof_)
512 0 : detail::throw_logic_error();
513 1728 : break;
514 :
515 3 : case state::header:
516 3 : if(fb_.size() == 0)
517 : {
518 : // start() called twice
519 2 : detail::throw_logic_error();
520 : }
521 : BOOST_FALLTHROUGH;
522 :
523 : case state::body:
524 : case state::set_body:
525 : // current message is incomplete
526 2 : detail::throw_logic_error();
527 :
528 8189 : case state::complete_in_place:
529 : // remove available body.
530 8189 : if(is_plain())
531 4174 : cb0_.consume(body_avail_);
532 : BOOST_FALLTHROUGH;
533 :
534 : case state::complete:
535 : {
536 : // move leftovers to front
537 :
538 8570 : ws_.clear();
539 8570 : leftover = cb0_.size();
540 :
541 8570 : auto* dest = reinterpret_cast<char*>(ws_.data());
542 8570 : auto cbp = cb0_.data();
543 8570 : auto* a = static_cast<char const*>(cbp[0].data());
544 8570 : auto* b = static_cast<char const*>(cbp[1].data());
545 8570 : auto an = cbp[0].size();
546 8570 : auto bn = cbp[1].size();
547 :
548 8570 : if(bn == 0)
549 : {
550 8132 : std::memmove(dest, a, an);
551 : }
552 : else
553 : {
554 : // if `a` can fit between `dest` and `b`, shift `b` to the left
555 : // and copy `a` to its position. if `a` fits perfectly, the
556 : // shift will be of size 0.
557 : // if `a` requires more space, shift `b` to the right and
558 : // copy `a` to its position. this process may require multiple
559 : // iterations and should be done chunk by chunk to prevent `b`
560 : // from overlapping with `a`.
561 : do
562 : {
563 : // clamp right shifts to prevent overlap with `a`
564 438 : auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
565 438 : b = static_cast<char const*>(std::memmove(bp, b, bn));
566 :
567 : // a chunk or all of `a` based on available space
568 438 : auto chunk_a = static_cast<std::size_t>(b - dest);
569 438 : std::memcpy(dest, a, chunk_a); // never overlap
570 438 : an -= chunk_a;
571 438 : dest += chunk_a;
572 438 : a += chunk_a;
573 438 : } while(an);
574 : }
575 :
576 8570 : break;
577 : }
578 : }
579 :
580 10298 : ws_.clear();
581 :
582 20596 : fb_ = {
583 10298 : ws_.data(),
584 10298 : svc_.cfg.headers.max_size + svc_.cfg.min_buffer,
585 : leftover };
586 :
587 10298 : BOOST_ASSERT(
588 : fb_.capacity() == svc_.max_overread() - leftover);
589 :
590 10298 : h_ = detail::header(detail::empty{h_.kind});
591 10298 : h_.buf = reinterpret_cast<char*>(ws_.data());
592 10298 : h_.cbuf = h_.buf;
593 10298 : h_.cap = ws_.size();
594 :
595 10298 : BOOST_ASSERT(! head_response ||
596 : h_.kind == detail::kind::response);
597 10298 : head_response_ = head_response;
598 :
599 : // begin with in_place mode
600 10298 : how_ = how::in_place;
601 10298 : st_ = state::header;
602 10298 : nprepare_ = 0;
603 10298 : chunk_remain_ = 0;
604 10298 : needs_chunk_close_ = false;
605 10298 : trailer_headers_ = false;
606 10298 : chunked_body_ended = false;
607 10298 : filter_ = nullptr;
608 10298 : sink_ = nullptr;
609 10298 : body_avail_ = 0;
610 10298 : body_total_ = 0;
611 10298 : }
612 :
613 : auto
614 51209 : parser::
615 : prepare() ->
616 : mutable_buffers_type
617 : {
618 51209 : nprepare_ = 0;
619 :
620 51209 : switch(st_)
621 : {
622 1 : default:
623 : case state::reset:
624 : // reset must be called first
625 1 : detail::throw_logic_error();
626 :
627 1 : case state::start:
628 : // start must be called first
629 1 : detail::throw_logic_error();
630 :
631 10378 : case state::header:
632 : {
633 10378 : BOOST_ASSERT(
634 : h_.size < svc_.cfg.headers.max_size);
635 10378 : std::size_t n = fb_.capacity() - fb_.size();
636 10378 : BOOST_ASSERT(n <= svc_.max_overread());
637 10378 : n = clamp(n, svc_.cfg.max_prepare);
638 10378 : mbp_[0] = fb_.prepare(n);
639 10378 : nprepare_ = n;
640 10378 : return mutable_buffers_type(&mbp_[0], 1);
641 : }
642 :
643 40828 : case state::body:
644 : {
645 40828 : if(got_eof_)
646 : {
647 : // forgot to call parse()
648 0 : detail::throw_logic_error();
649 : }
650 :
651 40828 : if(! is_plain())
652 : {
653 : // buffered payload
654 21778 : std::size_t n = cb0_.capacity();
655 21778 : n = clamp(n, svc_.cfg.max_prepare);
656 21778 : nprepare_ = n;
657 21778 : mbp_ = cb0_.prepare(n);
658 21778 : return mutable_buffers_type(mbp_);
659 : }
660 : else
661 : {
662 19050 : switch(how_)
663 : {
664 19029 : default:
665 : case how::in_place:
666 : case how::sink:
667 : {
668 19029 : std::size_t n = cb0_.capacity();
669 19029 : n = clamp(n, svc_.cfg.max_prepare);
670 :
671 19029 : if(h_.md.payload == payload::size)
672 : {
673 19004 : if(n > payload_remain_)
674 : {
675 17797 : std::size_t overread =
676 17797 : n - static_cast<std::size_t>(payload_remain_);
677 17797 : if(overread > svc_.max_overread())
678 7877 : n = static_cast<std::size_t>(payload_remain_) +
679 7877 : svc_.max_overread();
680 : }
681 : }
682 : else
683 : {
684 25 : BOOST_ASSERT(
685 : h_.md.payload == payload::to_eof);
686 :
687 25 : n = clamp(
688 25 : svc_.cfg.body_limit - body_total_ + 1, n);
689 : }
690 :
691 19029 : nprepare_ = n;
692 19029 : mbp_ = cb0_.prepare(n);
693 19029 : return mutable_buffers_type(mbp_);
694 : }
695 21 : case how::elastic:
696 : {
697 21 : BOOST_ASSERT(cb0_.size() == 0);
698 21 : BOOST_ASSERT(body_avail_ == 0);
699 :
700 21 : std::size_t n = svc_.cfg.min_buffer;
701 :
702 21 : if(h_.md.payload == payload::size)
703 : {
704 : // Overreads are not allowed, or
705 : // else the caller will see extra
706 : // unrelated data.
707 6 : n = clamp(payload_remain_, n);
708 : }
709 : else
710 : {
711 15 : BOOST_ASSERT(
712 : h_.md.payload == payload::to_eof);
713 15 : n = clamp(
714 15 : svc_.cfg.body_limit - body_total_, n);
715 15 : n = clamp(
716 15 : n, eb_->max_size() - eb_->size());
717 : // fill capacity first to avoid an allocation
718 : std::size_t avail =
719 15 : eb_->capacity() - eb_->size();
720 15 : if(avail != 0)
721 15 : n = clamp(n, avail);
722 :
723 15 : if(n == 0)
724 : {
725 : // dynamic buffer is full
726 : // attempt a 1 byte read so
727 : // we can detect overflow
728 1 : nprepare_ = 1;
729 1 : mbp_ = cb0_.prepare(1);
730 1 : return mutable_buffers_type(mbp_);
731 : }
732 : }
733 :
734 20 : n = clamp(n, svc_.cfg.max_prepare);
735 20 : BOOST_ASSERT(n != 0);
736 20 : nprepare_ = n;
737 20 : return eb_->prepare(n);
738 : }
739 : }
740 : }
741 : }
742 :
743 0 : case state::set_body:
744 : // forgot to call parse()
745 0 : detail::throw_logic_error();
746 :
747 1 : case state::complete_in_place:
748 : case state::complete:
749 : // already complete
750 1 : detail::throw_logic_error();
751 : }
752 : }
753 :
754 : void
755 51206 : parser::
756 : commit(
757 : std::size_t n)
758 : {
759 51206 : if(n > nprepare_)
760 : {
761 : // n can't be greater than size of
762 : // the buffers returned by prepare()
763 4 : detail::throw_invalid_argument();
764 : }
765 :
766 51202 : if(got_eof_)
767 : {
768 : // can't commit after EOF
769 1 : detail::throw_logic_error();
770 : }
771 :
772 51201 : switch(st_)
773 : {
774 1 : default:
775 : case state::reset:
776 : {
777 : // reset must be called first
778 1 : detail::throw_logic_error();
779 : }
780 :
781 1 : case state::start:
782 : {
783 : // forgot to call start()
784 1 : detail::throw_logic_error();
785 : }
786 :
787 10376 : case state::header:
788 : {
789 10376 : nprepare_ = 0; // invalidate
790 10376 : fb_.commit(n);
791 10376 : break;
792 : }
793 :
794 40823 : case state::body:
795 : {
796 40823 : nprepare_ = 0; // invalidate
797 40823 : if(is_plain() && how_ == how::elastic)
798 : {
799 20 : if(eb_->max_size() == eb_->size())
800 : {
801 : // borrowed 1 byte from cb0_
802 1 : BOOST_ASSERT(n <= 1);
803 1 : cb0_.commit(n);
804 1 : break;
805 : }
806 19 : eb_->commit(n);
807 19 : payload_remain_ -= n;
808 19 : body_total_ += n;
809 : }
810 : else
811 : {
812 40803 : cb0_.commit(n);
813 : }
814 40822 : break;
815 : }
816 :
817 0 : case state::set_body:
818 : {
819 : // forgot to call parse()
820 0 : detail::throw_logic_error();
821 : }
822 :
823 0 : case state::complete_in_place:
824 : case state::complete:
825 : {
826 : // already complete
827 0 : detail::throw_logic_error();
828 : }
829 : }
830 51199 : }
831 :
832 : void
833 402 : parser::
834 : commit_eof()
835 : {
836 402 : nprepare_ = 0; // invalidate
837 :
838 402 : switch(st_)
839 : {
840 1 : default:
841 : case state::reset:
842 : // reset must be called first
843 1 : detail::throw_logic_error();
844 :
845 1 : case state::start:
846 : // forgot to call prepare()
847 1 : detail::throw_logic_error();
848 :
849 31 : case state::header:
850 31 : got_eof_ = true;
851 31 : break;
852 :
853 368 : case state::body:
854 368 : got_eof_ = true;
855 368 : break;
856 :
857 0 : case state::set_body:
858 : // forgot to call parse()
859 0 : detail::throw_logic_error();
860 :
861 1 : case state::complete_in_place:
862 : case state::complete:
863 : // can't commit eof when complete
864 1 : detail::throw_logic_error();
865 : }
866 399 : }
867 :
868 : //-----------------------------------------------
869 :
870 : // process input data then
871 : // eof if input data runs out.
872 : void
873 57081 : parser::
874 : parse(
875 : system::error_code& ec)
876 : {
877 57081 : ec = {};
878 57081 : switch(st_)
879 : {
880 1 : default:
881 : case state::reset:
882 : // reset must be called first
883 1 : detail::throw_logic_error();
884 :
885 1 : case state::start:
886 : // start must be called first
887 1 : detail::throw_logic_error();
888 :
889 14393 : case state::header:
890 : {
891 14393 : BOOST_ASSERT(h_.buf == static_cast<
892 : void const*>(ws_.data()));
893 14393 : BOOST_ASSERT(h_.cbuf == static_cast<
894 : void const*>(ws_.data()));
895 :
896 14393 : h_.parse(fb_.size(), svc_.cfg.headers, ec);
897 :
898 14393 : if(ec == condition::need_more_input)
899 : {
900 4132 : if(! got_eof_)
901 : {
902 : // headers incomplete
903 4105 : return;
904 : }
905 :
906 27 : if(fb_.size() == 0)
907 : {
908 : // stream closed cleanly
909 12 : st_ = state::complete_in_place;
910 24 : ec = BOOST_HTTP_PROTO_ERR(
911 : error::end_of_stream);
912 12 : return;
913 : }
914 :
915 : // stream closed with a
916 : // partial message received
917 15 : st_ = state::reset;
918 30 : ec = BOOST_HTTP_PROTO_ERR(
919 : error::incomplete);
920 15 : return;
921 : }
922 10261 : if(ec.failed())
923 : {
924 : // other error,
925 : //
926 : // VFALCO map this to a bad
927 : // request or bad response error?
928 : //
929 259 : st_ = state::reset; // unrecoverable
930 259 : return;
931 : }
932 :
933 : // headers are complete
934 10002 : on_headers(ec);
935 10002 : if(ec.failed())
936 180 : return;
937 9822 : if(st_ == state::complete_in_place)
938 921 : break;
939 :
940 : BOOST_FALLTHROUGH;
941 : }
942 :
943 : case state::body:
944 : {
945 8901 : do_body:
946 50367 : BOOST_ASSERT(st_ == state::body);
947 50367 : BOOST_ASSERT(h_.md.payload != payload::none);
948 50367 : BOOST_ASSERT(h_.md.payload != payload::error);
949 :
950 8796 : auto set_state_to_complete = [&]()
951 : {
952 8796 : if(how_ == how::in_place)
953 : {
954 8558 : st_ = state::complete_in_place;
955 8558 : return;
956 : }
957 238 : st_ = state::complete;
958 50367 : };
959 :
960 50367 : if(h_.md.payload == payload::chunked)
961 : {
962 : for(;;)
963 : {
964 130820 : if(chunk_remain_ == 0
965 129629 : && !chunked_body_ended)
966 : {
967 125484 : if(cb0_.size() == 0)
968 : {
969 258 : ec = BOOST_HTTP_PROTO_ERR(
970 : error::need_data);
971 19229 : return;
972 : }
973 :
974 125355 : auto cs = chained_sequence(cb0_.data());
975 :
976 125355 : if(needs_chunk_close_)
977 : {
978 117014 : parse_eol(cs, ec);
979 117014 : if(ec)
980 5 : return;
981 : }
982 8341 : else if(trailer_headers_)
983 : {
984 4184 : skip_trailer_headers(cs, ec);
985 4184 : if(ec)
986 39 : return;
987 4145 : cb0_.consume(cb0_.size() - cs.size());
988 4145 : chunked_body_ended = true;
989 8292 : continue;
990 : }
991 :
992 121166 : auto chunk_size = parse_hex(cs, ec);
993 121166 : if(ec)
994 19039 : return;
995 :
996 : // skip chunk extensions
997 102127 : find_eol(cs, ec);
998 102127 : if(ec)
999 17 : return;
1000 :
1001 102110 : cb0_.consume(cb0_.size() - cs.size());
1002 102110 : chunk_remain_ = chunk_size;
1003 :
1004 102110 : needs_chunk_close_ = true;
1005 102110 : if(chunk_remain_ == 0)
1006 : {
1007 4147 : needs_chunk_close_ = false;
1008 4147 : trailer_headers_ = true;
1009 4147 : continue;
1010 : }
1011 : }
1012 :
1013 103299 : if(cb0_.size() == 0 && !chunked_body_ended)
1014 : {
1015 325 : if(got_eof_)
1016 : {
1017 0 : ec = BOOST_HTTP_PROTO_ERR(
1018 : error::incomplete);
1019 0 : st_ = state::reset;
1020 0 : return;
1021 : }
1022 :
1023 650 : ec = BOOST_HTTP_PROTO_ERR(
1024 : error::need_data);
1025 325 : return;
1026 : }
1027 :
1028 102974 : if(filter_)
1029 : {
1030 56592 : chunk_remain_ -= apply_filter(
1031 : ec,
1032 : clamp(chunk_remain_, cb0_.size()),
1033 56592 : !chunked_body_ended);
1034 :
1035 56592 : if(ec || chunked_body_ended)
1036 572 : return;
1037 : }
1038 : else
1039 : {
1040 : const std::size_t chunk_avail =
1041 46382 : clamp(chunk_remain_, cb0_.size());
1042 : const auto chunk =
1043 46382 : buffers::prefix(cb0_.data(), chunk_avail);
1044 :
1045 46382 : if(svc_.cfg.body_limit - body_total_
1046 : < chunk_avail)
1047 : {
1048 0 : ec = BOOST_HTTP_PROTO_ERR(
1049 : error::body_too_large);
1050 0 : st_ = state::reset;
1051 4121 : return;
1052 : }
1053 :
1054 46382 : switch(how_)
1055 : {
1056 46382 : case how::in_place:
1057 : {
1058 46382 : auto copied = buffers::buffer_copy(
1059 46382 : cb1_.prepare(cb1_.capacity()),
1060 : chunk);
1061 46382 : chunk_remain_ -= copied;
1062 46382 : body_avail_ += copied;
1063 46382 : body_total_ += copied;
1064 46382 : cb0_.consume(copied);
1065 46382 : cb1_.commit(copied);
1066 46382 : if(cb1_.capacity() == 0
1067 46382 : && !chunked_body_ended)
1068 : {
1069 0 : ec = BOOST_HTTP_PROTO_ERR(
1070 : error::in_place_overflow);
1071 0 : return;
1072 : }
1073 46382 : break;
1074 : }
1075 0 : case how::sink:
1076 : {
1077 0 : auto sink_rs = sink_->write(
1078 0 : chunk, !chunked_body_ended);
1079 0 : chunk_remain_ -= sink_rs.bytes;
1080 0 : body_total_ += sink_rs.bytes;
1081 0 : cb0_.consume(sink_rs.bytes);
1082 0 : if(sink_rs.ec.failed())
1083 : {
1084 0 : body_avail_ +=
1085 0 : chunk_avail - sink_rs.bytes;
1086 0 : ec = sink_rs.ec;
1087 0 : st_ = state::reset;
1088 0 : return;
1089 : }
1090 0 : break;
1091 : }
1092 0 : case how::elastic:
1093 : {
1094 0 : if(eb_->max_size() - eb_->size()
1095 0 : < chunk_avail)
1096 : {
1097 0 : ec = BOOST_HTTP_PROTO_ERR(
1098 : error::buffer_overflow);
1099 0 : st_ = state::reset;
1100 0 : return;
1101 : }
1102 0 : buffers::buffer_copy(
1103 0 : eb_->prepare(chunk_avail),
1104 : chunk);
1105 0 : chunk_remain_ -= chunk_avail;
1106 0 : body_total_ += chunk_avail;
1107 0 : cb0_.consume(chunk_avail);
1108 0 : eb_->commit(chunk_avail);
1109 0 : break;
1110 : }
1111 : }
1112 :
1113 46382 : if(chunked_body_ended)
1114 : {
1115 4121 : set_state_to_complete();
1116 4121 : return;
1117 : }
1118 : }
1119 106573 : }
1120 : }
1121 : else
1122 : {
1123 : // non-chunked payload
1124 :
1125 78360 : const std::size_t payload_avail = [&]()
1126 : {
1127 26120 : auto ret = cb0_.size();
1128 26120 : if(!filter_)
1129 24327 : ret -= body_avail_;
1130 26120 : if(h_.md.payload == payload::size)
1131 24241 : return clamp(payload_remain_, ret);
1132 : // payload::eof
1133 1879 : return ret;
1134 26120 : }();
1135 :
1136 78360 : const bool is_complete = [&]()
1137 : {
1138 26120 : if(h_.md.payload == payload::size)
1139 24241 : return payload_avail == payload_remain_;
1140 : // payload::eof
1141 1879 : return got_eof_;
1142 26120 : }();
1143 :
1144 26120 : if(filter_)
1145 : {
1146 3586 : payload_remain_ -= apply_filter(
1147 1793 : ec, payload_avail, !is_complete);
1148 1793 : if(ec || is_complete)
1149 1145 : return;
1150 : }
1151 : else
1152 : {
1153 24327 : switch(how_)
1154 : {
1155 23818 : case how::in_place:
1156 : {
1157 23818 : payload_remain_ -= payload_avail;
1158 23818 : body_avail_ += payload_avail;
1159 23818 : body_total_ += payload_avail;
1160 23818 : if(cb0_.capacity() == 0 && !is_complete)
1161 : {
1162 2 : ec = BOOST_HTTP_PROTO_ERR(
1163 : error::in_place_overflow);
1164 1 : return;
1165 : }
1166 23817 : break;
1167 : }
1168 249 : case how::sink:
1169 : {
1170 249 : payload_remain_ -= payload_avail;
1171 249 : body_total_ += payload_avail;
1172 249 : auto sink_rs = sink_->write(
1173 249 : buffers::prefix(
1174 0 : cb0_.data(),
1175 : payload_avail),
1176 249 : !is_complete);
1177 249 : cb0_.consume(sink_rs.bytes);
1178 249 : if(sink_rs.ec.failed())
1179 : {
1180 0 : body_avail_ +=
1181 0 : payload_avail - sink_rs.bytes;
1182 0 : ec = sink_rs.ec;
1183 0 : st_ = state::reset;
1184 0 : return;
1185 : }
1186 249 : break;
1187 : }
1188 260 : case how::elastic:
1189 : {
1190 : // payload_remain_ and body_total_
1191 : // are already updated in commit()
1192 :
1193 260 : if(cb0_.size() != 0)
1194 : {
1195 : // a non-empty cb0_ indicates that
1196 : // the elastic buffer ran out of space
1197 0 : ec = BOOST_HTTP_PROTO_ERR(
1198 : error::buffer_overflow);
1199 0 : st_ = state::reset;
1200 0 : return;
1201 : }
1202 260 : break;
1203 : }
1204 : }
1205 :
1206 24326 : if(body_total_ > svc_.cfg.body_limit)
1207 : {
1208 2 : ec = BOOST_HTTP_PROTO_ERR(
1209 : error::body_too_large);
1210 1 : st_ = state::reset;
1211 1 : return;
1212 : }
1213 :
1214 24325 : if(is_complete)
1215 : {
1216 4675 : set_state_to_complete();
1217 4675 : return;
1218 : }
1219 : }
1220 :
1221 20298 : if(h_.md.payload == payload::size && got_eof_)
1222 : {
1223 2 : ec = BOOST_HTTP_PROTO_ERR(
1224 : error::incomplete);
1225 1 : st_ = state::reset;
1226 1 : return;
1227 : }
1228 :
1229 40594 : ec = BOOST_HTTP_PROTO_ERR(
1230 : error::need_data);
1231 20297 : return;
1232 : }
1233 :
1234 : break;
1235 : }
1236 :
1237 922 : case state::set_body:
1238 : case state::complete_in_place:
1239 : {
1240 922 : auto& body_buf = is_plain() ? cb0_ : cb1_;
1241 :
1242 922 : BOOST_ASSERT(body_avail_ == body_buf.size());
1243 922 : BOOST_ASSERT(body_total_ == body_avail_);
1244 :
1245 922 : switch(how_)
1246 : {
1247 288 : case how::in_place:
1248 288 : return; // no-op
1249 312 : case how::sink:
1250 : {
1251 312 : auto rs = sink_->write(
1252 312 : buffers::prefix(
1253 0 : body_buf.data(),
1254 : body_avail_),
1255 312 : st_ == state::set_body);
1256 312 : body_buf.consume(rs.bytes);
1257 312 : body_avail_ -= rs.bytes;
1258 312 : if(rs.ec.failed())
1259 : {
1260 0 : ec = rs.ec;
1261 0 : st_ = state::reset;
1262 0 : return;
1263 : }
1264 312 : break;
1265 : }
1266 322 : case how::elastic:
1267 : {
1268 322 : if(eb_->max_size() - eb_->size()
1269 322 : < body_avail_)
1270 : {
1271 2 : ec = BOOST_HTTP_PROTO_ERR(
1272 : error::buffer_overflow);
1273 1 : return;
1274 : }
1275 321 : buffers::buffer_copy(
1276 321 : eb_->prepare(body_avail_),
1277 321 : body_buf.data());
1278 321 : body_buf.consume(body_avail_);
1279 321 : eb_->commit(body_avail_);
1280 321 : body_avail_ = 0;
1281 : // TODO: expand cb_0 when possible?
1282 321 : break;
1283 : }
1284 : }
1285 :
1286 633 : if(st_ == state::set_body)
1287 : {
1288 278 : st_ = state::body;
1289 278 : goto do_body;
1290 : }
1291 :
1292 355 : st_ = state::complete;
1293 355 : break;
1294 : }
1295 :
1296 576 : case state::complete:
1297 576 : break;
1298 : }
1299 : }
1300 :
1301 : //------------------------------------------------
1302 :
1303 : auto
1304 41250 : parser::
1305 : pull_body() ->
1306 : const_buffers_type
1307 : {
1308 41250 : switch(st_)
1309 : {
1310 41250 : case state::body:
1311 : case state::complete_in_place:
1312 41250 : cbp_ = buffers::prefix(
1313 41250 : (is_plain() ? cb0_ : cb1_).data(),
1314 : body_avail_);
1315 41250 : return const_buffers_type(cbp_);
1316 0 : default:
1317 0 : detail::throw_logic_error();
1318 : }
1319 : }
1320 :
1321 : void
1322 39606 : parser::
1323 : consume_body(std::size_t n)
1324 : {
1325 39606 : switch(st_)
1326 : {
1327 39606 : case state::body:
1328 : case state::complete_in_place:
1329 39606 : n = clamp(n, body_avail_);
1330 39606 : (is_plain() ? cb0_ : cb1_).consume(n);
1331 39606 : body_avail_ -= n;
1332 39606 : return;
1333 0 : default:
1334 0 : detail::throw_logic_error();
1335 : }
1336 : }
1337 :
1338 : core::string_view
1339 1968 : parser::
1340 : body() const noexcept
1341 : {
1342 1968 : if(st_ == state::complete_in_place)
1343 : {
1344 697 : auto cbp = (is_plain() ? cb0_ : cb1_).data();
1345 697 : BOOST_ASSERT(cbp[1].size() == 0);
1346 697 : BOOST_ASSERT(cbp[0].size() == body_avail_);
1347 697 : return core::string_view(
1348 697 : static_cast<char const*>(cbp[0].data()),
1349 1394 : body_avail_);
1350 : }
1351 1271 : return {};
1352 : }
1353 :
1354 : core::string_view
1355 0 : parser::
1356 : release_buffered_data() noexcept
1357 : {
1358 0 : return {};
1359 : }
1360 :
1361 : //------------------------------------------------
1362 : //
1363 : // Implementation
1364 : //
1365 : //------------------------------------------------
1366 :
1367 : auto
1368 314 : parser::
1369 : safe_get_header() const ->
1370 : detail::header const*
1371 : {
1372 : // headers must be received
1373 628 : if( ! got_header() ||
1374 314 : fb_.size() == 0) // happens on eof
1375 0 : detail::throw_logic_error();
1376 :
1377 314 : return &h_;
1378 : }
1379 :
1380 : bool
1381 181216 : parser::
1382 : is_plain() const noexcept
1383 : {
1384 352259 : return ! filter_ &&
1385 352259 : h_.md.payload != payload::chunked;
1386 : }
1387 :
1388 : // Called immediately after complete headers are received
1389 : // to setup the circular buffers for subsequent operations.
1390 : // We leave fb_ as-is to indicate whether any data was
1391 : // received before eof.
1392 : //
1393 : void
1394 10002 : parser::
1395 : on_headers(
1396 : system::error_code& ec)
1397 : {
1398 : // overread currently includes any and all octets that
1399 : // extend beyond the current end of the header
1400 : // this can include associated body octets for the
1401 : // current message or octets of the next message in the
1402 : // stream, e.g. pipelining is being used
1403 10002 : auto const overread = fb_.size() - h_.size;
1404 10002 : BOOST_ASSERT(
1405 : overread <= svc_.max_overread());
1406 :
1407 : // metadata error
1408 10002 : if(h_.md.payload == payload::error)
1409 : {
1410 : // VFALCO This needs looking at
1411 360 : ec = BOOST_HTTP_PROTO_ERR(
1412 : error::bad_payload);
1413 180 : st_ = state::reset; // unrecoverable
1414 180 : return;
1415 : }
1416 :
1417 : // reserve headers + table
1418 9822 : ws_.reserve_front(h_.size);
1419 9822 : ws_.reserve_back(h_.table_space());
1420 :
1421 : // no payload
1422 9822 : if( h_.md.payload == payload::none ||
1423 8901 : head_response_ )
1424 : {
1425 : // set cb0_ to overread
1426 921 : cb0_ = {
1427 921 : ws_.data(),
1428 921 : overread + fb_.capacity(),
1429 : overread };
1430 921 : st_ = state::complete_in_place;
1431 921 : return;
1432 : }
1433 :
1434 8901 : auto cap = fb_.capacity() + overread +
1435 8901 : svc_.cfg.min_buffer;
1436 :
1437 : // reserve body buffers first, as the decoder
1438 : // must be installed after them.
1439 8901 : auto const p = ws_.reserve_front(cap);
1440 :
1441 8901 : if( svc_.cfg.apply_deflate_decoder &&
1442 73 : h_.md.content_encoding.encoding == encoding::deflate )
1443 : {
1444 74 : filter_ = &ws_.emplace<inflator_filter>(
1445 37 : ctx_, ws_, false);
1446 : }
1447 8864 : else if( svc_.cfg.apply_gzip_decoder &&
1448 36 : h_.md.content_encoding.encoding == encoding::gzip )
1449 : {
1450 72 : filter_ = &ws_.emplace<inflator_filter>(
1451 36 : ctx_, ws_, true);
1452 : }
1453 : else
1454 : {
1455 8828 : cap += svc_.max_codec;
1456 8828 : ws_.reserve_front(svc_.max_codec);
1457 : }
1458 :
1459 8901 : if(h_.md.payload == payload::size )
1460 4372 : payload_remain_ = h_.md.payload_size;
1461 :
1462 8901 : if(is_plain())
1463 : {
1464 4696 : cb0_ = { p, cap, overread };
1465 4696 : if(h_.md.payload == payload::size)
1466 : {
1467 4348 : if(h_.md.payload_size >
1468 4348 : svc_.cfg.body_limit)
1469 : {
1470 0 : ec = BOOST_HTTP_PROTO_ERR(
1471 : error::body_too_large);
1472 0 : st_ = state::reset;
1473 0 : return;
1474 : }
1475 : }
1476 4696 : st_ = state::body;
1477 4696 : return;
1478 : }
1479 :
1480 : // buffered payload
1481 :
1482 8410 : const auto n0 = (overread > svc_.cfg.min_buffer)
1483 4205 : ? overread
1484 4169 : : svc_.cfg.min_buffer;
1485 4205 : const auto n1 = svc_.cfg.min_buffer;
1486 :
1487 4205 : cb0_ = { p , n0, overread };
1488 4205 : cb1_ = { p + n0 , n1 };
1489 4205 : st_ = state::body;
1490 : }
1491 :
1492 : // Called at the end of set_body
1493 : void
1494 635 : parser::
1495 : on_set_body()
1496 : {
1497 : // This function is called after all
1498 : // limit checking and calculation of
1499 : // chunked or filter.
1500 :
1501 635 : BOOST_ASSERT(got_header());
1502 :
1503 635 : nprepare_ = 0; // invalidate
1504 :
1505 635 : if(st_ == state::body)
1506 : {
1507 278 : st_ = state::set_body;
1508 278 : return;
1509 : }
1510 :
1511 357 : BOOST_ASSERT(
1512 : st_ == state::complete_in_place);
1513 : }
1514 :
1515 : std::size_t
1516 58385 : parser::
1517 : apply_filter(
1518 : system::error_code& ec,
1519 : std::size_t payload_avail,
1520 : bool more)
1521 : {
1522 58385 : std::size_t p0 = payload_avail;
1523 :
1524 : for(;;)
1525 : {
1526 116790 : if(payload_avail == 0 && more)
1527 58385 : return p0 - payload_avail;
1528 :
1529 0 : auto f_rs = [&](){
1530 60170 : if(how_ == how::elastic)
1531 : {
1532 19806 : std::size_t n = clamp(
1533 19806 : svc_.cfg.body_limit - body_total_);
1534 19806 : n = clamp(n, svc_.cfg.min_buffer);
1535 19806 : n = clamp(n, eb_->max_size() - eb_->size());
1536 :
1537 : // fill capacity first to avoid
1538 : // an allocation
1539 : std::size_t avail =
1540 19806 : eb_->capacity() - eb_->size();
1541 19806 : if(avail != 0)
1542 19801 : n = clamp(n, avail);
1543 :
1544 19806 : return filter_->process(
1545 19806 : eb_->prepare(n),
1546 19806 : buffers::prefix(
1547 19806 : cb0_.data(),
1548 : payload_avail),
1549 39612 : more);
1550 : }
1551 : else // in-place and sink
1552 : {
1553 40364 : std::size_t n = clamp(
1554 40364 : svc_.cfg.body_limit - body_total_);
1555 40364 : n = clamp(n, cb1_.capacity());
1556 :
1557 40364 : return filter_->process(
1558 40364 : cb1_.prepare(n),
1559 40364 : buffers::prefix(
1560 40364 : cb0_.data(),
1561 : payload_avail),
1562 80728 : more);
1563 : }
1564 60170 : }();
1565 :
1566 60170 : cb0_.consume(f_rs.in_bytes);
1567 60170 : payload_avail -= f_rs.in_bytes;
1568 60170 : body_total_ += f_rs.out_bytes;
1569 :
1570 120220 : bool needs_more_space = !f_rs.finished &&
1571 60050 : payload_avail != 0;
1572 :
1573 60170 : switch(how_)
1574 : {
1575 20567 : case how::in_place:
1576 : {
1577 20567 : cb1_.commit(f_rs.out_bytes);
1578 20567 : body_avail_ += f_rs.out_bytes;
1579 20567 : if( cb1_.capacity() == 0 &&
1580 : needs_more_space)
1581 : {
1582 3288 : ec = BOOST_HTTP_PROTO_ERR(
1583 : error::in_place_overflow);
1584 1644 : return p0 - payload_avail;
1585 : }
1586 18923 : break;
1587 : }
1588 19797 : case how::sink:
1589 : {
1590 19797 : cb1_.commit(f_rs.out_bytes);
1591 19797 : auto sink_rs = sink_->write(
1592 19797 : cb1_.data(), !f_rs.finished);
1593 19797 : cb1_.consume(sink_rs.bytes);
1594 19797 : if(sink_rs.ec.failed())
1595 : {
1596 0 : ec = sink_rs.ec;
1597 0 : st_ = state::reset;
1598 0 : return p0 - payload_avail;
1599 : }
1600 19797 : break;
1601 : }
1602 19806 : case how::elastic:
1603 : {
1604 19806 : eb_->commit(f_rs.out_bytes);
1605 19806 : if( eb_->max_size() - eb_->size() == 0 &&
1606 : needs_more_space)
1607 : {
1608 0 : ec = BOOST_HTTP_PROTO_ERR(
1609 : error::buffer_overflow);
1610 0 : st_ = state::reset;
1611 0 : return p0 - payload_avail;
1612 : }
1613 19806 : break;
1614 : }
1615 : }
1616 :
1617 58526 : if(f_rs.ec.failed())
1618 : {
1619 1 : ec = f_rs.ec;
1620 1 : st_ = state::reset;
1621 1 : return p0 - payload_avail;
1622 : }
1623 :
1624 58525 : if( svc_.cfg.body_limit == body_total_ &&
1625 : needs_more_space)
1626 : {
1627 0 : ec = BOOST_HTTP_PROTO_ERR(
1628 : error::body_too_large);
1629 0 : st_ = state::reset;
1630 0 : return p0 - payload_avail;
1631 : }
1632 :
1633 58525 : if(f_rs.finished)
1634 : {
1635 120 : if(!more)
1636 : {
1637 72 : st_ = (how_ == how::in_place)
1638 72 : ? state::complete_in_place
1639 : : state::complete;
1640 : }
1641 :
1642 120 : return p0 - payload_avail;
1643 : }
1644 58405 : }
1645 : }
1646 :
1647 : } // http_proto
1648 : } // boost
|