// // Copyright (c) 2016-2019 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_HPP #define BOOST_BEAST_CORE_IMPL_ICY_STREAM_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace http { namespace detail { template boost::tribool is_icy(ConstBufferSequence const& buffers) { char buf[3]; auto const n = net::buffer_copy( net::mutable_buffer(buf, 3), buffers); if(n >= 1 && buf[0] != 'I') return false; if(n >= 2 && buf[1] != 'C') return false; if(n >= 3 && buf[2] != 'Y') return false; if(n < 3) return boost::indeterminate; return true; } } // detail template struct icy_stream::ops { template class read_op : public beast::async_base> , public asio::coroutine { icy_stream& s_; Buffers b_; std::size_t n_ = 0; error_code ec_; bool match_ = false; public: template read_op( Handler_&& h, icy_stream& s, Buffers const& b) : async_base>( std::forward(h), s.get_executor()) , s_(s) , b_(b) { (*this)({}, 0, false); } void operator()( error_code ec, std::size_t bytes_transferred, bool cont = true) { BOOST_ASIO_CORO_REENTER(*this) { if(s_.detect_) { BOOST_ASSERT(s_.n_ == 0); for(;;) { // Try to read the first three characters BOOST_ASIO_CORO_YIELD s_.next_layer().async_read_some( net::mutable_buffer( s_.buf_ + s_.n_, 3 - s_.n_), std::move(*this)); s_.n_ += static_cast(bytes_transferred); if(ec) goto upcall; auto result = detail::is_icy( net::const_buffer(s_.buf_, s_.n_)); if(boost::indeterminate(result)) continue; if(result) s_.n_ = static_cast(net::buffer_copy( net::buffer(s_.buf_, sizeof(s_.buf_)), icy_stream::version())); break; } s_.detect_ = false; } if(s_.n_ > 0) { bytes_transferred = net::buffer_copy( b_, net::const_buffer(s_.buf_, s_.n_)); s_.n_ -= static_cast(bytes_transferred); std::memmove( s_.buf_, s_.buf_ + bytes_transferred, sizeof(s_.buf_) - bytes_transferred); } else { BOOST_ASIO_CORO_YIELD s_.next_layer().async_read_some( b_, std::move(*this)); } upcall: if(! cont) { ec_ = ec; n_ = bytes_transferred; BOOST_ASIO_CORO_YIELD s_.next_layer().async_read_some( net::mutable_buffer{}, std::move(*this)); ec = ec_; bytes_transferred = n_; } this->complete_now(ec, bytes_transferred); } } }; struct run_read_op { template void operator()( ReadHandler&& h, icy_stream* s, Buffers const& b) { // If you get an error on the following line it means // that your handler does not meet the documented type // requirements for the handler. static_assert( beast::detail::is_invocable::value, "ReadHandler type requirements not met"); read_op< Buffers, typename std::decay::type>( std::forward(h), *s, b); } }; }; //------------------------------------------------------------------------------ template template icy_stream:: icy_stream(Args&&... args) : stream_(std::forward(args)...) { std::memset(buf_, 0, sizeof(buf_)); } template template std::size_t icy_stream:: read_some(MutableBufferSequence const& buffers) { static_assert(is_sync_read_stream::value, "SyncReadStream type requirements not met"); static_assert(net::is_mutable_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence type requirements not met"); error_code ec; auto n = read_some(buffers, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return n; } template template std::size_t icy_stream:: read_some(MutableBufferSequence const& buffers, error_code& ec) { static_assert(is_sync_read_stream::value, "SyncReadStream type requirements not met"); static_assert(net::is_mutable_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence type requirements not met"); std::size_t bytes_transferred; if(detect_) { BOOST_ASSERT(n_ == 0); for(;;) { // Try to read the first three characters bytes_transferred = next_layer().read_some( net::mutable_buffer(buf_ + n_, 3 - n_), ec); n_ += static_cast(bytes_transferred); if(ec) return 0; auto result = detail::is_icy( net::const_buffer(buf_, n_)); if(boost::indeterminate(result)) continue; if(result) n_ = static_cast(net::buffer_copy( net::buffer(buf_, sizeof(buf_)), icy_stream::version())); break; } detect_ = false; } if(n_ > 0) { bytes_transferred = net::buffer_copy( buffers, net::const_buffer(buf_, n_)); n_ -= static_cast(bytes_transferred); std::memmove( buf_, buf_ + bytes_transferred, sizeof(buf_) - bytes_transferred); } else { bytes_transferred = next_layer().read_some(buffers, ec); } return bytes_transferred; } template template< class MutableBufferSequence, class ReadHandler> BOOST_BEAST_ASYNC_RESULT2(ReadHandler) icy_stream:: async_read_some( MutableBufferSequence const& buffers, ReadHandler&& handler) { static_assert(is_async_read_stream::value, "AsyncReadStream type requirements not met"); static_assert(net::is_mutable_buffer_sequence< MutableBufferSequence >::value, "MutableBufferSequence type requirements not met"); return net::async_initiate< ReadHandler, void(error_code, std::size_t)>( typename ops::run_read_op{}, handler, this, buffers); } template template std::size_t icy_stream:: write_some(MutableBufferSequence const& buffers) { static_assert(is_sync_write_stream::value, "SyncWriteStream type requirements not met"); static_assert(net::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence type requirements not met"); return stream_.write_some(buffers); } template template std::size_t icy_stream:: write_some(MutableBufferSequence const& buffers, error_code& ec) { static_assert(is_sync_write_stream::value, "SyncWriteStream type requirements not met"); static_assert(net::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence type requirements not met"); return stream_.write_some(buffers, ec); } template template< class MutableBufferSequence, class WriteHandler> BOOST_BEAST_ASYNC_RESULT2(WriteHandler) icy_stream:: async_write_some( MutableBufferSequence const& buffers, WriteHandler&& handler) { static_assert(is_async_write_stream::value, "AsyncWriteStream type requirements not met"); static_assert(net::is_const_buffer_sequence< MutableBufferSequence>::value, "MutableBufferSequence type requirements not met"); return stream_.async_write_some( buffers, std::forward(handler)); } } // http } // beast } // boost #endif