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