// // Copyright (c) 2016-2017 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_HTTP_IMPL_WRITE_IPP #define BOOST_BEAST_HTTP_IMPL_WRITE_IPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace http { namespace detail { template< class Stream, class Handler, bool isRequest, class Body, class Fields> class write_some_op { Stream& s_; serializer& sr_; Handler h_; class lambda { write_some_op& op_; public: bool invoked = false; explicit lambda(write_some_op& op) : op_(op) { } template void operator()(error_code& ec, ConstBufferSequence const& buffers) { invoked = true; ec.assign(0, ec.category()); return op_.s_.async_write_some( buffers, std::move(op_)); } }; public: write_some_op(write_some_op&&) = default; write_some_op(write_some_op const&) = default; template write_some_op(DeducedHandler&& h, Stream& s, serializer& sr) : s_(s) , sr_(sr) , h_(std::forward(h)) { } using allocator_type = boost::asio::associated_allocator_t; allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(h_); } using executor_type = boost::asio::associated_executor_t< Handler, decltype(std::declval().get_executor())>; executor_type get_executor() const noexcept { return boost::asio::get_associated_executor( h_, s_.get_executor()); } void operator()(); void operator()( error_code ec, std::size_t bytes_transferred); friend bool asio_handler_is_continuation(write_some_op* op) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation( std::addressof(op->h_)); } }; template< class Stream, class Handler, bool isRequest, class Body, class Fields> void write_some_op< Stream, Handler, isRequest, Body, Fields>:: operator()() { error_code ec; if(! sr_.is_done()) { lambda f{*this}; sr_.next(ec, f); if(ec) { BOOST_ASSERT(! f.invoked); return boost::asio::post( s_.get_executor(), bind_handler(std::move(*this), ec, 0)); } if(f.invoked) { // *this has been moved from, // cannot access members here. return; } // What else could it be? BOOST_ASSERT(sr_.is_done()); } return boost::asio::post( s_.get_executor(), bind_handler(std::move(*this), ec, 0)); } template< class Stream, class Handler, bool isRequest, class Body, class Fields> void write_some_op< Stream, Handler, isRequest, Body, Fields>:: operator()( error_code ec, std::size_t bytes_transferred) { if(! ec) sr_.consume(bytes_transferred); h_(ec, bytes_transferred); } //------------------------------------------------------------------------------ struct serializer_is_header_done { template< bool isRequest, class Body, class Fields> bool operator()( serializer& sr) const { return sr.is_header_done(); } }; struct serializer_is_done { template< bool isRequest, class Body, class Fields> bool operator()( serializer& sr) const { return sr.is_done(); } }; //------------------------------------------------------------------------------ template< class Stream, class Handler, class Predicate, bool isRequest, class Body, class Fields> class write_op { int state_ = 0; Stream& s_; serializer& sr_; std::size_t bytes_transferred_ = 0; Handler h_; public: write_op(write_op&&) = default; write_op(write_op const&) = default; template write_op(DeducedHandler&& h, Stream& s, serializer& sr) : s_(s) , sr_(sr) , h_(std::forward(h)) { } using allocator_type = boost::asio::associated_allocator_t; allocator_type get_allocator() const noexcept { return boost::asio::get_associated_allocator(h_); } using executor_type = boost::asio::associated_executor_t< Handler, decltype(std::declval().get_executor())>; executor_type get_executor() const noexcept { return boost::asio::get_associated_executor( h_, s_.get_executor()); } void operator()( error_code ec = {}, std::size_t bytes_transferred = 0); friend bool asio_handler_is_continuation(write_op* op) { using boost::asio::asio_handler_is_continuation; return op->state_ >= 3 || asio_handler_is_continuation( std::addressof(op->h_)); } }; template< class Stream, class Handler, class Predicate, bool isRequest, class Body, class Fields> void write_op:: operator()( error_code ec, std::size_t bytes_transferred) { if(ec) goto upcall; switch(state_) { case 0: { if(Predicate{}(sr_)) { state_ = 1; return boost::asio::post( s_.get_executor(), bind_handler(std::move(*this), ec, 0)); } state_ = 2; return beast::http::async_write_some( s_, sr_, std::move(*this)); } case 1: goto upcall; case 2: state_ = 3; BOOST_BEAST_FALLTHROUGH; case 3: { bytes_transferred_ += bytes_transferred; if(Predicate{}(sr_)) goto upcall; return beast::http::async_write_some( s_, sr_, std::move(*this)); } } upcall: h_(ec, bytes_transferred_); } //------------------------------------------------------------------------------ template class write_msg_op { struct data { Stream& s; serializer sr; data(Handler&, Stream& s_, message< isRequest, Body, Fields>& m_) : s(s_) , sr(m_) { } }; handler_ptr d_; public: write_msg_op(write_msg_op&&) = default; write_msg_op(write_msg_op const&) = default; template write_msg_op(DeducedHandler&& h, Stream& s, Args&&... args) : d_(std::forward(h), s, std::forward(args)...) { } using allocator_type = boost::asio::associated_allocator_t; 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().get_executor())>; executor_type get_executor() const noexcept { return boost::asio::get_associated_executor( d_.handler(), d_->s.get_executor()); } void operator()(); void operator()( error_code ec, std::size_t bytes_transferred); friend bool asio_handler_is_continuation(write_msg_op* op) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation( std::addressof(op->d_.handler())); } }; template void write_msg_op< Stream, Handler, isRequest, Body, Fields>:: operator()() { auto& d = *d_; return async_write(d.s, d.sr, std::move(*this)); } template void write_msg_op< Stream, Handler, isRequest, Body, Fields>:: operator()(error_code ec, std::size_t bytes_transferred) { d_.invoke(ec, bytes_transferred); } //------------------------------------------------------------------------------ template class write_some_lambda { Stream& stream_; public: bool invoked = false; std::size_t bytes_transferred = 0; explicit write_some_lambda(Stream& stream) : stream_(stream) { } template void operator()(error_code& ec, ConstBufferSequence const& buffers) { invoked = true; bytes_transferred = stream_.write_some(buffers, ec); } }; template class write_lambda { Stream& stream_; public: bool invoked = false; std::size_t bytes_transferred = 0; explicit write_lambda(Stream& stream) : stream_(stream) { } template void operator()(error_code& ec, ConstBufferSequence const& buffers) { invoked = true; bytes_transferred = boost::asio::write( stream_, buffers, ec); } }; template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write_some( SyncWriteStream& stream, serializer& sr, error_code& ec) { if(! sr.is_done()) { write_some_lambda f{stream}; sr.next(ec, f); if(ec) return f.bytes_transferred; if(f.invoked) sr.consume(f.bytes_transferred); return f.bytes_transferred; } ec.assign(0, ec.category()); return 0; } template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write_some( AsyncWriteStream& stream, serializer& sr, WriteHandler&& handler) { boost::asio::async_completion< WriteHandler, void(error_code, std::size_t)> init{handler}; detail::write_some_op< AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), isRequest, Body, Fields>{ init.completion_handler, stream, sr}(); return init.result.get(); } } // detail //------------------------------------------------------------------------------ template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write_some( SyncWriteStream& stream, serializer& sr) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); error_code ec; auto const bytes_transferred = write_some(stream, sr, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; } template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write_some( SyncWriteStream& stream, serializer& sr, error_code& ec) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); return detail::write_some(stream, sr, ec); } template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write_some( AsyncWriteStream& stream, serializer& sr, WriteHandler&& handler) { static_assert(is_async_write_stream< AsyncWriteStream>::value, "AsyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); return detail::async_write_some(stream, sr, std::forward(handler)); } //------------------------------------------------------------------------------ template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write_header(SyncWriteStream& stream, serializer& sr) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); error_code ec; auto const bytes_transferred = write_header(stream, sr, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; } template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write_header( SyncWriteStream& stream, serializer& sr, error_code& ec) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); sr.split(true); std::size_t bytes_transferred = 0; if(! sr.is_header_done()) { detail::write_lambda f{stream}; do { sr.next(ec, f); bytes_transferred += f.bytes_transferred; if(ec) return bytes_transferred; BOOST_ASSERT(f.invoked); sr.consume(f.bytes_transferred); } while(! sr.is_header_done()); } else { ec.assign(0, ec.category()); } return bytes_transferred; } template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write_header( AsyncWriteStream& stream, serializer& sr, WriteHandler&& handler) { static_assert(is_async_write_stream< AsyncWriteStream>::value, "AsyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); sr.split(true); boost::asio::async_completion< WriteHandler, void(error_code, std::size_t)> init{handler}; detail::write_op< AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), detail::serializer_is_header_done, isRequest, Body, Fields>{ init.completion_handler, stream, sr}(); return init.result.get(); } //------------------------------------------------------------------------------ template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write( SyncWriteStream& stream, serializer& sr) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); error_code ec; auto const bytes_transferred = write(stream, sr, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; } template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write( SyncWriteStream& stream, serializer& sr, error_code& ec) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); std::size_t bytes_transferred = 0; sr.split(false); for(;;) { bytes_transferred += write_some(stream, sr, ec); if(ec) return bytes_transferred; if(sr.is_done()) break; } return bytes_transferred; } template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write( AsyncWriteStream& stream, serializer& sr, WriteHandler&& handler) { static_assert(is_async_write_stream< AsyncWriteStream>::value, "AsyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); sr.split(false); boost::asio::async_completion< WriteHandler, void(error_code, std::size_t)> init{handler}; detail::write_op< AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), detail::serializer_is_done, isRequest, Body, Fields>{ init.completion_handler, stream, sr}(); return init.result.get(); } //------------------------------------------------------------------------------ template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write( SyncWriteStream& stream, message const& msg) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); error_code ec; auto const bytes_transferred = write(stream, msg, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); return bytes_transferred; } template< class SyncWriteStream, bool isRequest, class Body, class Fields> std::size_t write( SyncWriteStream& stream, message const& msg, error_code& ec) { static_assert(is_sync_write_stream::value, "SyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); serializer sr{msg}; return write(stream, sr, ec); } template< class AsyncWriteStream, bool isRequest, class Body, class Fields, class WriteHandler> BOOST_ASIO_INITFN_RESULT_TYPE( WriteHandler, void(error_code, std::size_t)) async_write( AsyncWriteStream& stream, message& msg, WriteHandler&& handler) { static_assert( is_async_write_stream::value, "AsyncWriteStream requirements not met"); static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); boost::asio::async_completion< WriteHandler, void(error_code, std::size_t)> init{handler}; detail::write_msg_op< AsyncWriteStream, BOOST_ASIO_HANDLER_TYPE(WriteHandler, void(error_code, std::size_t)), isRequest, Body, Fields>{ init.completion_handler, stream, msg}(); return init.result.get(); } //------------------------------------------------------------------------------ namespace detail { template class write_ostream_lambda { std::ostream& os_; Serializer& sr_; public: write_ostream_lambda(std::ostream& os, Serializer& sr) : os_(os) , sr_(sr) { } template void operator()(error_code& ec, ConstBufferSequence const& buffers) const { ec.assign(0, ec.category()); if(os_.fail()) return; std::size_t bytes_transferred = 0; for(auto b : buffers_range(buffers)) { os_.write(reinterpret_cast( b.data()), b.size()); if(os_.fail()) return; bytes_transferred += b.size(); } sr_.consume(bytes_transferred); } }; } // detail template std::ostream& operator<<(std::ostream& os, header const& h) { typename Fields::writer fr{ h, h.version(), h.method()}; return os << buffers(fr.get()); } template std::ostream& operator<<(std::ostream& os, header const& h) { typename Fields::writer fr{ h, h.version(), h.result_int()}; return os << buffers(fr.get()); } template std::ostream& operator<<(std::ostream& os, message const& msg) { static_assert(is_body::value, "Body requirements not met"); static_assert(is_body_writer::value, "BodyWriter requirements not met"); serializer sr{msg}; error_code ec; detail::write_ostream_lambda f{os, sr}; do { sr.next(ec, f); if(os.fail()) break; if(ec) { os.setstate(std::ios::failbit); break; } } while(! sr.is_done()); return os; } } // http } // beast } // boost #endif