Line data Source code
1 : //
2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3 : // Copyright (c) 2024 Mohammad Nejati
4 : //
5 : // Distributed under the Boost Software License, Version 1.0. (See accompanying
6 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7 : //
8 : // Official repository: https://github.com/cppalliance/http
9 : //
10 :
11 : #include <boost/http/detail/except.hpp>
12 : #include <boost/http/detail/workspace.hpp>
13 : #include <boost/http/error.hpp>
14 : #include <boost/http/parser.hpp>
15 : #include <boost/http/static_request.hpp>
16 : #include <boost/http/static_response.hpp>
17 :
18 : #include <boost/assert.hpp>
19 : #include <boost/capy/buffers/circular_dynamic_buffer.hpp>
20 : #include <boost/capy/buffers/buffer_copy.hpp>
21 : #include <boost/capy/buffers/flat_dynamic_buffer.hpp>
22 : #include <boost/capy/buffers/front.hpp>
23 : #include <boost/capy/buffers/slice.hpp>
24 : #include <boost/capy/ex/system_context.hpp>
25 : #include <boost/http/brotli/decode.hpp>
26 : #include <boost/http/zlib/error.hpp>
27 : #include <boost/http/zlib/inflate.hpp>
28 : #include <boost/url/grammar/ci_string.hpp>
29 : #include <boost/url/grammar/error.hpp>
30 : #include <boost/url/grammar/hexdig_chars.hpp>
31 :
32 : #include "src/detail/brotli_filter_base.hpp"
33 : #include "src/detail/buffer_utils.hpp"
34 : #include "src/detail/zlib_filter_base.hpp"
35 :
36 : #include <memory>
37 :
38 : namespace boost {
39 : namespace http {
40 :
41 : /*
42 : Principles for fixed-size buffer design
43 :
44 : axiom 1:
45 : To read data you must have a buffer.
46 :
47 : axiom 2:
48 : The size of the HTTP header is not
49 : known in advance.
50 :
51 : conclusion 3:
52 : A single I/O can produce a complete
53 : HTTP header and additional payload
54 : data.
55 :
56 : conclusion 4:
57 : A single I/O can produce multiple
58 : complete HTTP headers, complete
59 : payloads, and a partial header or
60 : payload.
61 :
62 : axiom 5:
63 : A process is in one of two states:
64 : 1. at or below capacity
65 : 2. above capacity
66 :
67 : axiom 6:
68 : A program which can allocate an
69 : unbounded number of resources can
70 : go above capacity.
71 :
72 : conclusion 7:
73 : A program can guarantee never going
74 : above capacity if all resources are
75 : provisioned at program startup.
76 :
77 : corollary 8:
78 : `parser` and `serializer` should each
79 : allocate a single buffer of calculated
80 : size, and never resize it.
81 :
82 : axiom #:
83 : A parser and a serializer are always
84 : used in pairs.
85 :
86 : Buffer Usage
87 :
88 : | | begin
89 : | H | p | | f | read headers
90 : | H | p | | T | f | set T body
91 : | H | p | | C | T | f | make codec C
92 : | H | p | b | C | T | f | decode p into b
93 : | H | p | b | C | T | f | read/parse loop
94 : | H | | T | f | destroy codec
95 : | H | | T | f | finished
96 :
97 : H headers
98 : C codec
99 : T body
100 : f table
101 : p partial payload
102 : b body data
103 :
104 : "payload" is the bytes coming in from
105 : the stream.
106 :
107 : "body" is the logical body, after transfer
108 : encoding is removed. This can be the
109 : same as the payload.
110 :
111 : A "plain payload" is when the payload and
112 : body are identical (no transfer encodings).
113 :
114 : A "buffered payload" is any payload which is
115 : not plain. A second buffer is required
116 : for reading.
117 :
118 : "overread" is additional data received past
119 : the end of the headers when reading headers,
120 : or additional data received past the end of
121 : the message payload.
122 : */
123 :
124 : namespace {
125 :
126 : class chained_sequence
127 : {
128 : char const* pos_;
129 : char const* end_;
130 : char const* begin_b_;
131 : char const* end_b_;
132 :
133 : public:
134 71111 : chained_sequence(capy::const_buffer_pair const& cbp)
135 71111 : : pos_(static_cast<char const*>(cbp[0].data()))
136 71111 : , end_(pos_ + cbp[0].size())
137 71111 : , begin_b_(static_cast<char const*>(cbp[1].data()))
138 71111 : , end_b_(begin_b_ + cbp[1].size())
139 : {
140 71111 : }
141 :
142 : char const*
143 319026 : next() noexcept
144 : {
145 319026 : ++pos_;
146 : // most frequently taken branch
147 319026 : if(pos_ < end_)
148 296983 : return pos_;
149 :
150 : // bring the second range
151 22043 : if(begin_b_ != end_b_)
152 : {
153 0 : pos_ = begin_b_;
154 0 : end_ = end_b_;
155 0 : begin_b_ = end_b_;
156 0 : return pos_;
157 : }
158 :
159 : // undo the increament
160 22043 : pos_ = end_;
161 22043 : return nullptr;
162 : }
163 :
164 : bool
165 211814 : is_empty() const noexcept
166 : {
167 211814 : return pos_ == end_;
168 : }
169 :
170 : char
171 304559 : value() const noexcept
172 : {
173 304559 : return *pos_;
174 : }
175 :
176 : std::size_t
177 225998 : size() const noexcept
178 : {
179 225998 : return (end_ - pos_) + (end_b_ - begin_b_);
180 : }
181 : };
182 :
183 : std::uint64_t
184 66555 : parse_hex(
185 : chained_sequence& cs,
186 : system::error_code& ec) noexcept
187 : {
188 66555 : std::uint64_t v = 0;
189 66555 : std::size_t init_size = cs.size();
190 153453 : while(!cs.is_empty())
191 : {
192 133707 : auto n = grammar::hexdig_value(cs.value());
193 133707 : if(n < 0)
194 : {
195 46808 : if(init_size == cs.size())
196 : {
197 2 : ec = BOOST_HTTP_ERR(
198 : error::bad_payload);
199 1 : return 0;
200 : }
201 46807 : return v;
202 : }
203 :
204 : // at least 4 significant bits are free
205 86899 : if(v > (std::numeric_limits<std::uint64_t>::max)() >> 4)
206 : {
207 2 : ec = BOOST_HTTP_ERR(
208 : error::bad_payload);
209 1 : return 0;
210 : }
211 :
212 86898 : v = (v << 4) | static_cast<std::uint64_t>(n);
213 86898 : cs.next();
214 : }
215 39492 : ec = BOOST_HTTP_ERR(
216 : error::need_data);
217 19746 : return 0;
218 : }
219 :
220 : void
221 47159 : find_eol(
222 : chained_sequence& cs,
223 : system::error_code& ec) noexcept
224 : {
225 53848 : while(!cs.is_empty())
226 : {
227 53760 : if(cs.value() == '\r')
228 : {
229 47071 : if(!cs.next())
230 238 : break;
231 46833 : if(cs.value() != '\n')
232 : {
233 4 : ec = BOOST_HTTP_ERR(
234 : error::bad_payload);
235 2 : return;
236 : }
237 46831 : cs.next();
238 46831 : return;
239 : }
240 6689 : cs.next();
241 : }
242 652 : ec = BOOST_HTTP_ERR(
243 : error::need_data);
244 : }
245 :
246 : void
247 61961 : parse_eol(
248 : chained_sequence& cs,
249 : system::error_code& ec) noexcept
250 : {
251 61961 : if(cs.size() >= 2)
252 : {
253 : // we are sure size is at least 2
254 61637 : if(cs.value() == '\r' && *cs.next() == '\n')
255 : {
256 61634 : cs.next();
257 61634 : return;
258 : }
259 6 : ec = BOOST_HTTP_ERR(
260 : error::bad_payload);
261 3 : return;
262 : }
263 648 : ec = BOOST_HTTP_ERR(
264 : error::need_data);
265 : }
266 :
267 : void
268 4229 : skip_trailer_headers(
269 : chained_sequence& cs,
270 : system::error_code& ec) noexcept
271 : {
272 4513 : while(!cs.is_empty())
273 : {
274 4493 : if(cs.value() == '\r')
275 : {
276 4141 : if(!cs.next())
277 12 : break;
278 4129 : if(cs.value() != '\n')
279 : {
280 4 : ec = BOOST_HTTP_ERR(
281 : error::bad_payload);
282 2 : return;
283 : }
284 4127 : cs.next();
285 4127 : return;
286 : }
287 : // skip to the end of field
288 352 : find_eol(cs, ec);
289 352 : if(ec)
290 68 : return;
291 : }
292 64 : ec = BOOST_HTTP_ERR(
293 : error::need_data);
294 : }
295 :
296 : template<class UInt>
297 : std::size_t
298 179401 : clamp(
299 : UInt x,
300 : std::size_t limit = (std::numeric_limits<
301 : std::size_t>::max)()) noexcept
302 : {
303 179401 : if(x >= limit)
304 45506 : return limit;
305 133895 : return static_cast<std::size_t>(x);
306 : }
307 :
308 : class zlib_filter
309 : : public detail::zlib_filter_base
310 : {
311 : http::zlib::inflate_service& svc_;
312 :
313 : public:
314 0 : zlib_filter(
315 : http::zlib::inflate_service& svc,
316 : int window_bits)
317 0 : : svc_(svc)
318 : {
319 : system::error_code ec = static_cast<http::zlib::error>(
320 0 : svc_.init2(strm_, window_bits));
321 0 : if(ec != http::zlib::error::ok)
322 0 : detail::throw_system_error(ec);
323 0 : }
324 :
325 : private:
326 : virtual
327 : results
328 0 : do_process(
329 : capy::mutable_buffer out,
330 : capy::const_buffer in,
331 : bool more) noexcept override
332 : {
333 0 : strm_.next_out = static_cast<unsigned char*>(out.data());
334 0 : strm_.avail_out = saturate_cast(out.size());
335 0 : strm_.next_in = static_cast<unsigned char*>(const_cast<void *>(in.data()));
336 0 : strm_.avail_in = saturate_cast(in.size());
337 :
338 : auto rs = static_cast<http::zlib::error>(
339 0 : svc_.inflate(
340 0 : strm_,
341 : more ? http::zlib::no_flush : http::zlib::finish));
342 :
343 0 : results rv;
344 0 : rv.out_bytes = saturate_cast(out.size()) - strm_.avail_out;
345 0 : rv.in_bytes = saturate_cast(in.size()) - strm_.avail_in;
346 0 : rv.finished = (rs == http::zlib::error::stream_end);
347 :
348 0 : if(rs < http::zlib::error::ok && rs != http::zlib::error::buf_err)
349 0 : rv.ec = rs;
350 :
351 0 : return rv;
352 : }
353 : };
354 :
355 : class brotli_filter
356 : : public detail::brotli_filter_base
357 : {
358 : http::brotli::decode_service& svc_;
359 : http::brotli::decoder_state* state_;
360 :
361 : public:
362 0 : brotli_filter(http::brotli::decode_service& svc)
363 0 : : svc_(svc)
364 : {
365 0 : state_ = svc_.create_instance(nullptr, nullptr, nullptr);
366 0 : if(!state_)
367 0 : detail::throw_bad_alloc();
368 0 : }
369 :
370 0 : ~brotli_filter()
371 0 : {
372 0 : svc_.destroy_instance(state_);
373 0 : }
374 :
375 : private:
376 : virtual
377 : results
378 0 : do_process(
379 : capy::mutable_buffer out,
380 : capy::const_buffer in,
381 : bool more) noexcept override
382 : {
383 0 : auto* next_in = reinterpret_cast<const std::uint8_t*>(in.data());
384 0 : auto available_in = in.size();
385 0 : auto* next_out = reinterpret_cast<std::uint8_t*>(out.data());
386 0 : auto available_out = out.size();
387 :
388 0 : auto rs = svc_.decompress_stream(
389 : state_,
390 : &available_in,
391 : &next_in,
392 : &available_out,
393 : &next_out,
394 : nullptr);
395 :
396 0 : results rv;
397 0 : rv.in_bytes = in.size() - available_in;
398 0 : rv.out_bytes = out.size() - available_out;
399 0 : rv.finished = svc_.is_finished(state_);
400 :
401 0 : if(!more && rs == http::brotli::decoder_result::needs_more_input)
402 0 : rv.ec = BOOST_HTTP_ERR(error::bad_payload);
403 :
404 0 : if(rs == http::brotli::decoder_result::error)
405 0 : rv.ec = BOOST_HTTP_ERR(
406 : svc_.get_error_code(state_));
407 :
408 0 : return rv;
409 : }
410 : };
411 :
412 : } // namespace
413 :
414 : //------------------------------------------------
415 :
416 : class parser::impl
417 : {
418 : enum class state
419 : {
420 : reset,
421 : start,
422 : header,
423 : header_done,
424 : body,
425 : complete,
426 : };
427 :
428 : std::shared_ptr<parser_config_impl const> cfg_;
429 :
430 : detail::workspace ws_;
431 : static_request m_;
432 : std::uint64_t body_limit_;
433 : std::uint64_t body_total_;
434 : std::uint64_t payload_remain_;
435 : std::uint64_t chunk_remain_;
436 : std::size_t body_avail_;
437 : std::size_t nprepare_;
438 :
439 : capy::flat_dynamic_buffer fb_;
440 : capy::circular_dynamic_buffer cb0_;
441 : capy::circular_dynamic_buffer cb1_;
442 :
443 : capy::mutable_buffer_pair mbp_;
444 : capy::const_buffer_pair cbp_;
445 :
446 : std::unique_ptr<detail::filter> filter_;
447 :
448 : state state_;
449 : bool got_header_;
450 : bool got_eof_;
451 : bool head_response_;
452 : bool needs_chunk_close_;
453 : bool trailer_headers_;
454 : bool chunked_body_ended;
455 :
456 : public:
457 1689 : impl(std::shared_ptr<parser_config_impl const> cfg, detail::kind k)
458 1689 : : cfg_(std::move(cfg))
459 1689 : , ws_(cfg_->space_needed)
460 1689 : , m_(ws_.data(), ws_.size())
461 1689 : , state_(state::reset)
462 1689 : , got_header_(false)
463 : {
464 1689 : m_.h_ = detail::header(detail::empty{ k });
465 1689 : }
466 :
467 : bool
468 31380 : got_header() const noexcept
469 : {
470 31380 : return got_header_;
471 : }
472 :
473 : bool
474 50092 : is_complete() const noexcept
475 : {
476 50092 : return state_ == state::complete;
477 : }
478 :
479 : static_request const&
480 315 : safe_get_request() const
481 : {
482 : // headers must be received
483 315 : if(! got_header_)
484 0 : detail::throw_logic_error();
485 :
486 315 : return m_;
487 : }
488 :
489 : static_response const&
490 3 : safe_get_response() const
491 : {
492 : // headers must be received
493 3 : if(! got_header_)
494 0 : detail::throw_logic_error();
495 :
496 : // TODO: use a union
497 3 : return reinterpret_cast<static_response const&>(m_);
498 : }
499 :
500 : void
501 2235 : reset() noexcept
502 : {
503 2235 : ws_.clear();
504 2235 : state_ = state::start;
505 2235 : got_header_ = false;
506 2235 : got_eof_ = false;
507 2235 : }
508 :
509 : void
510 10164 : start(
511 : bool head_response)
512 : {
513 10164 : std::size_t leftover = 0;
514 10164 : switch(state_)
515 : {
516 1 : default:
517 : case state::reset:
518 : // reset must be called first
519 1 : detail::throw_logic_error();
520 :
521 2160 : case state::start:
522 : // reset required on eof
523 2160 : if(got_eof_)
524 0 : detail::throw_logic_error();
525 2160 : break;
526 :
527 3 : case state::header:
528 3 : if(fb_.size() == 0)
529 : {
530 : // start() called twice
531 2 : detail::throw_logic_error();
532 : }
533 : BOOST_FALLTHROUGH;
534 :
535 : case state::header_done:
536 : case state::body:
537 : // current message is incomplete
538 2 : detail::throw_logic_error();
539 :
540 7999 : case state::complete:
541 : {
542 : // remove available body.
543 7999 : if(is_plain())
544 4000 : cb0_.consume(body_avail_);
545 : // move leftovers to front
546 :
547 7999 : ws_.clear();
548 7999 : leftover = cb0_.size();
549 :
550 7999 : auto* dest = reinterpret_cast<char*>(ws_.data());
551 7999 : auto cbp = cb0_.data();
552 7999 : auto* a = static_cast<char const*>(cbp[0].data());
553 7999 : auto* b = static_cast<char const*>(cbp[1].data());
554 7999 : auto an = cbp[0].size();
555 7999 : auto bn = cbp[1].size();
556 :
557 7999 : if(bn == 0)
558 : {
559 7561 : std::memmove(dest, a, an);
560 : }
561 : else
562 : {
563 : // if `a` can fit between `dest` and `b`, shift `b` to the left
564 : // and copy `a` to its position. if `a` fits perfectly, the
565 : // shift will be of size 0.
566 : // if `a` requires more space, shift `b` to the right and
567 : // copy `a` to its position. this process may require multiple
568 : // iterations and should be done chunk by chunk to prevent `b`
569 : // from overlapping with `a`.
570 : do
571 : {
572 : // clamp right shifts to prevent overlap with `a`
573 438 : auto* bp = (std::min)(dest + an, const_cast<char*>(a) - bn);
574 438 : b = static_cast<char const*>(std::memmove(bp, b, bn));
575 :
576 : // a chunk or all of `a` based on available space
577 438 : auto chunk_a = static_cast<std::size_t>(b - dest);
578 438 : std::memcpy(dest, a, chunk_a); // never overlap
579 438 : an -= chunk_a;
580 438 : dest += chunk_a;
581 438 : a += chunk_a;
582 438 : } while(an);
583 : }
584 :
585 7999 : break;
586 : }
587 : }
588 :
589 10159 : ws_.clear();
590 :
591 20318 : fb_ = {
592 10159 : ws_.data(),
593 10159 : cfg_->headers.max_size + cfg_->min_buffer,
594 : leftover };
595 :
596 10159 : BOOST_ASSERT(
597 : fb_.capacity() == cfg_->max_overread() - leftover);
598 :
599 10159 : BOOST_ASSERT(
600 : head_response == false ||
601 : m_.h_.kind == detail::kind::response);
602 :
603 10159 : m_.h_ = detail::header(detail::empty{m_.h_.kind});
604 10159 : m_.h_.buf = reinterpret_cast<char*>(ws_.data());
605 10159 : m_.h_.cbuf = m_.h_.buf;
606 10159 : m_.h_.cap = ws_.size();
607 :
608 10159 : state_ = state::header;
609 :
610 : // reset to the configured default
611 10159 : body_limit_ = cfg_->body_limit;
612 :
613 10159 : body_total_ = 0;
614 10159 : payload_remain_ = 0;
615 10159 : chunk_remain_ = 0;
616 10159 : body_avail_ = 0;
617 10159 : nprepare_ = 0;
618 :
619 10159 : filter_.reset();
620 :
621 10159 : got_header_ = false;
622 10159 : head_response_ = head_response;
623 10159 : needs_chunk_close_ = false;
624 10159 : trailer_headers_ = false;
625 10159 : chunked_body_ended = false;
626 10159 : }
627 :
628 : auto
629 68721 : prepare() ->
630 : mutable_buffers_type
631 : {
632 68721 : nprepare_ = 0;
633 :
634 68721 : switch(state_)
635 : {
636 1 : default:
637 : case state::reset:
638 : // reset must be called first
639 1 : detail::throw_logic_error();
640 :
641 1 : case state::start:
642 : // start must be called first
643 1 : detail::throw_logic_error();
644 :
645 27874 : case state::header:
646 : {
647 27874 : BOOST_ASSERT(
648 : m_.h_.size < cfg_->headers.max_size);
649 27874 : std::size_t n = fb_.capacity();
650 27874 : BOOST_ASSERT(n <= cfg_->max_overread());
651 27874 : n = clamp(n, cfg_->max_prepare);
652 27874 : mbp_[0] = fb_.prepare(n);
653 27874 : nprepare_ = n;
654 27874 : return mutable_buffers_type(&mbp_[0], 1);
655 : }
656 :
657 0 : case state::header_done:
658 : // forgot to call parse()
659 0 : detail::throw_logic_error();
660 :
661 40844 : case state::body:
662 : {
663 40844 : if(got_eof_)
664 : {
665 : // forgot to call parse()
666 0 : detail::throw_logic_error();
667 : }
668 :
669 40844 : if(! is_plain())
670 : {
671 : // buffered payload
672 21207 : std::size_t n = cb0_.capacity();
673 21207 : n = clamp(n, cfg_->max_prepare);
674 21207 : nprepare_ = n;
675 21207 : mbp_ = cb0_.prepare(n);
676 21207 : return detail::make_span(mbp_);
677 : }
678 : else
679 : {
680 : // plain payload
681 19637 : std::size_t n = cb0_.capacity();
682 19637 : n = clamp(n, cfg_->max_prepare);
683 :
684 19637 : if(m_.payload() == payload::size)
685 : {
686 19623 : if(n > payload_remain_)
687 : {
688 18416 : std::size_t overread =
689 18416 : n - static_cast<std::size_t>(payload_remain_);
690 18416 : if(overread > cfg_->max_overread())
691 8504 : n = static_cast<std::size_t>(payload_remain_) +
692 8504 : cfg_->max_overread();
693 : }
694 : }
695 : else
696 : {
697 14 : BOOST_ASSERT(
698 : m_.payload() == payload::to_eof);
699 : // No more messages can be pipelined, so
700 : // limit the output buffer to the remaining
701 : // body limit plus one byte to detect
702 : // exhaustion.
703 14 : std::uint64_t r = body_limit_remain();
704 14 : if(r != std::uint64_t(-1))
705 14 : r += 1;
706 14 : n = clamp(r, n);
707 : }
708 :
709 19637 : nprepare_ = n;
710 19637 : mbp_ = cb0_.prepare(n);
711 19637 : return detail::make_span(mbp_);
712 : }
713 : }
714 :
715 1 : case state::complete:
716 : // already complete
717 1 : detail::throw_logic_error();
718 : }
719 : }
720 :
721 : void
722 68128 : commit(
723 : std::size_t n)
724 : {
725 68128 : switch(state_)
726 : {
727 1 : default:
728 : case state::reset:
729 : {
730 : // reset must be called first
731 1 : detail::throw_logic_error();
732 : }
733 :
734 1 : case state::start:
735 : {
736 : // forgot to call start()
737 1 : detail::throw_logic_error();
738 : }
739 :
740 27444 : case state::header:
741 : {
742 27444 : if(n > nprepare_)
743 : {
744 : // n can't be greater than size of
745 : // the buffers returned by prepare()
746 1 : detail::throw_invalid_argument();
747 : }
748 :
749 27443 : if(got_eof_)
750 : {
751 : // can't commit after EOF
752 1 : detail::throw_logic_error();
753 : }
754 :
755 27442 : nprepare_ = 0; // invalidate
756 27442 : fb_.commit(n);
757 27442 : break;
758 : }
759 :
760 0 : case state::header_done:
761 : {
762 : // forgot to call parse()
763 0 : detail::throw_logic_error();
764 : }
765 :
766 40682 : case state::body:
767 : {
768 40682 : if(n > nprepare_)
769 : {
770 : // n can't be greater than size of
771 : // the buffers returned by prepare()
772 2 : detail::throw_invalid_argument();
773 : }
774 :
775 40680 : if(got_eof_)
776 : {
777 : // can't commit after EOF
778 0 : detail::throw_logic_error();
779 : }
780 :
781 40680 : nprepare_ = 0; // invalidate
782 40680 : cb0_.commit(n);
783 40680 : break;
784 : }
785 :
786 0 : case state::complete:
787 : {
788 : // already complete
789 0 : detail::throw_logic_error();
790 : }
791 : }
792 68122 : }
793 :
794 : void
795 132 : commit_eof()
796 : {
797 132 : nprepare_ = 0; // invalidate
798 :
799 132 : switch(state_)
800 : {
801 1 : default:
802 : case state::reset:
803 : // reset must be called first
804 1 : detail::throw_logic_error();
805 :
806 1 : case state::start:
807 : // forgot to call start()
808 1 : detail::throw_logic_error();
809 :
810 12 : case state::header:
811 12 : got_eof_ = true;
812 12 : break;
813 :
814 0 : case state::header_done:
815 : // forgot to call parse()
816 0 : detail::throw_logic_error();
817 :
818 117 : case state::body:
819 117 : got_eof_ = true;
820 117 : break;
821 :
822 1 : case state::complete:
823 : // can't commit eof when complete
824 1 : detail::throw_logic_error();
825 : }
826 129 : }
827 :
828 : void
829 85415 : parse(
830 : system::error_code& ec)
831 : {
832 85415 : ec = {};
833 85415 : switch(state_)
834 : {
835 1 : default:
836 : case state::reset:
837 : // reset must be called first
838 1 : detail::throw_logic_error();
839 :
840 1 : case state::start:
841 : // start must be called first
842 1 : detail::throw_logic_error();
843 :
844 32948 : case state::header:
845 : {
846 32948 : BOOST_ASSERT(m_.h_.buf == static_cast<
847 : void const*>(ws_.data()));
848 32948 : BOOST_ASSERT(m_.h_.cbuf == static_cast<
849 : void const*>(ws_.data()));
850 :
851 32948 : m_.h_.parse(fb_.size(), cfg_->headers, ec);
852 :
853 32948 : if(ec == condition::need_more_input)
854 : {
855 23238 : if(! got_eof_)
856 : {
857 : // headers incomplete
858 23229 : return;
859 : }
860 :
861 9 : if(fb_.size() == 0)
862 : {
863 : // stream closed cleanly
864 4 : state_ = state::reset;
865 8 : ec = BOOST_HTTP_ERR(
866 : error::end_of_stream);
867 4 : return;
868 : }
869 :
870 : // stream closed with a
871 : // partial message received
872 5 : state_ = state::reset;
873 10 : ec = BOOST_HTTP_ERR(
874 : error::incomplete);
875 5 : return;
876 : }
877 9710 : else if(ec)
878 : {
879 : // other error,
880 : //
881 : // VFALCO map this to a bad
882 : // request or bad response error?
883 : //
884 259 : state_ = state::reset; // unrecoverable
885 259 : return;
886 : }
887 :
888 9451 : got_header_ = true;
889 :
890 : // reserve headers + table
891 9451 : ws_.reserve_front(m_.h_.size);
892 9451 : ws_.reserve_back(m_.h_.table_space());
893 :
894 : // no payload
895 18096 : if(m_.payload() == payload::none ||
896 8645 : head_response_)
897 : {
898 : // octets of the next message
899 806 : auto overread = fb_.size() - m_.h_.size;
900 806 : cb0_ = { ws_.data(), overread, overread };
901 806 : ws_.reserve_front(overread);
902 806 : state_ = state::complete;
903 806 : return;
904 : }
905 :
906 8645 : state_ = state::header_done;
907 8645 : break;
908 : }
909 :
910 8642 : case state::header_done:
911 : {
912 : // metadata error
913 8642 : if(m_.payload() == payload::error)
914 : {
915 : // VFALCO This needs looking at
916 120 : ec = BOOST_HTTP_ERR(
917 : error::bad_payload);
918 60 : state_ = state::reset; // unrecoverable
919 60 : return;
920 : }
921 :
922 : // overread currently includes any and all octets that
923 : // extend beyond the current end of the header
924 : // this can include associated body octets for the
925 : // current message or octets of the next message in the
926 : // stream, e.g. pipelining is being used
927 8582 : auto const overread = fb_.size() - m_.h_.size;
928 8582 : BOOST_ASSERT(overread <= cfg_->max_overread());
929 :
930 8582 : auto cap = fb_.capacity() + overread +
931 8582 : cfg_->min_buffer;
932 :
933 : // reserve body buffers first, as the decoder
934 : // must be installed after them.
935 8582 : auto const p = ws_.reserve_front(cap);
936 :
937 : // Content-Encoding
938 8582 : switch(m_.metadata().content_encoding.coding)
939 : {
940 0 : case content_coding::deflate:
941 0 : if(!cfg_->apply_deflate_decoder)
942 0 : goto no_filter;
943 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
944 : {
945 0 : filter_.reset(new zlib_filter(
946 : *svc,
947 0 : cfg_->zlib_window_bits));
948 : }
949 0 : break;
950 :
951 0 : case content_coding::gzip:
952 0 : if(!cfg_->apply_gzip_decoder)
953 0 : goto no_filter;
954 0 : if(auto* svc = capy::get_system_context().find_service<http::zlib::inflate_service>())
955 : {
956 0 : filter_.reset(new zlib_filter(
957 : *svc,
958 0 : cfg_->zlib_window_bits + 16));
959 : }
960 0 : break;
961 :
962 0 : case content_coding::br:
963 0 : if(!cfg_->apply_brotli_decoder)
964 0 : goto no_filter;
965 0 : if(auto* svc = capy::get_system_context().find_service<http::brotli::decode_service>())
966 : {
967 0 : filter_.reset(new brotli_filter(*svc));
968 : }
969 0 : break;
970 :
971 0 : no_filter:
972 8582 : default:
973 8582 : break;
974 : }
975 :
976 8582 : if(is_plain())
977 : {
978 4311 : cb0_ = { p, cap, overread };
979 4311 : cb1_ = {};
980 : }
981 : else
982 : {
983 : // buffered payload
984 4271 : std::size_t n0 = (overread > cfg_->min_buffer)
985 8542 : ? overread
986 4271 : : cfg_->min_buffer;
987 4271 : std::size_t n1 = cfg_->min_buffer;
988 :
989 4271 : cb0_ = { p , n0, overread };
990 4271 : cb1_ = { p + n0 , n1 };
991 : }
992 :
993 8582 : if(m_.payload() == payload::size)
994 : {
995 8388 : if(!filter_ &&
996 4194 : body_limit_ < m_.payload_size())
997 : {
998 2 : ec = BOOST_HTTP_ERR(
999 : error::body_too_large);
1000 1 : state_ = state::reset;
1001 1 : return;
1002 : }
1003 4193 : payload_remain_ = m_.payload_size();
1004 : }
1005 :
1006 8581 : state_ = state::body;
1007 : BOOST_FALLTHROUGH;
1008 : }
1009 :
1010 50194 : case state::body:
1011 : {
1012 50194 : BOOST_ASSERT(state_ == state::body);
1013 50194 : BOOST_ASSERT(m_.payload() != payload::none);
1014 50194 : BOOST_ASSERT(m_.payload() != payload::error);
1015 :
1016 8349 : auto set_state_to_complete = [&]()
1017 : {
1018 8349 : state_ = state::complete;
1019 58543 : };
1020 :
1021 50194 : if(m_.payload() == payload::chunked)
1022 : {
1023 : for(;;)
1024 : {
1025 77449 : if(chunk_remain_ == 0
1026 75238 : && !chunked_body_ended)
1027 : {
1028 71111 : auto cs = chained_sequence(cb0_.data());
1029 20437 : auto check_ec = [&]()
1030 : {
1031 20437 : if(ec == condition::need_more_input && got_eof_)
1032 : {
1033 0 : ec = BOOST_HTTP_ERR(error::incomplete);
1034 0 : state_ = state::reset;
1035 : }
1036 91548 : };
1037 :
1038 71111 : if(needs_chunk_close_)
1039 : {
1040 61961 : parse_eol(cs, ec);
1041 61961 : if(ec)
1042 : {
1043 327 : check_ec();
1044 20437 : return;
1045 : }
1046 : }
1047 9150 : else if(trailer_headers_)
1048 : {
1049 4229 : skip_trailer_headers(cs, ec);
1050 4229 : if(ec)
1051 : {
1052 102 : check_ec();
1053 102 : return;
1054 : }
1055 4127 : cb0_.consume(cb0_.size() - cs.size());
1056 4127 : chunked_body_ended = true;
1057 8264 : continue;
1058 : }
1059 :
1060 66555 : auto chunk_size = parse_hex(cs, ec);
1061 66555 : if(ec)
1062 : {
1063 19748 : check_ec();
1064 19748 : return;
1065 : }
1066 :
1067 : // skip chunk extensions
1068 46807 : find_eol(cs, ec);
1069 46807 : if(ec)
1070 : {
1071 260 : check_ec();
1072 260 : return;
1073 : }
1074 :
1075 46547 : cb0_.consume(cb0_.size() - cs.size());
1076 46547 : chunk_remain_ = chunk_size;
1077 :
1078 46547 : needs_chunk_close_ = true;
1079 46547 : if(chunk_remain_ == 0)
1080 : {
1081 4137 : needs_chunk_close_ = false;
1082 4137 : trailer_headers_ = true;
1083 4137 : continue;
1084 : }
1085 : }
1086 :
1087 48748 : if(cb0_.size() == 0 && !chunked_body_ended)
1088 : {
1089 1432 : if(got_eof_)
1090 : {
1091 2 : ec = BOOST_HTTP_ERR(
1092 : error::incomplete);
1093 1 : state_ = state::reset;
1094 1 : return;
1095 : }
1096 :
1097 2862 : ec = BOOST_HTTP_ERR(
1098 : error::need_data);
1099 1431 : return;
1100 : }
1101 :
1102 47316 : if(filter_)
1103 : {
1104 0 : chunk_remain_ -= apply_filter(
1105 : ec,
1106 : clamp(chunk_remain_, cb0_.size()),
1107 0 : !chunked_body_ended);
1108 :
1109 0 : if(ec || chunked_body_ended)
1110 0 : return;
1111 : }
1112 : else
1113 : {
1114 : const std::size_t chunk_avail =
1115 47316 : clamp(chunk_remain_, cb0_.size());
1116 : const auto chunk =
1117 47316 : capy::prefix(cb0_.data(), chunk_avail);
1118 :
1119 47316 : if(body_limit_remain() < chunk_avail)
1120 : {
1121 0 : ec = BOOST_HTTP_ERR(
1122 : error::body_too_large);
1123 0 : state_ = state::reset;
1124 4127 : return;
1125 : }
1126 :
1127 : // in_place style
1128 47316 : auto copied = capy::buffer_copy(
1129 47316 : cb1_.prepare(cb1_.capacity()),
1130 : chunk);
1131 47316 : chunk_remain_ -= copied;
1132 47316 : body_avail_ += copied;
1133 47316 : body_total_ += copied;
1134 47316 : cb0_.consume(copied);
1135 47316 : cb1_.commit(copied);
1136 47316 : if(cb1_.capacity() == 0
1137 47316 : && !chunked_body_ended)
1138 : {
1139 0 : ec = BOOST_HTTP_ERR(
1140 : error::in_place_overflow);
1141 0 : return;
1142 : }
1143 :
1144 47316 : if(chunked_body_ended)
1145 : {
1146 4127 : set_state_to_complete();
1147 4127 : return;
1148 : }
1149 : }
1150 51453 : }
1151 : }
1152 : else
1153 : {
1154 : // non-chunked payload
1155 :
1156 72594 : const std::size_t payload_avail = [&]()
1157 : {
1158 24198 : auto ret = cb0_.size();
1159 24198 : if(!filter_)
1160 24198 : ret -= body_avail_;
1161 24198 : if(m_.payload() == payload::size)
1162 23941 : return clamp(payload_remain_, ret);
1163 : // payload::eof
1164 257 : return ret;
1165 24198 : }();
1166 :
1167 72594 : const bool is_complete = [&]()
1168 : {
1169 24198 : if(m_.payload() == payload::size)
1170 23941 : return payload_avail == payload_remain_;
1171 : // payload::eof
1172 257 : return got_eof_;
1173 24198 : }();
1174 :
1175 24198 : if(filter_)
1176 : {
1177 0 : payload_remain_ -= apply_filter(
1178 0 : ec, payload_avail, !is_complete);
1179 0 : if(ec || is_complete)
1180 0 : return;
1181 : }
1182 : else
1183 : {
1184 : // plain body
1185 :
1186 24198 : if(m_.payload() == payload::to_eof)
1187 : {
1188 257 : if(body_limit_remain() < payload_avail)
1189 : {
1190 2 : ec = BOOST_HTTP_ERR(
1191 : error::body_too_large);
1192 1 : state_ = state::reset;
1193 1 : return;
1194 : }
1195 : }
1196 :
1197 : // in_place style
1198 24197 : payload_remain_ -= payload_avail;
1199 24197 : body_avail_ += payload_avail;
1200 24197 : body_total_ += payload_avail;
1201 24197 : if(cb0_.capacity() == 0 && !is_complete)
1202 : {
1203 2 : ec = BOOST_HTTP_ERR(
1204 : error::in_place_overflow);
1205 1 : return;
1206 : }
1207 :
1208 24196 : if(is_complete)
1209 : {
1210 4222 : set_state_to_complete();
1211 4222 : return;
1212 : }
1213 : }
1214 :
1215 19974 : if(m_.payload() == payload::size && got_eof_)
1216 : {
1217 2 : ec = BOOST_HTTP_ERR(
1218 : error::incomplete);
1219 1 : state_ = state::reset;
1220 1 : return;
1221 : }
1222 :
1223 39946 : ec = BOOST_HTTP_ERR(
1224 : error::need_data);
1225 19973 : return;
1226 : }
1227 :
1228 : break;
1229 : }
1230 :
1231 2210 : case state::complete:
1232 2210 : break;
1233 : }
1234 : }
1235 :
1236 : auto
1237 41176 : pull_body() ->
1238 : const_buffers_type
1239 : {
1240 41176 : switch(state_)
1241 : {
1242 0 : case state::header_done:
1243 0 : return {};
1244 41176 : case state::body:
1245 : case state::complete:
1246 41176 : cbp_ = capy::prefix(
1247 41176 : (is_plain() ? cb0_ : cb1_).data(),
1248 : body_avail_);
1249 41176 : return detail::make_span(cbp_);
1250 0 : default:
1251 0 : detail::throw_logic_error();
1252 : }
1253 : }
1254 :
1255 : void
1256 39412 : consume_body(std::size_t n)
1257 : {
1258 39412 : switch(state_)
1259 : {
1260 0 : case state::header_done:
1261 0 : return;
1262 39412 : case state::body:
1263 : case state::complete:
1264 39412 : n = clamp(n, body_avail_);
1265 39412 : (is_plain() ? cb0_ : cb1_).consume(n);
1266 39412 : body_avail_ -= n;
1267 39412 : return;
1268 0 : default:
1269 0 : detail::throw_logic_error();
1270 : }
1271 : }
1272 :
1273 : core::string_view
1274 700 : body() const
1275 : {
1276 : // Precondition violation
1277 700 : if(state_ != state::complete)
1278 0 : detail::throw_logic_error();
1279 :
1280 : // Precondition violation
1281 700 : if(body_avail_ != body_total_)
1282 0 : detail::throw_logic_error();
1283 :
1284 700 : auto cbp = (is_plain() ? cb0_ : cb1_).data();
1285 700 : BOOST_ASSERT(cbp[1].size() == 0);
1286 700 : BOOST_ASSERT(cbp[0].size() == body_avail_);
1287 700 : return core::string_view(
1288 700 : static_cast<char const*>(cbp[0].data()),
1289 1400 : body_avail_);
1290 : }
1291 :
1292 : void
1293 5 : set_body_limit(std::uint64_t n)
1294 : {
1295 5 : switch(state_)
1296 : {
1297 1 : case state::header:
1298 : case state::header_done:
1299 1 : body_limit_ = n;
1300 1 : break;
1301 2 : case state::complete:
1302 : // only allowed for empty bodies
1303 2 : if(body_total_ == 0)
1304 1 : break;
1305 : BOOST_FALLTHROUGH;
1306 : default:
1307 : // set body_limit before parsing the body
1308 3 : detail::throw_logic_error();
1309 : }
1310 2 : }
1311 :
1312 : private:
1313 : bool
1314 138713 : is_plain() const noexcept
1315 : {
1316 277426 : return ! filter_ &&
1317 277426 : m_.payload() != payload::chunked;
1318 : }
1319 :
1320 : std::uint64_t
1321 47587 : body_limit_remain() const noexcept
1322 : {
1323 47587 : return body_limit_ - body_total_;
1324 : }
1325 :
1326 : std::size_t
1327 0 : apply_filter(
1328 : system::error_code& ec,
1329 : std::size_t payload_avail,
1330 : bool more)
1331 : {
1332 0 : std::size_t p0 = payload_avail;
1333 : for(;;)
1334 : {
1335 0 : if(payload_avail == 0 && more)
1336 0 : break;
1337 :
1338 0 : auto f_rs = [&](){
1339 0 : BOOST_ASSERT(filter_ != nullptr);
1340 0 : std::size_t n = clamp(body_limit_remain());
1341 0 : n = clamp(n, cb1_.capacity());
1342 :
1343 0 : return filter_->process(
1344 0 : detail::make_span(cb1_.prepare(n)),
1345 0 : capy::prefix(cb0_.data(), payload_avail),
1346 0 : more);
1347 0 : }();
1348 :
1349 0 : cb0_.consume(f_rs.in_bytes);
1350 0 : payload_avail -= f_rs.in_bytes;
1351 0 : body_total_ += f_rs.out_bytes;
1352 :
1353 : // in_place style
1354 0 : cb1_.commit(f_rs.out_bytes);
1355 0 : body_avail_ += f_rs.out_bytes;
1356 0 : if(cb1_.capacity() == 0 &&
1357 0 : !f_rs.finished && f_rs.in_bytes == 0)
1358 : {
1359 0 : ec = BOOST_HTTP_ERR(
1360 : error::in_place_overflow);
1361 0 : goto done;
1362 : }
1363 :
1364 0 : if(f_rs.ec)
1365 : {
1366 0 : ec = f_rs.ec;
1367 0 : state_ = state::reset;
1368 0 : break;
1369 : }
1370 :
1371 0 : if(body_limit_remain() == 0 &&
1372 0 : !f_rs.finished && f_rs.in_bytes == 0)
1373 : {
1374 0 : ec = BOOST_HTTP_ERR(
1375 : error::body_too_large);
1376 0 : state_ = state::reset;
1377 0 : break;
1378 : }
1379 :
1380 0 : if(f_rs.finished)
1381 : {
1382 0 : if(!more)
1383 0 : state_ = state::complete;
1384 0 : break;
1385 : }
1386 0 : }
1387 :
1388 0 : done:
1389 0 : return p0 - payload_avail;
1390 : }
1391 : };
1392 :
1393 : //------------------------------------------------
1394 : //
1395 : // Special Members
1396 : //
1397 : //------------------------------------------------
1398 :
1399 1704 : parser::
1400 : ~parser()
1401 : {
1402 1704 : delete impl_;
1403 1704 : }
1404 :
1405 12 : parser::
1406 12 : parser() noexcept
1407 12 : : impl_(nullptr)
1408 : {
1409 12 : }
1410 :
1411 3 : parser::
1412 3 : parser(parser&& other) noexcept
1413 3 : : impl_(other.impl_)
1414 : {
1415 3 : other.impl_ = nullptr;
1416 3 : }
1417 :
1418 1689 : parser::
1419 : parser(
1420 : std::shared_ptr<parser_config_impl const> cfg,
1421 1689 : detail::kind k)
1422 1689 : : impl_(new impl(std::move(cfg), k))
1423 : {
1424 : // TODO: use a single allocation for
1425 : // impl and workspace buffer.
1426 1689 : }
1427 :
1428 : void
1429 4 : parser::
1430 : assign(parser&& other) noexcept
1431 : {
1432 4 : if(this == &other)
1433 0 : return;
1434 4 : delete impl_;
1435 4 : impl_ = other.impl_;
1436 4 : other.impl_ = nullptr;
1437 : }
1438 :
1439 : //--------------------------------------------
1440 : //
1441 : // Observers
1442 : //
1443 : //--------------------------------------------
1444 :
1445 : bool
1446 31380 : parser::got_header() const noexcept
1447 : {
1448 31380 : BOOST_ASSERT(impl_);
1449 31380 : return impl_->got_header();
1450 : }
1451 :
1452 : bool
1453 50092 : parser::is_complete() const noexcept
1454 : {
1455 50092 : BOOST_ASSERT(impl_);
1456 50092 : return impl_->is_complete();
1457 : }
1458 :
1459 : //------------------------------------------------
1460 : //
1461 : // Modifiers
1462 : //
1463 : //------------------------------------------------
1464 :
1465 : void
1466 2235 : parser::
1467 : reset() noexcept
1468 : {
1469 2235 : BOOST_ASSERT(impl_);
1470 2235 : impl_->reset();
1471 2235 : }
1472 :
1473 : void
1474 10164 : parser::start()
1475 : {
1476 10164 : BOOST_ASSERT(impl_);
1477 10164 : impl_->start(false);
1478 10159 : }
1479 :
1480 : auto
1481 68721 : parser::
1482 : prepare() ->
1483 : mutable_buffers_type
1484 : {
1485 68721 : BOOST_ASSERT(impl_);
1486 68721 : return impl_->prepare();
1487 : }
1488 :
1489 : void
1490 68128 : parser::
1491 : commit(
1492 : std::size_t n)
1493 : {
1494 68128 : BOOST_ASSERT(impl_);
1495 68128 : impl_->commit(n);
1496 68122 : }
1497 :
1498 : void
1499 132 : parser::
1500 : commit_eof()
1501 : {
1502 132 : BOOST_ASSERT(impl_);
1503 132 : impl_->commit_eof();
1504 129 : }
1505 :
1506 : void
1507 85415 : parser::
1508 : parse(
1509 : system::error_code& ec)
1510 : {
1511 85415 : BOOST_ASSERT(impl_);
1512 85415 : impl_->parse(ec);
1513 85413 : }
1514 :
1515 : auto
1516 41176 : parser::
1517 : pull_body() ->
1518 : const_buffers_type
1519 : {
1520 41176 : BOOST_ASSERT(impl_);
1521 41176 : return impl_->pull_body();
1522 : }
1523 :
1524 : void
1525 39412 : parser::
1526 : consume_body(std::size_t n)
1527 : {
1528 39412 : BOOST_ASSERT(impl_);
1529 39412 : impl_->consume_body(n);
1530 39412 : }
1531 :
1532 : core::string_view
1533 700 : parser::
1534 : body() const
1535 : {
1536 700 : BOOST_ASSERT(impl_);
1537 700 : return impl_->body();
1538 : }
1539 :
1540 : core::string_view
1541 0 : parser::
1542 : release_buffered_data() noexcept
1543 : {
1544 : // TODO
1545 0 : return {};
1546 : }
1547 :
1548 : void
1549 5 : parser::
1550 : set_body_limit(std::uint64_t n)
1551 : {
1552 5 : BOOST_ASSERT(impl_);
1553 5 : impl_->set_body_limit(n);
1554 2 : }
1555 :
1556 : //------------------------------------------------
1557 : //
1558 : // Implementation
1559 : //
1560 : //------------------------------------------------
1561 :
1562 : void
1563 0 : parser::
1564 : start_impl(bool head_response)
1565 : {
1566 0 : BOOST_ASSERT(impl_);
1567 0 : impl_->start(head_response);
1568 0 : }
1569 :
1570 : static_request const&
1571 315 : parser::
1572 : safe_get_request() const
1573 : {
1574 315 : BOOST_ASSERT(impl_);
1575 315 : return impl_->safe_get_request();
1576 : }
1577 :
1578 : static_response const&
1579 3 : parser::
1580 : safe_get_response() const
1581 : {
1582 3 : BOOST_ASSERT(impl_);
1583 3 : return impl_->safe_get_response();
1584 : }
1585 :
1586 : } // http
1587 : } // boost
|