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