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