| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- //
- // Copyright (c) 2018 Vinnie Falco (vinnie dot falco at gmail dot com)
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- //
- // Official repository: https://github.com/boostorg/beast
- //
- #ifndef BOOST_BEAST_CORE_IMPL_ICY_STREAM_IPP
- #define BOOST_BEAST_CORE_IMPL_ICY_STREAM_IPP
- #include <boost/beast/core/bind_handler.hpp>
- #include <boost/beast/core/buffers_adapter.hpp>
- #include <boost/beast/core/buffers_prefix.hpp>
- #include <boost/beast/core/buffers_suffix.hpp>
- #include <boost/beast/core/detail/buffers_ref.hpp>
- #include <boost/beast/core/handler_ptr.hpp>
- #include <boost/asio/associated_allocator.hpp>
- #include <boost/asio/associated_executor.hpp>
- #include <boost/asio/buffer.hpp>
- #include <boost/asio/buffers_iterator.hpp>
- #include <boost/asio/coroutine.hpp>
- #include <boost/asio/handler_continuation_hook.hpp>
- #include <boost/asio/handler_invoke_hook.hpp>
- #include <boost/asio/post.hpp>
- #include <boost/asio/read.hpp>
- #include <boost/asio/read_until.hpp>
- #include <boost/assert.hpp>
- #include <boost/throw_exception.hpp>
- #include <algorithm>
- #include <memory>
- #include <utility>
- namespace boost {
- namespace beast {
- namespace http {
- namespace detail {
- template<class DynamicBuffer>
- class dynamic_buffer_ref
- {
- DynamicBuffer& b_;
- public:
- using const_buffers_type =
- typename DynamicBuffer::const_buffers_type;
- using mutable_buffers_type =
- typename DynamicBuffer::mutable_buffers_type;
- dynamic_buffer_ref(dynamic_buffer_ref&&) = default;
- explicit
- dynamic_buffer_ref(DynamicBuffer& b)
- : b_(b)
- {
- }
- std::size_t
- size() const
- {
- return b_.size();
- }
-
- std::size_t
- max_size() const
- {
- return b_.max_size();
- }
- std::size_t
- capacity() const
- {
- return b_.capacity();
- }
- const_buffers_type
- data() const
- {
- return b_.data();
- }
- mutable_buffers_type
- prepare(std::size_t n)
- {
- return b_.prepare(n);
- }
- void
- commit(std::size_t n)
- {
- b_.commit(n);
- }
- void
- consume(std::size_t n)
- {
- b_.consume(n);
- }
- };
- template<class DynamicBuffer>
- typename std::enable_if<
- boost::asio::is_dynamic_buffer<DynamicBuffer>::value,
- dynamic_buffer_ref<DynamicBuffer>>::type
- ref(DynamicBuffer& b)
- {
- return dynamic_buffer_ref<DynamicBuffer>(b);
- }
- template<class MutableBuffers, class ConstBuffers>
- void
- buffer_shift(MutableBuffers const& out, ConstBuffers const& in)
- {
- using boost::asio::buffer_size;
- auto in_pos = boost::asio::buffer_sequence_end(in);
- auto out_pos = boost::asio::buffer_sequence_end(out);
- auto const in_begin = boost::asio::buffer_sequence_begin(in);
- auto const out_begin = boost::asio::buffer_sequence_begin(out);
- BOOST_ASSERT(buffer_size(in) == buffer_size(out));
- if(in_pos == in_begin || out_pos == out_begin)
- return;
- boost::asio::const_buffer cb{*--in_pos};
- boost::asio::mutable_buffer mb{*--out_pos};
- for(;;)
- {
- if(mb.size() >= cb.size())
- {
- std::memmove(
- static_cast<char*>(
- mb.data()) + mb.size() - cb.size(),
- cb.data(),
- cb.size());
- mb = boost::asio::mutable_buffer{
- mb.data(), mb.size() - cb.size()};
- if(in_pos == in_begin)
- break;
- cb = *--in_pos;
- }
- else
- {
- std::memmove(
- mb.data(),
- static_cast<char const*>(
- cb.data()) + cb.size() - mb.size(),
- mb.size());
- cb = boost::asio::const_buffer{
- cb.data(), cb.size() - mb.size()};
- if(out_pos == out_begin)
- break;
- mb = *--out_pos;
- }
- }
- }
- template<class FwdIt>
- class match_icy
- {
- bool& match_;
- public:
- using result_type = std::pair<FwdIt, bool>;
- explicit
- match_icy(bool& b)
- : match_(b)
- {
- }
- result_type
- operator()(FwdIt first, FwdIt last) const
- {
- auto it = first;
- if(it == last)
- return {first, false};
- if(*it != 'I')
- return {last, true};
- if(++it == last)
- return {first, false};
- if(*it != 'C')
- return {last, true};
- if(++it == last)
- return {first, false};
- if(*it != 'Y')
- return {last, true};
- match_ = true;
- return {last, true};
- };
- };
- } // detail
- template<class NextLayer>
- template<class MutableBufferSequence, class Handler>
- class icy_stream<NextLayer>::read_op
- : public boost::asio::coroutine
- {
- using alloc_type = typename
- #if defined(BOOST_NO_CXX11_ALLOCATOR)
- boost::asio::associated_allocator_t<Handler>::template
- rebind<char>::other;
- #else
- std::allocator_traits<boost::asio::associated_allocator_t<Handler>>
- ::template rebind_alloc<char>;
- #endif
- struct data
- {
- icy_stream<NextLayer>& s;
- buffers_adapter<MutableBufferSequence> b;
- bool match = false;
- data(
- Handler const&,
- icy_stream<NextLayer>& s_,
- MutableBufferSequence const& b_)
- : s(s_)
- , b(b_)
- {
- }
- };
- handler_ptr<data, Handler> d_;
- public:
- read_op(read_op&&) = default;
- read_op(read_op const&) = delete;
- template<class DeducedHandler, class... Args>
- read_op(
- DeducedHandler&& h,
- icy_stream<NextLayer>& s,
- MutableBufferSequence const& b)
- : d_(std::forward<DeducedHandler>(h), s, b)
- {
- }
- using allocator_type =
- boost::asio::associated_allocator_t<Handler>;
- allocator_type
- get_allocator() const noexcept
- {
- return (boost::asio::get_associated_allocator)(d_.handler());
- }
- using executor_type = boost::asio::associated_executor_t<
- Handler, decltype(std::declval<NextLayer&>().get_executor())>;
- executor_type
- get_executor() const noexcept
- {
- return (boost::asio::get_associated_executor)(
- d_.handler(), d_->s.get_executor());
- }
- void
- operator()(
- boost::system::error_code ec,
- std::size_t bytes_transferred);
- template<class Function>
- friend
- void asio_handler_invoke(Function&& f, read_op* op)
- {
- using boost::asio::asio_handler_invoke;
- asio_handler_invoke(f, std::addressof(op->d_.handler()));
- }
- };
- template<class NextLayer>
- template<class MutableBufferSequence, class Handler>
- void
- icy_stream<NextLayer>::
- read_op<MutableBufferSequence, Handler>::
- operator()(
- error_code ec,
- std::size_t bytes_transferred)
- {
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- using iterator = boost::asio::buffers_iterator<
- typename detail::dynamic_buffer_ref<
- buffers_adapter<MutableBufferSequence>>::const_buffers_type>;
- auto& d = *d_;
- BOOST_ASIO_CORO_REENTER(*this)
- {
- if(d.b.max_size() == 0)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::post(d.s.get_executor(),
- bind_handler(std::move(*this), ec, 0));
- goto upcall;
- }
- if(! d.s.detect_)
- {
- if(d.s.copy_ > 0)
- {
- auto const n = buffer_copy(
- d.b.prepare(std::min<std::size_t>(
- d.s.copy_, d.b.max_size())),
- boost::asio::buffer(d.s.buf_));
- d.b.commit(n);
- d.s.copy_ = static_cast<unsigned char>(
- d.s.copy_ - n);
- if(d.s.copy_ > 0)
- std::memmove(
- d.s.buf_,
- &d.s.buf_[n],
- d.s.copy_);
- }
- if(d.b.size() < d.b.max_size())
- {
- BOOST_ASIO_CORO_YIELD
- d.s.next_layer().async_read_some(
- d.b.prepare(d.b.max_size() - d.b.size()),
- std::move(*this));
- d.b.commit(bytes_transferred);
- }
- bytes_transferred = d.b.size();
- goto upcall;
- }
- d.s.detect_ = false;
- if(d.b.max_size() < 8)
- {
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_read(
- d.s.next_layer(),
- boost::asio::buffer(d.s.buf_, 3),
- std::move(*this));
- if(ec)
- goto upcall;
- auto n = bytes_transferred;
- BOOST_ASSERT(n == 3);
- if(
- d.s.buf_[0] != 'I' ||
- d.s.buf_[1] != 'C' ||
- d.s.buf_[2] != 'Y')
- {
- buffer_copy(
- d.b.value(),
- boost::asio::buffer(d.s.buf_, n));
- if(d.b.max_size() < 3)
- {
- d.s.copy_ = static_cast<unsigned char>(
- 3 - d.b.max_size());
- std::memmove(
- d.s.buf_,
- &d.s.buf_[d.b.max_size()],
- d.s.copy_);
- }
- bytes_transferred = (std::min)(
- n, d.b.max_size());
- goto upcall;
- }
- d.s.copy_ = static_cast<unsigned char>(
- buffer_copy(
- boost::asio::buffer(d.s.buf_),
- icy_stream::version() + d.b.max_size()));
- bytes_transferred = buffer_copy(
- d.b.value(),
- icy_stream::version());
- goto upcall;
- }
- BOOST_ASIO_CORO_YIELD
- boost::asio::async_read_until(
- d.s.next_layer(),
- detail::ref(d.b),
- detail::match_icy<iterator>(d.match),
- std::move(*this));
- if(ec)
- goto upcall;
- {
- auto n = bytes_transferred;
- BOOST_ASSERT(n == d.b.size());
- if(! d.match)
- goto upcall;
- if(d.b.size() + 5 > d.b.max_size())
- {
- d.s.copy_ = static_cast<unsigned char>(
- n + 5 - d.b.max_size());
- std::copy(
- boost::asio::buffers_begin(d.b.value()) + n - d.s.copy_,
- boost::asio::buffers_begin(d.b.value()) + n,
- d.s.buf_);
- n = d.b.max_size() - 5;
- }
- {
- buffers_suffix<beast::detail::buffers_ref<
- MutableBufferSequence>> dest(
- boost::in_place_init, d.b.value());
- dest.consume(5);
- detail::buffer_shift(
- buffers_prefix(n, dest),
- buffers_prefix(n, d.b.value()));
- buffer_copy(d.b.value(), icy_stream::version());
- n += 5;
- bytes_transferred = n;
- }
- }
- upcall:
- d_.invoke(ec, bytes_transferred);
- }
- }
- //------------------------------------------------------------------------------
- template<class NextLayer>
- template<class... Args>
- icy_stream<NextLayer>::
- icy_stream(Args&&... args)
- : stream_(std::forward<Args>(args)...)
- {
- }
- template<class NextLayer>
- template<class MutableBufferSequence>
- std::size_t
- icy_stream<NextLayer>::
- read_some(MutableBufferSequence const& buffers)
- {
- static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
- "SyncReadStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- error_code ec;
- auto n = read_some(buffers, ec);
- if(ec)
- BOOST_THROW_EXCEPTION(boost::system::system_error{ec});
- return n;
- }
- template<class NextLayer>
- template<class MutableBufferSequence>
- std::size_t
- icy_stream<NextLayer>::
- read_some(MutableBufferSequence const& buffers, error_code& ec)
- {
- static_assert(boost::beast::is_sync_read_stream<next_layer_type>::value,
- "SyncReadStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- using boost::asio::buffer_copy;
- using boost::asio::buffer_size;
- using iterator = boost::asio::buffers_iterator<
- typename detail::dynamic_buffer_ref<
- buffers_adapter<MutableBufferSequence>>::const_buffers_type>;
- buffers_adapter<MutableBufferSequence> b(buffers);
- if(b.max_size() == 0)
- {
- ec.assign(0, ec.category());
- return 0;
- }
- if(! detect_)
- {
- if(copy_ > 0)
- {
- auto const n = buffer_copy(
- b.prepare(std::min<std::size_t>(
- copy_, b.max_size())),
- boost::asio::buffer(buf_));
- b.commit(n);
- copy_ = static_cast<unsigned char>(
- copy_ - n);
- if(copy_ > 0)
- std::memmove(
- buf_,
- &buf_[n],
- copy_);
- }
- if(b.size() < b.max_size())
- b.commit(stream_.read_some(
- b.prepare(b.max_size() - b.size()), ec));
- return b.size();
- }
- detect_ = false;
- if(b.max_size() < 8)
- {
- auto n = boost::asio::read(
- stream_,
- boost::asio::buffer(buf_, 3),
- ec);
- if(ec)
- return 0;
- BOOST_ASSERT(n == 3);
- if(
- buf_[0] != 'I' ||
- buf_[1] != 'C' ||
- buf_[2] != 'Y')
- {
- buffer_copy(
- buffers,
- boost::asio::buffer(buf_, n));
- if(b.max_size() < 3)
- {
- copy_ = static_cast<unsigned char>(
- 3 - b.max_size());
- std::memmove(
- buf_,
- &buf_[b.max_size()],
- copy_);
- }
- return (std::min)(n, b.max_size());
- }
- copy_ = static_cast<unsigned char>(
- buffer_copy(
- boost::asio::buffer(buf_),
- version() + b.max_size()));
- return buffer_copy(
- buffers,
- version());
- }
- bool match = false;
- auto n = boost::asio::read_until(
- stream_,
- detail::ref(b),
- detail::match_icy<iterator>(match),
- ec);
- if(ec)
- return n;
- BOOST_ASSERT(n == b.size());
- if(! match)
- return n;
- if(b.size() + 5 > b.max_size())
- {
- copy_ = static_cast<unsigned char>(
- n + 5 - b.max_size());
- std::copy(
- boost::asio::buffers_begin(buffers) + n - copy_,
- boost::asio::buffers_begin(buffers) + n,
- buf_);
- n = b.max_size() - 5;
- }
- buffers_suffix<beast::detail::buffers_ref<
- MutableBufferSequence>> dest(
- boost::in_place_init, buffers);
- dest.consume(5);
- detail::buffer_shift(
- buffers_prefix(n, dest),
- buffers_prefix(n, buffers));
- buffer_copy(buffers, version());
- n += 5;
- return n;
- }
- template<class NextLayer>
- template<
- class MutableBufferSequence,
- class ReadHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- ReadHandler, void(error_code, std::size_t))
- icy_stream<NextLayer>::
- async_read_some(
- MutableBufferSequence const& buffers,
- ReadHandler&& handler)
- {
- static_assert(boost::beast::is_async_read_stream<next_layer_type>::value,
- "AsyncReadStream requirements not met");
- static_assert(boost::asio::is_mutable_buffer_sequence<
- MutableBufferSequence >::value,
- "MutableBufferSequence requirements not met");
- BOOST_BEAST_HANDLER_INIT(
- ReadHandler, void(error_code, std::size_t));
- read_op<
- MutableBufferSequence,
- BOOST_ASIO_HANDLER_TYPE(
- ReadHandler, void(error_code, std::size_t))>{
- std::move(init.completion_handler), *this, buffers}(
- {}, 0);
- return init.result.get();
- }
- template<class NextLayer>
- template<class MutableBufferSequence>
- std::size_t
- icy_stream<NextLayer>::
- write_some(MutableBufferSequence const& buffers)
- {
- static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
- "SyncWriteStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- return stream_.write_some(buffers);
- }
- template<class NextLayer>
- template<class MutableBufferSequence>
- std::size_t
- icy_stream<NextLayer>::
- write_some(MutableBufferSequence const& buffers, error_code& ec)
- {
- static_assert(boost::beast::is_sync_write_stream<next_layer_type>::value,
- "SyncWriteStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- return stream_.write_some(buffers, ec);
- }
- template<class NextLayer>
- template<
- class MutableBufferSequence,
- class WriteHandler>
- BOOST_ASIO_INITFN_RESULT_TYPE(
- WriteHandler, void(error_code, std::size_t))
- icy_stream<NextLayer>::
- async_write_some(
- MutableBufferSequence const& buffers,
- WriteHandler&& handler)
- {
- static_assert(boost::beast::is_async_write_stream<next_layer_type>::value,
- "AsyncWriteStream requirements not met");
- static_assert(boost::asio::is_const_buffer_sequence<
- MutableBufferSequence>::value,
- "MutableBufferSequence requirements not met");
- return stream_.async_write_some(buffers, std::forward<WriteHandler>(handler));
- }
- } // http
- } // beast
- } // boost
- #endif
|