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