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 : // possible aliasing violation here if these two
611 : // ranges overlap
612 : // need to examine if this is problematic in
613 : // practice or not
614 : //
615 : //
616 : // move unused octets to front
617 0 : buffers::buffer_copy(
618 0 : buffers::mutable_buffer(
619 0 : ws_.data(),
620 : leftover),
621 0 : fb_.data());
622 : }
623 : else
624 : {
625 : // leftover data after body
626 : }
627 240 : break;
628 : }
629 : }
630 :
631 1825 : ws_.clear();
632 :
633 3650 : fb_ = {
634 1825 : ws_.data(),
635 1825 : svc_.cfg.headers.max_size +
636 1825 : svc_.cfg.min_buffer,
637 : leftover };
638 1825 : BOOST_ASSERT(fb_.capacity() ==
639 : svc_.max_overread());
640 :
641 3650 : h_ = detail::header(
642 1825 : detail::empty{h_.kind});
643 1825 : h_.buf = reinterpret_cast<
644 1825 : char*>(ws_.data());
645 1825 : h_.cbuf = h_.buf;
646 1825 : h_.cap = ws_.size();
647 :
648 1825 : BOOST_ASSERT(! head_response ||
649 : h_.kind == detail::kind::response);
650 1825 : head_response_ = head_response;
651 :
652 : // begin with in_place mode
653 1825 : how_ = how::in_place;
654 1825 : st_ = state::header;
655 1825 : nprepare_ = 0;
656 1825 : chunk_remain_ = 0;
657 1825 : needs_chunk_close_ = false;
658 1825 : body_avail_ = 0;
659 1825 : }
660 :
661 : auto
662 5802 : parser::
663 : prepare() ->
664 : mutable_buffers_type
665 : {
666 5802 : nprepare_ = 0;
667 :
668 5802 : switch(st_)
669 : {
670 1 : default:
671 : case state::reset:
672 : // reset must be called first
673 1 : detail::throw_logic_error();
674 :
675 1 : case state::start:
676 : // start must be called first
677 1 : detail::throw_logic_error();
678 :
679 5589 : case state::header:
680 : {
681 5589 : BOOST_ASSERT(h_.size <
682 : svc_.cfg.headers.max_size);
683 5589 : auto n = fb_.capacity() - fb_.size();
684 5589 : BOOST_ASSERT(n <= svc_.max_overread());
685 5589 : if( n > svc_.cfg.max_prepare)
686 29 : n = svc_.cfg.max_prepare;
687 5589 : mbp_[0] = fb_.prepare(n);
688 5589 : nprepare_ = n;
689 5589 : return mutable_buffers_type(
690 11178 : &mbp_[0], 1);
691 : }
692 :
693 181 : case state::body:
694 : {
695 181 : if(got_eof_)
696 0 : return mutable_buffers_type{};
697 :
698 181 : do_body:
699 205 : if(! is_plain())
700 : {
701 : // buffered payload
702 149 : auto n = cb0_.capacity() -
703 149 : cb0_.size();
704 149 : if( n > svc_.cfg.max_prepare)
705 0 : n = svc_.cfg.max_prepare;
706 149 : mbp_ = cb0_.prepare(n);
707 149 : nprepare_ = n;
708 149 : return mutable_buffers_type(mbp_);
709 : }
710 :
711 : // plain payload
712 :
713 56 : if(how_ == how::in_place)
714 : {
715 : auto n =
716 29 : body_buf_->capacity() -
717 29 : body_buf_->size();
718 29 : if( n > svc_.cfg.max_prepare)
719 1 : n = svc_.cfg.max_prepare;
720 29 : mbp_ = body_buf_->prepare(n);
721 29 : nprepare_ = n;
722 29 : return mutable_buffers_type(mbp_);
723 : }
724 :
725 27 : if(how_ == how::elastic)
726 : {
727 : // Overreads are not allowed, or
728 : // else the caller will see extra
729 : // unrelated data.
730 :
731 27 : if(h_.md.payload == payload::size)
732 : {
733 : // set_body moves avail to dyn
734 9 : BOOST_ASSERT(body_buf_->size() == 0);
735 9 : BOOST_ASSERT(body_avail_ == 0);
736 9 : auto n = static_cast<std::size_t>(payload_remain_);
737 9 : if( n > svc_.cfg.max_prepare)
738 1 : n = svc_.cfg.max_prepare;
739 9 : nprepare_ = n;
740 9 : return eb_->prepare(n);
741 : }
742 :
743 18 : BOOST_ASSERT(
744 : h_.md.payload == payload::to_eof);
745 18 : std::size_t n = 0;
746 18 : if(! got_eof_)
747 : {
748 : // calculate n heuristically
749 18 : n = svc_.cfg.min_buffer;
750 18 : if( n > svc_.cfg.max_prepare)
751 1 : n = svc_.cfg.max_prepare;
752 : {
753 : // apply max_size()
754 : auto avail =
755 18 : eb_->max_size() -
756 18 : eb_->size();
757 18 : if( n > avail)
758 9 : n = avail;
759 : }
760 : // fill capacity() first,
761 : // to avoid an allocation
762 : {
763 : auto avail =
764 18 : eb_->capacity() -
765 18 : eb_->size();
766 18 : if( n > avail &&
767 : avail != 0)
768 3 : n = avail;
769 : }
770 18 : if(n == 0)
771 : {
772 : // dynamic buffer is full
773 : // attempt a 1 byte read so
774 : // we can detect overflow
775 2 : BOOST_ASSERT(
776 : body_buf_->size() == 0);
777 : // handled in init_dynamic
778 2 : BOOST_ASSERT(
779 : body_avail_ == 0);
780 2 : mbp_ = body_buf_->prepare(1);
781 2 : nprepare_ = 1;
782 : return
783 2 : mutable_buffers_type(mbp_);
784 : }
785 : }
786 16 : nprepare_ = n;
787 16 : return eb_->prepare(n);
788 : }
789 :
790 : // VFALCO TODO
791 0 : if(how_ == how::pull)
792 0 : detail::throw_logic_error();
793 :
794 : // VFALCO TODO
795 0 : detail::throw_logic_error();
796 : }
797 :
798 27 : case state::set_body:
799 : {
800 27 : if(how_ == how::elastic)
801 : {
802 : // attempt to transfer in-place
803 : // body into the dynamic buffer.
804 27 : system::error_code ec;
805 27 : init_dynamic(ec);
806 27 : if(! ec.failed())
807 : {
808 26 : if(st_ == state::body)
809 24 : goto do_body;
810 2 : BOOST_ASSERT(
811 : st_ == state::complete);
812 2 : return mutable_buffers_type{};
813 : }
814 :
815 : // not enough room, so we
816 : // return this error from parse()
817 : return
818 1 : mutable_buffers_type{};
819 : }
820 :
821 0 : if(how_ == how::sink)
822 : {
823 : // this is a no-op, to get the
824 : // caller to call parse next.
825 0 : return mutable_buffers_type{};
826 : }
827 :
828 : // VFALCO TODO
829 0 : detail::throw_logic_error();
830 : }
831 :
832 3 : case state::complete:
833 : // intended no-op
834 3 : return mutable_buffers_type{};
835 : }
836 : }
837 :
838 : void
839 5793 : parser::
840 : commit(
841 : std::size_t n)
842 : {
843 5793 : switch(st_)
844 : {
845 1 : default:
846 : case state::reset:
847 : {
848 : // reset must be called first
849 1 : detail::throw_logic_error();
850 : }
851 :
852 1 : case state::start:
853 : {
854 : // forgot to call start()
855 1 : detail::throw_logic_error();
856 : }
857 :
858 5589 : case state::header:
859 : {
860 5589 : if(n > nprepare_)
861 : {
862 : // n can't be greater than size of
863 : // the buffers returned by prepare()
864 1 : detail::throw_invalid_argument();
865 : }
866 :
867 5588 : if(got_eof_)
868 : {
869 : // can't commit after EOF
870 1 : detail::throw_logic_error();
871 : }
872 :
873 5587 : nprepare_ = 0; // invalidate
874 5587 : fb_.commit(n);
875 5587 : break;
876 : }
877 :
878 196 : case state::body:
879 : {
880 196 : if(n > nprepare_)
881 : {
882 : // n can't be greater than size of
883 : // the buffers returned by prepare()
884 1 : detail::throw_invalid_argument();
885 : }
886 :
887 195 : BOOST_ASSERT(! got_eof_ || n == 0);
888 :
889 195 : if(! is_plain())
890 : {
891 : // buffered payload
892 149 : cb0_.commit(n);
893 149 : break;
894 : }
895 :
896 : // plain payload
897 :
898 46 : if(how_ == how::in_place)
899 : {
900 26 : BOOST_ASSERT(body_buf_ == &cb0_);
901 26 : cb0_.commit(n);
902 26 : if(h_.md.payload == payload::size)
903 : {
904 12 : if(cb0_.size() <
905 12 : h_.md.payload_size)
906 : {
907 4 : body_avail_ += n;
908 4 : payload_remain_ -= n;
909 4 : break;
910 : }
911 8 : body_avail_ = h_.md.payload_size;
912 8 : payload_remain_ = 0;
913 8 : st_ = state::complete;
914 8 : break;
915 : }
916 :
917 14 : BOOST_ASSERT(
918 : h_.md.payload == payload::to_eof);
919 14 : body_avail_ += n;
920 14 : break;
921 : }
922 :
923 20 : if(how_ == how::elastic)
924 : {
925 20 : if(eb_->size() < eb_->max_size())
926 : {
927 19 : BOOST_ASSERT(body_avail_ == 0);
928 19 : BOOST_ASSERT(
929 : body_buf_->size() == 0);
930 19 : eb_->commit(n);
931 : }
932 : else
933 : {
934 : // If we get here then either
935 : // n==0 as a no-op, or n==1 for
936 : // an intended one byte read.
937 1 : BOOST_ASSERT(n <= 1);
938 1 : body_buf_->commit(n);
939 1 : body_avail_ += n;
940 : }
941 20 : body_total_ += n;
942 20 : if(h_.md.payload == payload::size)
943 : {
944 6 : BOOST_ASSERT(
945 : n <= payload_remain_);
946 6 : payload_remain_ -= n;
947 6 : if(payload_remain_ == 0)
948 6 : st_ = state::complete;
949 : }
950 20 : break;
951 : }
952 :
953 0 : if(how_ == how::sink)
954 : {
955 0 : cb0_.commit(n);
956 0 : break;
957 : }
958 :
959 0 : if(how_ == how::pull)
960 : {
961 : // VFALCO TODO
962 0 : detail::throw_logic_error();
963 : }
964 0 : break;
965 : }
966 :
967 2 : case state::set_body:
968 : {
969 2 : if(n > nprepare_)
970 : {
971 : // n can't be greater than size of
972 : // the buffers returned by prepare()
973 1 : detail::throw_invalid_argument();
974 : }
975 :
976 1 : BOOST_ASSERT(is_plain());
977 1 : BOOST_ASSERT(n == 0);
978 1 : if( how_ == how::elastic ||
979 0 : how_ == how::sink)
980 : {
981 : // intended no-op
982 : break;
983 : }
984 :
985 : // VFALCO TODO
986 0 : detail::throw_logic_error();
987 : }
988 :
989 4 : case state::complete:
990 : {
991 4 : BOOST_ASSERT(nprepare_ == 0);
992 :
993 4 : if(n > 0)
994 : {
995 : // n can't be greater than size of
996 : // the buffers returned by prepare()
997 1 : detail::throw_invalid_argument();
998 : }
999 :
1000 : // intended no-op
1001 3 : break;
1002 : }
1003 : }
1004 5786 : }
1005 :
1006 : void
1007 363 : parser::
1008 : commit_eof()
1009 : {
1010 363 : nprepare_ = 0; // invalidate
1011 :
1012 363 : switch(st_)
1013 : {
1014 1 : default:
1015 : case state::reset:
1016 : // reset must be called first
1017 1 : detail::throw_logic_error();
1018 :
1019 1 : case state::start:
1020 : // forgot to call prepare()
1021 1 : detail::throw_logic_error();
1022 :
1023 21 : case state::header:
1024 21 : got_eof_ = true;
1025 21 : break;
1026 :
1027 127 : case state::body:
1028 127 : got_eof_ = true;
1029 127 : break;
1030 :
1031 212 : case state::set_body:
1032 212 : got_eof_ = true;
1033 212 : break;
1034 :
1035 1 : case state::complete:
1036 : // can't commit eof when complete
1037 1 : detail::throw_logic_error();
1038 : }
1039 360 : }
1040 :
1041 : //-----------------------------------------------
1042 :
1043 : // process input data then
1044 : // eof if input data runs out.
1045 : void
1046 6701 : parser::
1047 : parse(
1048 : system::error_code& ec)
1049 : {
1050 6701 : ec = {};
1051 6701 : switch(st_)
1052 : {
1053 1 : default:
1054 : case state::reset:
1055 : // reset must be called first
1056 1 : detail::throw_logic_error();
1057 :
1058 1 : case state::start:
1059 : // start must be called first
1060 1 : detail::throw_logic_error();
1061 :
1062 5589 : case state::header:
1063 : {
1064 5589 : BOOST_ASSERT(h_.buf == static_cast<
1065 : void const*>(ws_.data()));
1066 5589 : BOOST_ASSERT(h_.cbuf == static_cast<
1067 : void const*>(ws_.data()));
1068 :
1069 5589 : h_.parse(fb_.size(), svc_.cfg.headers, ec);
1070 :
1071 5589 : if(ec == condition::need_more_input)
1072 : {
1073 3792 : if(! got_eof_)
1074 : {
1075 : // headers incomplete
1076 3774 : return;
1077 : }
1078 :
1079 18 : if(fb_.size() == 0)
1080 : {
1081 : // stream closed cleanly
1082 8 : st_ = state::complete;
1083 16 : ec = BOOST_HTTP_PROTO_ERR(
1084 : error::end_of_stream);
1085 8 : return;
1086 : }
1087 :
1088 : // stream closed with a
1089 : // partial message received
1090 10 : st_ = state::reset;
1091 20 : ec = BOOST_HTTP_PROTO_ERR(
1092 : error::incomplete);
1093 10 : return;
1094 : }
1095 1797 : if(ec.failed())
1096 : {
1097 : // other error,
1098 : //
1099 : // VFALCO map this to a bad
1100 : // request or bad response error?
1101 : //
1102 259 : st_ = state::reset; // unrecoverable
1103 259 : return;
1104 : }
1105 :
1106 : // headers are complete
1107 1538 : on_headers(ec);
1108 1538 : if(ec.failed())
1109 120 : return;
1110 1418 : if(st_ == state::complete)
1111 844 : break;
1112 :
1113 : BOOST_FALLTHROUGH;
1114 : }
1115 :
1116 : case state::body:
1117 : {
1118 574 : do_body:
1119 983 : BOOST_ASSERT(st_ == state::body);
1120 983 : BOOST_ASSERT(
1121 : h_.md.payload != payload::none);
1122 983 : BOOST_ASSERT(
1123 : h_.md.payload != payload::error);
1124 :
1125 983 : if( h_.md.payload == payload::chunked )
1126 : {
1127 238 : auto completed = false;
1128 238 : auto& input = cb0_;
1129 :
1130 238 : if( how_ == how::in_place )
1131 : {
1132 238 : auto& output = cb1_;
1133 : completed =
1134 238 : parse_chunked(
1135 238 : input, output, ec, chunk_remain_,
1136 238 : body_avail_, needs_chunk_close_);
1137 : }
1138 : else
1139 0 : detail::throw_logic_error();
1140 :
1141 238 : if( completed )
1142 73 : st_ = state::complete;
1143 :
1144 238 : return;
1145 : }
1146 745 : else if( filt_ )
1147 : {
1148 : // VFALCO TODO apply filter
1149 0 : detail::throw_logic_error();
1150 : }
1151 :
1152 745 : if(how_ == how::in_place)
1153 : {
1154 618 : BOOST_ASSERT(body_avail_ ==
1155 : body_buf_->size());
1156 618 : if(h_.md.payload == payload::size)
1157 : {
1158 255 : if(body_avail_ <
1159 255 : h_.md.payload_size)
1160 : {
1161 30 : if(got_eof_)
1162 : {
1163 : // incomplete
1164 2 : ec = BOOST_HTTP_PROTO_ERR(
1165 : error::incomplete);
1166 1 : return;
1167 : }
1168 29 : if(body_buf_->capacity() == 0)
1169 : {
1170 : // in_place buffer limit
1171 2 : ec = BOOST_HTTP_PROTO_ERR(
1172 : error::in_place_overflow);
1173 1 : return;
1174 : }
1175 56 : ec = BOOST_HTTP_PROTO_ERR(
1176 : error::need_data);
1177 28 : return;
1178 : }
1179 225 : BOOST_ASSERT(body_avail_ ==
1180 : h_.md.payload_size);
1181 225 : st_ = state::complete;
1182 225 : break;
1183 : }
1184 363 : if(body_avail_ > svc_.cfg.body_limit)
1185 : {
1186 2 : ec = BOOST_HTTP_PROTO_ERR(
1187 : error::body_too_large);
1188 1 : st_ = state::reset; // unrecoverable
1189 1 : return;
1190 : }
1191 362 : if( h_.md.payload == payload::chunked ||
1192 362 : ! got_eof_)
1193 : {
1194 496 : ec = BOOST_HTTP_PROTO_ERR(
1195 : error::need_data);
1196 248 : return;
1197 : }
1198 114 : BOOST_ASSERT(got_eof_);
1199 114 : st_ = state::complete;
1200 114 : break;
1201 : }
1202 :
1203 127 : if(how_ == how::elastic)
1204 : {
1205 : // state already updated in commit
1206 127 : if(h_.md.payload == payload::size)
1207 : {
1208 0 : BOOST_ASSERT(body_total_ <
1209 : h_.md.payload_size);
1210 0 : BOOST_ASSERT(payload_remain_ > 0);
1211 0 : if(body_avail_ != 0)
1212 : {
1213 0 : BOOST_ASSERT(
1214 : eb_->max_size() -
1215 : eb_->size() <
1216 : payload_remain_);
1217 0 : ec = BOOST_HTTP_PROTO_ERR(
1218 : error::buffer_overflow);
1219 0 : st_ = state::reset; // unrecoverable
1220 0 : return;
1221 : }
1222 0 : if(got_eof_)
1223 : {
1224 0 : ec = BOOST_HTTP_PROTO_ERR(
1225 : error::incomplete);
1226 0 : st_ = state::reset; // unrecoverable
1227 0 : return;
1228 : }
1229 0 : return;
1230 : }
1231 127 : BOOST_ASSERT(
1232 : h_.md.payload == payload::to_eof);
1233 173 : if( eb_->size() == eb_->max_size() &&
1234 46 : body_avail_ > 0)
1235 : {
1236 : // got here from the 1-byte read
1237 0 : ec = BOOST_HTTP_PROTO_ERR(
1238 : error::buffer_overflow);
1239 0 : st_ = state::reset; // unrecoverable
1240 0 : return;
1241 : }
1242 127 : if(got_eof_)
1243 : {
1244 113 : BOOST_ASSERT(body_avail_ == 0);
1245 113 : st_ = state::complete;
1246 113 : break;
1247 : }
1248 14 : BOOST_ASSERT(body_avail_ == 0);
1249 14 : break;
1250 : }
1251 :
1252 : // VFALCO TODO
1253 0 : detail::throw_logic_error();
1254 : }
1255 :
1256 211 : case state::set_body:
1257 : {
1258 : // transfer in_place data into set body
1259 :
1260 211 : if(how_ == how::elastic)
1261 : {
1262 211 : init_dynamic(ec);
1263 211 : if(! ec.failed())
1264 : {
1265 211 : if(st_ == state::body)
1266 102 : goto do_body;
1267 109 : BOOST_ASSERT(
1268 : st_ == state::complete);
1269 109 : break;
1270 : }
1271 0 : st_ = state::reset; // unrecoverable
1272 0 : return;
1273 : }
1274 :
1275 0 : if(how_ == how::sink)
1276 : {
1277 0 : auto n = body_buf_->size();
1278 0 : if(h_.md.payload == payload::size)
1279 : {
1280 : // sink_->size_hint(h_.md.payload_size, ec);
1281 :
1282 0 : if(n < h_.md.payload_size)
1283 : {
1284 0 : auto rv = sink_->write(
1285 0 : body_buf_->data(), false);
1286 0 : BOOST_ASSERT(rv.ec.failed() ||
1287 : rv.bytes == body_buf_->size());
1288 0 : BOOST_ASSERT(
1289 : rv.bytes >= body_avail_);
1290 0 : BOOST_ASSERT(
1291 : rv.bytes < payload_remain_);
1292 0 : body_buf_->consume(rv.bytes);
1293 0 : body_avail_ -= rv.bytes;
1294 0 : body_total_ += rv.bytes;
1295 0 : payload_remain_ -= rv.bytes;
1296 0 : if(rv.ec.failed())
1297 : {
1298 0 : ec = rv.ec;
1299 0 : st_ = state::reset; // unrecoverable
1300 0 : return;
1301 : }
1302 0 : st_ = state::body;
1303 0 : goto do_body;
1304 : }
1305 :
1306 0 : n = static_cast<std::size_t>(h_.md.payload_size);
1307 : }
1308 : // complete
1309 0 : BOOST_ASSERT(body_buf_ == &cb0_);
1310 0 : auto rv = sink_->write(
1311 0 : body_buf_->data(), true);
1312 0 : BOOST_ASSERT(rv.ec.failed() ||
1313 : rv.bytes == body_buf_->size());
1314 0 : body_buf_->consume(rv.bytes);
1315 0 : if(rv.ec.failed())
1316 : {
1317 0 : ec = rv.ec;
1318 0 : st_ = state::reset; // unrecoverable
1319 0 : return;
1320 : }
1321 0 : st_ = state::complete;
1322 0 : return;
1323 : }
1324 :
1325 : // VFALCO TODO
1326 0 : detail::throw_logic_error();
1327 : }
1328 :
1329 592 : case state::complete:
1330 : {
1331 : // This is a no-op except when set_body
1332 : // was called and we have in-place data.
1333 592 : switch(how_)
1334 : {
1335 296 : default:
1336 : case how::in_place:
1337 296 : break;
1338 :
1339 296 : case how::elastic:
1340 : {
1341 296 : if(body_buf_->size() == 0)
1342 296 : break;
1343 0 : BOOST_ASSERT(eb_->size() == 0);
1344 0 : auto n = buffers::buffer_copy(
1345 0 : eb_->prepare(
1346 0 : body_buf_->size()),
1347 0 : body_buf_->data());
1348 0 : body_buf_->consume(n);
1349 0 : break;
1350 : }
1351 :
1352 0 : case how::sink:
1353 : {
1354 0 : if(body_buf_->size() == 0)
1355 0 : break;
1356 0 : auto rv = sink_->write(
1357 0 : body_buf_->data(), false);
1358 0 : body_buf_->consume(rv.bytes);
1359 0 : if(rv.ec.failed())
1360 : {
1361 0 : ec = rv.ec;
1362 0 : st_ = state::reset; // unrecoverable
1363 0 : return;
1364 : }
1365 0 : break;
1366 : }
1367 :
1368 0 : case how::pull:
1369 : // VFALCO TODO
1370 0 : detail::throw_logic_error();
1371 : }
1372 : }
1373 : }
1374 : }
1375 :
1376 : //------------------------------------------------
1377 :
1378 : auto
1379 0 : parser::
1380 : pull_some() ->
1381 : const_buffers_type
1382 : {
1383 0 : return {};
1384 : }
1385 :
1386 : core::string_view
1387 1344 : parser::
1388 : body() const noexcept
1389 : {
1390 1344 : switch(st_)
1391 : {
1392 349 : default:
1393 : case state::reset:
1394 : case state::start:
1395 : case state::header:
1396 : case state::body:
1397 : case state::set_body:
1398 : // not complete
1399 349 : return {};
1400 :
1401 995 : case state::complete:
1402 995 : if(how_ != how::in_place)
1403 : {
1404 : // not in_place
1405 346 : return {};
1406 : }
1407 649 : auto cbp = body_buf_->data();
1408 649 : BOOST_ASSERT(cbp[1].size() == 0);
1409 649 : BOOST_ASSERT(cbp[0].size() == body_avail_);
1410 649 : return core::string_view(
1411 : static_cast<char const*>(
1412 649 : cbp[0].data()),
1413 1298 : static_cast<std::size_t>(body_avail_));
1414 : }
1415 : }
1416 :
1417 : core::string_view
1418 0 : parser::
1419 : release_buffered_data() noexcept
1420 : {
1421 0 : return {};
1422 : }
1423 :
1424 : //------------------------------------------------
1425 : //
1426 : // Implementation
1427 : //
1428 : //------------------------------------------------
1429 :
1430 : auto
1431 314 : parser::
1432 : safe_get_header() const ->
1433 : detail::header const*
1434 : {
1435 : // headers must be received
1436 628 : if( ! got_header() ||
1437 314 : fb_.size() == 0) // happens on eof
1438 0 : detail::throw_logic_error();
1439 :
1440 314 : return &h_;
1441 : }
1442 :
1443 : bool
1444 975 : parser::
1445 : is_plain() const noexcept
1446 : {
1447 1950 : return ! filt_ &&
1448 975 : h_.md.payload !=
1449 975 : payload::chunked;
1450 : }
1451 :
1452 : // Called immediately after complete headers are received
1453 : // to setup the circular buffers for subsequent operations.
1454 : // We leave fb_ as-is to indicate whether any data was
1455 : // received before eof.
1456 : //
1457 : void
1458 1538 : parser::
1459 : on_headers(
1460 : system::error_code& ec)
1461 : {
1462 : // overread currently includes any and all octets that
1463 : // extend beyond the current end of the header
1464 : // this can include associated body octets for the
1465 : // current message or octets of the next message in the
1466 : // stream, e.g. pipelining is being used
1467 1538 : auto const overread = fb_.size() - h_.size;
1468 1538 : BOOST_ASSERT(
1469 : overread <= svc_.max_overread());
1470 :
1471 : // metadata error
1472 1538 : if(h_.md.payload == payload::error)
1473 : {
1474 : // VFALCO This needs looking at
1475 240 : ec = BOOST_HTTP_PROTO_ERR(
1476 : error::bad_payload);
1477 120 : st_ = state::reset; // unrecoverable
1478 120 : return;
1479 : }
1480 :
1481 : // reserve headers + table
1482 1418 : ws_.reserve_front(h_.size);
1483 1418 : ws_.reserve_back(h_.table_space());
1484 :
1485 : // no payload
1486 1418 : if( h_.md.payload == payload::none ||
1487 574 : head_response_ )
1488 : {
1489 : // set cb0_ to overread
1490 844 : cb0_ = {
1491 844 : ws_.data(),
1492 844 : overread + fb_.capacity(),
1493 : overread };
1494 844 : body_avail_ = 0;
1495 844 : body_total_ = 0;
1496 844 : body_buf_ = &cb0_;
1497 844 : st_ = state::complete;
1498 844 : return;
1499 : }
1500 :
1501 : // calculate filter
1502 574 : filt_ = nullptr; // VFALCO TODO
1503 :
1504 574 : if(is_plain())
1505 : {
1506 : // plain payload
1507 485 : if(h_.md.payload == payload::size)
1508 : {
1509 250 : if(h_.md.payload_size >
1510 250 : svc_.cfg.body_limit)
1511 : {
1512 0 : ec = BOOST_HTTP_PROTO_ERR(
1513 : error::body_too_large);
1514 0 : st_ = state::reset; // unrecoverable
1515 0 : return;
1516 : }
1517 :
1518 : // for plain messages with a known size,, we can
1519 : // get away with only using cb0_ as our input
1520 : // area and leaving cb1_ blank
1521 :
1522 250 : BOOST_ASSERT(fb_.max_size() >= h_.size);
1523 250 : BOOST_ASSERT(
1524 : fb_.max_size() - h_.size ==
1525 : overread + fb_.capacity());
1526 250 : BOOST_ASSERT(fb_.data().data() == h_.buf);
1527 250 : BOOST_ASSERT(svc_.max_codec == 0);
1528 :
1529 : auto cap =
1530 250 : (overread + fb_.capacity()) + // reuse previously designated storage
1531 250 : svc_.cfg.min_buffer + // minimum buffer size for prepare() calls
1532 250 : svc_.max_codec; // tentatively we can delete this
1533 :
1534 499 : if( cap > h_.md.payload_size &&
1535 249 : cap - h_.md.payload_size >= svc_.max_overread() )
1536 : {
1537 : // we eagerly process octets as they arrive,
1538 : // so it's important to limit potential
1539 : // overread as applying a transformation algo
1540 : // can be prohibitively expensive
1541 14 : cap =
1542 14 : static_cast<std::size_t>(h_.md.payload_size) +
1543 14 : svc_.max_overread();
1544 : }
1545 :
1546 250 : BOOST_ASSERT(cap <= ws_.size());
1547 :
1548 250 : cb0_ = { ws_.data(), cap, overread };
1549 250 : cb1_ = {};
1550 :
1551 250 : body_buf_ = &cb0_;
1552 250 : body_avail_ = cb0_.size();
1553 250 : if( body_avail_ >= h_.md.payload_size)
1554 225 : body_avail_ = h_.md.payload_size;
1555 :
1556 250 : body_total_ = body_avail_;
1557 250 : payload_remain_ =
1558 250 : h_.md.payload_size - body_total_;
1559 :
1560 250 : st_ = state::body;
1561 250 : return;
1562 : }
1563 :
1564 : // overread is not applicable
1565 235 : BOOST_ASSERT(
1566 : h_.md.payload == payload::to_eof);
1567 : auto const n0 =
1568 235 : fb_.capacity() - h_.size +
1569 235 : svc_.cfg.min_buffer +
1570 235 : svc_.max_codec;
1571 235 : BOOST_ASSERT(n0 <= ws_.size());
1572 235 : cb0_ = { ws_.data(), n0, overread };
1573 235 : body_buf_ = &cb0_;
1574 235 : body_avail_ = cb0_.size();
1575 235 : body_total_ = body_avail_;
1576 235 : st_ = state::body;
1577 235 : return;
1578 : }
1579 :
1580 89 : if( h_.md.payload == payload::chunked )
1581 : {
1582 : auto const n0 =
1583 89 : fb_.capacity() - fb_.size();
1584 :
1585 89 : cb0_ = { ws_.data(), n0 / 2, overread };
1586 89 : cb1_ = { ws_.data() + n0 / 2, n0 - (n0 / 2), 0 };
1587 89 : body_buf_ = &cb1_;
1588 89 : st_ = state::body;
1589 89 : return;
1590 : }
1591 :
1592 : // buffered payload
1593 0 : auto const n0 = fb_.capacity() - h_.size;
1594 0 : BOOST_ASSERT(n0 <= svc_.max_overread());
1595 0 : auto n1 = svc_.cfg.min_buffer;
1596 0 : if(! filt_)
1597 0 : n1 += svc_.max_codec;
1598 0 : BOOST_ASSERT(n0 + n1 <= ws_.size());
1599 0 : cb0_ = { ws_.data(), n0, overread };
1600 0 : cb1_ = { ws_.data() + n0, n1 };
1601 0 : body_buf_ = &cb1_;
1602 0 : body_avail_ = 0;
1603 0 : body_total_ = 0;
1604 0 : st_ = state::body;
1605 : }
1606 :
1607 : // Called at the end of set_body
1608 : void
1609 299 : parser::
1610 : on_set_body()
1611 : {
1612 : // This function is called after all
1613 : // limit checking and calculation of
1614 : // chunked or filter.
1615 :
1616 299 : BOOST_ASSERT(got_header());
1617 :
1618 299 : nprepare_ = 0; // invalidate
1619 :
1620 299 : if(how_ == how::elastic)
1621 : {
1622 299 : if(h_.md.payload == payload::none)
1623 : {
1624 58 : BOOST_ASSERT(st_ == state::complete);
1625 58 : return;
1626 : }
1627 :
1628 241 : st_ = state::set_body;
1629 241 : return;
1630 : }
1631 :
1632 0 : if(how_ == how::sink)
1633 : {
1634 0 : if(h_.md.payload == payload::none)
1635 : {
1636 0 : BOOST_ASSERT(st_ == state::complete);
1637 : // force a trip through parse so
1638 : // we can calculate any error.
1639 0 : st_ = state::set_body;
1640 0 : return;
1641 : }
1642 :
1643 0 : st_ = state::set_body;
1644 0 : return;
1645 : }
1646 :
1647 : // VFALCO TODO
1648 0 : detail::throw_logic_error();
1649 : }
1650 :
1651 : void
1652 238 : parser::
1653 : init_dynamic(
1654 : system::error_code& ec)
1655 : {
1656 : // attempt to transfer in-place
1657 : // body into the dynamic buffer.
1658 238 : BOOST_ASSERT(
1659 : body_avail_ == body_buf_->size());
1660 238 : BOOST_ASSERT(
1661 : body_total_ == body_avail_);
1662 : auto const space_left =
1663 238 : eb_->max_size() - eb_->size();
1664 :
1665 238 : if(h_.md.payload == payload::size)
1666 : {
1667 121 : if(space_left < h_.md.payload_size)
1668 : {
1669 2 : ec = BOOST_HTTP_PROTO_ERR(
1670 : error::buffer_overflow);
1671 1 : return;
1672 : }
1673 : // reserve the full size
1674 120 : eb_->prepare(static_cast<std::size_t>(h_.md.payload_size));
1675 : // transfer in-place body
1676 120 : auto n = static_cast<std::size_t>(body_avail_);
1677 120 : if( n > h_.md.payload_size)
1678 0 : n = static_cast<std::size_t>(h_.md.payload_size);
1679 120 : eb_->commit(
1680 : buffers::buffer_copy(
1681 120 : eb_->prepare(n),
1682 120 : body_buf_->data()));
1683 120 : BOOST_ASSERT(body_avail_ == n);
1684 120 : BOOST_ASSERT(body_total_ == n);
1685 120 : BOOST_ASSERT(payload_remain_ ==
1686 : h_.md.payload_size - n);
1687 120 : body_buf_->consume(n);
1688 120 : body_avail_ = 0;
1689 120 : if(n < h_.md.payload_size)
1690 : {
1691 9 : BOOST_ASSERT(
1692 : body_buf_->size() == 0);
1693 9 : st_ = state::body;
1694 9 : return;
1695 : }
1696 : // complete
1697 111 : st_ = state::complete;
1698 111 : return;
1699 : }
1700 :
1701 117 : BOOST_ASSERT(h_.md.payload ==
1702 : payload::to_eof);
1703 117 : if(space_left < body_avail_)
1704 : {
1705 0 : ec = BOOST_HTTP_PROTO_ERR(
1706 : error::buffer_overflow);
1707 0 : return;
1708 : }
1709 117 : eb_->commit(
1710 : buffers::buffer_copy(
1711 117 : eb_->prepare(static_cast<std::size_t>(body_avail_)),
1712 117 : body_buf_->data()));
1713 117 : body_buf_->consume(static_cast<std::size_t>(body_avail_));
1714 117 : body_avail_ = 0;
1715 117 : BOOST_ASSERT(
1716 : body_buf_->size() == 0);
1717 117 : st_ = state::body;
1718 : }
1719 :
1720 : } // http_proto
1721 : } // boost
|