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