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 : #ifndef BOOST_HTTP_PROTO_IMPL_PARSER_IPP
11 : #define BOOST_HTTP_PROTO_IMPL_PARSER_IPP
12 :
13 : #include <boost/http_proto/parser.hpp>
14 : #include <boost/http_proto/context.hpp>
15 : #include <boost/http_proto/error.hpp>
16 : #include <boost/http_proto/service/zlib_service.hpp>
17 : #include <boost/http_proto/detail/except.hpp>
18 : #include <boost/buffers/buffer_copy.hpp>
19 : #include <boost/url/grammar/ci_string.hpp>
20 : #include <boost/assert.hpp>
21 : #include <memory>
22 :
23 : namespace boost {
24 : namespace http_proto {
25 :
26 : //------------------------------------------------
27 : /*
28 : Buffer Usage
29 :
30 : | | begin
31 : | H | p | | f | read headers
32 : | H | p | | T | f | set T body
33 : | H | p | | C | T | f | make codec C
34 : | H | p | b | C | T | f | decode p into b
35 : | H | p | b | C | T | f | read/parse loop
36 : | H | | T | f | destroy codec
37 : | H | | T | f | finished
38 :
39 : H headers
40 : C codec
41 : T body
42 : f table
43 : p partial payload
44 : b body data
45 :
46 : - We can compact the headers:
47 : move the table downwards to
48 : squeeze out the unused space
49 :
50 : A "plain payload" has no actionable transfer
51 : encoding.
52 :
53 : A "buffered payload" is any payload which is
54 : not plain. A second buffer is required
55 : for reading.
56 : */
57 : //------------------------------------------------
58 :
59 : class parser_service
60 : : public service
61 : {
62 : public:
63 : parser::config_base cfg;
64 : std::size_t space_needed = 0;
65 : std::size_t max_codec = 0;
66 : zlib::deflate_decoder_service const*
67 : deflate_svc = nullptr;
68 :
69 : parser_service(
70 : context& ctx,
71 : parser::config_base const& cfg_);
72 :
73 : std::size_t
74 40065 : max_overread() const noexcept
75 : {
76 : return
77 40065 : cfg.headers.max_size +
78 40065 : cfg.min_buffer;
79 : }
80 : };
81 :
82 6 : parser_service::
83 : parser_service(
84 : context& ctx,
85 6 : parser::config_base const& cfg_)
86 6 : : cfg(cfg_)
87 : {
88 : /*
89 : | fb | cb0 | cb1 | C | T | f |
90 :
91 : fb flat_buffer headers.max_size
92 : cb0 circular_buffer min_buffer
93 : cb1 circular_buffer min_buffer
94 : C codec max_codec
95 : T body max_type_erase
96 : f table max_table_space
97 :
98 : */
99 : // validate
100 : //if(cfg.min_prepare > cfg.max_prepare)
101 : //detail::throw_invalid_argument();
102 :
103 6 : if( cfg.min_buffer < 1 ||
104 6 : cfg.min_buffer > cfg.body_limit)
105 0 : detail::throw_invalid_argument();
106 :
107 6 : if(cfg.max_prepare < 1)
108 0 : detail::throw_invalid_argument();
109 :
110 : // VFALCO TODO OVERFLOW CHECING
111 : {
112 : //fb_.size() - h_.size +
113 : //svc_.cfg.min_buffer +
114 : //svc_.cfg.min_buffer +
115 : //svc_.max_codec;
116 : }
117 :
118 : // VFALCO OVERFLOW CHECKING ON THIS
119 6 : space_needed +=
120 6 : cfg.headers.valid_space_needed();
121 :
122 : // cb0_, cb1_
123 : // VFALCO OVERFLOW CHECKING ON THIS
124 6 : space_needed +=
125 6 : cfg.min_buffer +
126 : cfg.min_buffer;
127 :
128 : // T
129 6 : space_needed += cfg.max_type_erase;
130 :
131 : // max_codec
132 : {
133 6 : if(cfg.apply_deflate_decoder)
134 : {
135 0 : deflate_svc = &ctx.get_service<
136 0 : zlib::deflate_decoder_service>();
137 : auto const n =
138 0 : deflate_svc->space_needed();
139 0 : if( max_codec < n)
140 0 : max_codec = n;
141 : }
142 : }
143 6 : space_needed += max_codec;
144 6 : }
145 :
146 : void
147 6 : install_parser_service(
148 : context& ctx,
149 : parser::config_base const& cfg)
150 : {
151 : ctx.make_service<
152 6 : parser_service>(cfg);
153 6 : }
154 :
155 : //------------------------------------------------
156 :
157 6646 : parser::
158 : parser(
159 : context& ctx,
160 6646 : detail::kind k)
161 : : ctx_(ctx)
162 : , svc_(ctx.get_service<
163 13292 : parser_service>())
164 : , h_(detail::empty{k})
165 6646 : , st_(state::reset)
166 : {
167 6646 : auto const n =
168 6646 : svc_.space_needed;
169 6646 : ws_.allocate(n);
170 6646 : h_.cap = n;
171 6646 : }
172 :
173 : //------------------------------------------------
174 : //
175 : // Special Members
176 : //
177 : //------------------------------------------------
178 :
179 6646 : parser::
180 6646 : ~parser()
181 : {
182 6646 : }
183 :
184 : parser::
185 : parser(
186 : parser&&) noexcept = default;
187 :
188 : //------------------------------------------------
189 : //
190 : // Modifiers
191 : //
192 : //------------------------------------------------
193 :
194 : // prepare for a new stream
195 : void
196 6644 : parser::
197 : reset() noexcept
198 : {
199 6644 : ws_.clear();
200 6644 : st_ = state::start;
201 6644 : got_eof_ = false;
202 6644 : }
203 :
204 : void
205 6644 : parser::
206 : start_impl(
207 : bool head_response)
208 : {
209 6644 : std::size_t leftover = 0;
210 6644 : switch(st_)
211 : {
212 0 : default:
213 : case state::reset:
214 : // reset must be called first
215 0 : detail::throw_logic_error();
216 :
217 6644 : case state::start:
218 6644 : BOOST_ASSERT(h_.size == 0);
219 6644 : BOOST_ASSERT(fb_.size() == 0);
220 6644 : BOOST_ASSERT(! got_eof_);
221 6644 : break;
222 :
223 0 : case state::header:
224 : // start() called twice
225 0 : detail::throw_logic_error();
226 :
227 0 : case state::body:
228 : // current message is incomplete
229 0 : detail::throw_logic_error();
230 :
231 0 : case state::complete:
232 : {
233 : // overread data is always in cb0_
234 :
235 0 : if(fb_.size() > 0)
236 : {
237 : // headers with no body
238 0 : BOOST_ASSERT(h_.size > 0);
239 0 : fb_.consume(h_.size);
240 0 : leftover = fb_.size();
241 : // move unused octets to front
242 0 : buffers::buffer_copy(
243 0 : buffers::mutable_buffer(
244 0 : ws_.data(),
245 : leftover),
246 0 : fb_.data());
247 : }
248 : else
249 : {
250 : // leftover data after body
251 : }
252 0 : break;
253 : }
254 : }
255 :
256 6644 : ws_.clear();
257 :
258 13288 : fb_ = {
259 6644 : ws_.data(),
260 6644 : svc_.cfg.headers.max_size +
261 6644 : svc_.cfg.min_buffer,
262 : leftover };
263 6644 : BOOST_ASSERT(
264 : fb_.capacity() == svc_.max_overread());
265 :
266 13288 : h_ = detail::header(
267 6644 : detail::empty{h_.kind});
268 6644 : h_.buf = reinterpret_cast<
269 6644 : char*>(ws_.data());
270 6644 : h_.cbuf = h_.buf;
271 6644 : h_.cap = ws_.size();
272 :
273 6644 : BOOST_ASSERT(! head_response ||
274 : h_.kind == detail::kind::response);
275 6644 : head_response_ = head_response;
276 :
277 6644 : body_ = body::in_place;
278 6644 : st_ = state::header;
279 6644 : }
280 :
281 : auto
282 20356 : parser::
283 : prepare() ->
284 : mutable_buffers_type
285 : {
286 20356 : switch(st_)
287 : {
288 0 : default:
289 : case state::reset:
290 : // reset must be called first
291 0 : detail::throw_logic_error();
292 :
293 0 : case state::start:
294 : // start must be called first
295 0 : detail::throw_logic_error();
296 :
297 19459 : case state::header:
298 : {
299 19459 : BOOST_ASSERT(h_.size <
300 : svc_.cfg.headers.max_size);
301 19459 : auto n = fb_.capacity() - fb_.size();
302 19459 : BOOST_ASSERT(n <= svc_.max_overread());
303 19459 : if( n > svc_.cfg.max_prepare)
304 0 : n = svc_.cfg.max_prepare;
305 19459 : mbp_[0] = fb_.prepare(n);
306 19459 : return mutable_buffers_type(
307 38918 : &mbp_[0], 1);
308 : }
309 :
310 897 : case state::body:
311 : {
312 : // buffered payload
313 897 : if(body_buf_ != &cb0_)
314 : {
315 0 : auto n = cb0_.capacity() -
316 0 : cb0_.size();
317 0 : if( n > svc_.cfg.max_prepare)
318 0 : n = svc_.cfg.max_prepare;
319 0 : mbp_ = cb0_.prepare(n);
320 0 : return mutable_buffers_type(mbp_);
321 : }
322 :
323 : // plain payload
324 :
325 897 : if( body_ == body::in_place ||
326 0 : body_ == body::sink)
327 : {
328 897 : auto n = cb0_.capacity() -
329 897 : cb0_.size();
330 897 : if( n > svc_.cfg.max_prepare)
331 0 : n = svc_.cfg.max_prepare;
332 897 : mbp_ = cb0_.prepare(n);
333 897 : BOOST_ASSERT(mbp_[1].size() == 0);
334 897 : return mutable_buffers_type(
335 1794 : &mbp_[0], 1);
336 : }
337 :
338 0 : if(body_ == body::dynamic)
339 : {
340 0 : BOOST_ASSERT(dyn_ != nullptr);
341 0 : if(h_.md.payload == payload::size)
342 : {
343 : // exact size
344 : std::size_t n =
345 0 : h_.md.payload_size - dyn_->size();
346 0 : if( n > svc_.cfg.max_prepare)
347 0 : n = svc_.cfg.max_prepare;
348 0 : return dyn_->prepare(n);
349 : }
350 :
351 : // heuristic size
352 0 : std::size_t n = svc_.cfg.min_buffer;
353 0 : if( n > svc_.cfg.max_prepare)
354 0 : n = svc_.cfg.max_prepare;
355 0 : return dyn_->prepare(n);
356 : }
357 :
358 : // VFALCO TODO
359 0 : if(body_ == body::stream)
360 0 : detail::throw_logic_error();
361 :
362 : // VFALCO TODO
363 0 : detail::throw_logic_error();
364 : }
365 :
366 0 : case state::complete:
367 : // We allow the call for callers
368 : // who want normalized usage, but
369 : // just return a 0-sized sequence.
370 0 : return mutable_buffers_type{};
371 : }
372 : }
373 :
374 : void
375 20356 : parser::
376 : commit(
377 : std::size_t n)
378 : {
379 : // Can't commit after eof
380 20356 : if(got_eof_)
381 0 : detail::throw_logic_error();
382 :
383 20356 : switch(st_)
384 : {
385 0 : default:
386 : case state::reset:
387 : // reset must be called first
388 0 : detail::throw_logic_error();
389 :
390 0 : case state::start:
391 : // forgot to call start()
392 0 : detail::throw_logic_error();
393 :
394 19459 : case state::header:
395 19459 : fb_.commit(n);
396 19459 : break;
397 :
398 897 : case state::body:
399 897 : if(body_buf_ != &cb0_)
400 : {
401 : // buffered body
402 0 : cb0_.commit(n);
403 0 : break;
404 : }
405 897 : if(body_ == body::in_place)
406 : {
407 897 : cb0_.commit(n);
408 897 : break;
409 : }
410 0 : if(body_ == body::dynamic)
411 : {
412 0 : dyn_->commit(n);
413 0 : break;
414 : }
415 0 : if(body_ == body::sink)
416 : {
417 0 : cb0_.commit(n);
418 0 : break;
419 : }
420 0 : if(body_ == body::stream)
421 : {
422 : // VFALCO TODO
423 0 : detail::throw_logic_error();
424 : }
425 0 : break;
426 :
427 0 : case state::complete:
428 : // intended no-op
429 0 : break;
430 : }
431 20356 : }
432 :
433 : void
434 1450 : parser::
435 : commit_eof()
436 : {
437 1450 : switch(st_)
438 : {
439 0 : default:
440 : case state::reset:
441 : // reset must be called first
442 0 : detail::throw_logic_error();
443 :
444 0 : case state::start:
445 : // forgot to call prepare()
446 0 : detail::throw_logic_error();
447 :
448 0 : case state::header:
449 0 : got_eof_ = true;
450 0 : break;
451 :
452 1450 : case state::body:
453 1450 : got_eof_ = true;
454 1450 : break;
455 :
456 0 : case state::complete:
457 : // can't commit eof when complete
458 0 : detail::throw_logic_error();
459 : }
460 1450 : }
461 :
462 : //-----------------------------------------------
463 :
464 : // process input data then
465 : // eof if input data runs out.
466 : void
467 21806 : parser::
468 : parse(
469 : system::error_code& ec)
470 : {
471 21806 : ec = {};
472 21806 : switch(st_)
473 : {
474 0 : default:
475 : case state::reset:
476 : // reset must be called first
477 0 : detail::throw_logic_error();
478 :
479 0 : case state::start:
480 : // start must be called first
481 0 : detail::throw_logic_error();
482 :
483 19459 : case state::header:
484 : {
485 19459 : BOOST_ASSERT(h_.buf == static_cast<
486 : void const*>(ws_.data()));
487 19459 : BOOST_ASSERT(h_.cbuf == static_cast<
488 : void const*>(ws_.data()));
489 19459 : auto const new_size = fb_.size();
490 19459 : h_.parse(new_size, svc_.cfg.headers, ec);
491 19459 : if(ec == condition::need_more_input)
492 : {
493 12815 : if(! got_eof_)
494 : {
495 : // headers incomplete
496 12815 : return;
497 : }
498 0 : if(h_.size == 0)
499 : {
500 : // stream closed cleanly
501 0 : ec = BOOST_HTTP_PROTO_ERR(
502 : error::end_of_stream);
503 0 : return;
504 : }
505 :
506 : // stream closed with a
507 : // partial message received
508 0 : ec = BOOST_HTTP_PROTO_ERR(
509 : error::incomplete);
510 0 : return;
511 : }
512 6644 : if(ec.failed())
513 : {
514 : // other error,
515 : //
516 : // VFALCO map this to a bad
517 : // request or bad response error?
518 : //
519 128 : st_ = state::reset; // unrecoverable
520 128 : return;
521 : }
522 :
523 : // headers are complete
524 6516 : on_headers(ec);
525 6516 : if(ec.failed())
526 0 : return;
527 6516 : if(st_ == state::complete)
528 1343 : break;
529 5173 : BOOST_ASSERT(st_ == state::body);
530 : BOOST_FALLTHROUGH;
531 : }
532 :
533 : case state::body:
534 : {
535 7520 : if(body_ == body::in_place)
536 : {
537 7520 : if(filt_)
538 : {
539 : // apply filter
540 0 : detail::throw_logic_error();
541 : }
542 7520 : if(h_.md.payload == payload::size)
543 : {
544 4338 : if(cb0_.size() <
545 4338 : h_.md.payload_size)
546 : {
547 1230 : ec = BOOST_HTTP_PROTO_ERR(
548 : error::need_data);
549 615 : return;
550 : }
551 3723 : st_ = state::complete;
552 3723 : break;
553 : }
554 3182 : BOOST_ASSERT(h_.md.payload ==
555 : payload::to_eof);
556 3182 : if(! got_eof_)
557 : {
558 3464 : ec = BOOST_HTTP_PROTO_ERR(
559 : error::need_data);
560 1732 : return;
561 : }
562 1450 : st_ = state::complete;
563 1450 : break;
564 : }
565 :
566 : // VFALCO TODO
567 0 : detail::throw_logic_error();
568 : }
569 :
570 0 : case state::complete:
571 : {
572 : // This is a no-op except when set_body
573 : // was called and we have in-place data.
574 0 : switch(body_)
575 : {
576 0 : default:
577 : case body::in_place:
578 0 : break;
579 :
580 0 : case body::dynamic:
581 : {
582 0 : if(body_buf_->size() == 0)
583 0 : break;
584 0 : BOOST_ASSERT(dyn_->size() == 0);
585 0 : auto n = buffers::buffer_copy(
586 0 : dyn_->prepare(
587 0 : body_buf_->size()),
588 0 : body_buf_->data());
589 0 : body_buf_->consume(n);
590 0 : break;
591 : }
592 :
593 0 : case body::sink:
594 : {
595 0 : if(body_buf_->size() == 0)
596 0 : break;
597 0 : auto rv = sink_->write(
598 0 : body_buf_->data(), false);
599 0 : body_buf_->consume(rv.bytes);
600 0 : if(rv.ec.failed())
601 : {
602 0 : ec = rv.ec;
603 0 : st_ = state::reset; // unrecoverable
604 0 : return;
605 : }
606 0 : break;
607 : }
608 :
609 0 : case body::stream:
610 : // VFALCO TODO
611 0 : detail::throw_logic_error();
612 : }
613 : }
614 : }
615 : }
616 :
617 : //------------------------------------------------
618 :
619 : auto
620 0 : parser::
621 : get_stream() ->
622 : stream
623 : {
624 : // body type already chosen
625 0 : if(body_ != body::in_place)
626 0 : detail::throw_logic_error();
627 :
628 0 : body_ = body::stream;
629 0 : return stream(*this);
630 : }
631 :
632 : core::string_view
633 4543 : parser::
634 : in_place_body() const
635 : {
636 : // not in place
637 4543 : if(body_ != body::in_place)
638 0 : detail::throw_logic_error();
639 :
640 : // incomplete
641 4543 : if(st_ != state::complete)
642 0 : detail::throw_logic_error();
643 :
644 4543 : buffers::const_buffer_pair bs = body_buf_->data();
645 4543 : BOOST_ASSERT(bs[1].size() == 0);
646 9086 : return core::string_view(static_cast<
647 4543 : char const*>(bs[0].data()),
648 9086 : bs[0].size());
649 : }
650 :
651 : //------------------------------------------------
652 :
653 : core::string_view
654 0 : parser::
655 : release_buffered_data() noexcept
656 : {
657 0 : return {};
658 : }
659 :
660 : //------------------------------------------------
661 : //
662 : // Implementation
663 : //
664 : //------------------------------------------------
665 :
666 : auto
667 5969 : parser::
668 : safe_get_header() const ->
669 : detail::header const*
670 : {
671 5969 : switch(st_)
672 : {
673 0 : default:
674 : case state::start:
675 : case state::header:
676 : // Headers not received yet
677 0 : detail::throw_logic_error();
678 :
679 5969 : case state::body:
680 : case state::complete:
681 : // VFALCO check if headers are discarded?
682 5969 : break;
683 : }
684 5969 : return &h_;
685 : }
686 :
687 : // Called immediately after
688 : // complete headers are received
689 : void
690 6516 : parser::
691 : on_headers(
692 : system::error_code& ec)
693 : {
694 : auto const overread =
695 6516 : fb_.size() - h_.size;
696 6516 : BOOST_ASSERT(
697 : overread <= svc_.max_overread());
698 :
699 : // reserve headers + table
700 6516 : ws_.reserve_front(h_.size);
701 6516 : ws_.reserve_back(h_.table_space());
702 :
703 : // no payload
704 6516 : if( h_.md.payload == payload::none ||
705 5173 : head_response_)
706 : {
707 : // set cb0_ to overread
708 2686 : cb0_ = {
709 1343 : ws_.data(),
710 1343 : fb_.capacity() - h_.size,
711 : overread };
712 1343 : st_ = state::complete;
713 1343 : return;
714 : }
715 :
716 : // metadata error
717 5173 : if(h_.md.payload == payload::error)
718 : {
719 : // VFALCO This needs looking at
720 0 : ec = BOOST_HTTP_PROTO_ERR(
721 : error::bad_payload);
722 0 : st_ = state::reset; // unrecoverable
723 0 : return;
724 : }
725 :
726 : // calculate filter
727 5173 : filt_ = nullptr;
728 :
729 : // plain payload
730 5173 : if( h_.md.payload != payload::chunked &&
731 5173 : ! filt_)
732 : {
733 : // payload size is known
734 5173 : if(h_.md.payload == payload::size)
735 : {
736 3723 : if(h_.md.payload_size >
737 3723 : svc_.cfg.body_limit)
738 : {
739 0 : ec = BOOST_HTTP_PROTO_ERR(
740 : error::body_too_large);
741 0 : st_ = state::reset; // unrecoverable
742 0 : return;
743 : }
744 : auto n0 =
745 3723 : fb_.capacity() - h_.size +
746 3723 : svc_.cfg.min_buffer +
747 3723 : svc_.max_codec;
748 : // limit the capacity of cb0_ so
749 : // that going over max_overread
750 : // is impossible.
751 7446 : if( n0 > h_.md.payload_size &&
752 3723 : n0 - h_.md.payload_size >=
753 3723 : svc_.max_overread())
754 3723 : n0 =
755 3723 : h_.md.payload_size +
756 3723 : svc_.max_overread();
757 3723 : BOOST_ASSERT(n0 <= ws_.size());
758 3723 : cb0_ = {
759 3723 : ws_.data(),
760 : n0,
761 : overread };
762 3723 : body_buf_ = &cb0_;
763 3723 : st_ = state::body;
764 3723 : return;
765 : }
766 :
767 : // payload to eof
768 : // overread is not applicable
769 : auto const n0 =
770 1450 : fb_.capacity() - h_.size +
771 1450 : svc_.cfg.min_buffer +
772 1450 : svc_.max_codec;
773 1450 : BOOST_ASSERT(n0 <= ws_.size());
774 1450 : cb0_ = {
775 1450 : ws_.data(),
776 : n0,
777 : overread };
778 1450 : body_buf_ = &cb0_;
779 1450 : st_ = state::body;
780 1450 : return;
781 : }
782 :
783 : // buffered payload
784 : auto const n0 =
785 0 : fb_.capacity() - h_.size;
786 0 : BOOST_ASSERT(
787 : n0 <= svc_.max_overread());
788 0 : auto n1 = svc_.cfg.min_buffer;
789 0 : if(! filt_)
790 0 : n1 += svc_.max_codec;
791 0 : BOOST_ASSERT(
792 : n0 + n1 <= ws_.size());
793 0 : cb0_ = {
794 0 : ws_.data(),
795 : n0,
796 : overread };
797 0 : cb1_ = {
798 0 : ws_.data() + n0,
799 : n1 };
800 0 : body_buf_ = &cb1_;
801 0 : st_ = state::body;
802 0 : return;
803 : }
804 :
805 : // Called at the end of set_body
806 : void
807 0 : parser::
808 : on_set_body()
809 : {
810 : // This function is called after all
811 : // limit checking and calculation of
812 : // chunked or filter.
813 :
814 0 : BOOST_ASSERT(got_header());
815 :
816 0 : if(h_.md.payload == payload::size)
817 : {
818 0 : if(body_ == body::dynamic)
819 : {
820 0 : dyn_->prepare(
821 0 : h_.md.payload_size);
822 0 : return;
823 : }
824 0 : if(body_ == body::sink)
825 : {
826 : //sink_->size_hint(&h_.md.payload_size);
827 0 : return;
828 : }
829 0 : return;
830 : }
831 :
832 0 : return;
833 : }
834 :
835 : } // http_proto
836 : } // boost
837 :
838 : #endif
|