boost/beast/websocket/impl/accept.ipp
// // 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_WEBSOCKET_IMPL_ACCEPT_IPP #define BOOST_BEAST_WEBSOCKET_IMPL_ACCEPT_IPP #include <boost/beast/websocket/detail/type_traits.hpp> #include <boost/beast/http/empty_body.hpp> #include <boost/beast/http/parser.hpp> #include <boost/beast/http/read.hpp> #include <boost/beast/http/string_body.hpp> #include <boost/beast/http/write.hpp> #include <boost/beast/core/buffers_prefix.hpp> #include <boost/beast/core/handler_ptr.hpp> #include <boost/beast/core/detail/type_traits.hpp> #include <boost/asio/coroutine.hpp> #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> #include <boost/assert.hpp> #include <boost/throw_exception.hpp> #include <memory> #include <type_traits> namespace boost { namespace beast { namespace websocket { // Respond to an upgrade HTTP request template<class NextLayer, bool deflateSupported> template<class Handler> class stream<NextLayer, deflateSupported>::response_op : public boost::asio::coroutine { struct data { stream<NextLayer, deflateSupported>& ws; error_code result; response_type res; template<class Body, class Allocator, class Decorator> data( Handler const&, stream<NextLayer, deflateSupported>& ws_, http::request<Body, http::basic_fields<Allocator>> const& req, Decorator const& decorator) : ws(ws_) , res(ws_.build_response(req, decorator, result)) { } }; handler_ptr<data, Handler> d_; public: response_op(response_op&&) = default; response_op(response_op const&) = delete; template<class DeducedHandler, class... Args> response_op(DeducedHandler&& h, stream<NextLayer, deflateSupported>& ws, Args&&... args) : d_(std::forward<DeducedHandler>(h), ws, std::forward<Args>(args)...) { } 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< stream<NextLayer, deflateSupported>&>().get_executor())>; executor_type get_executor() const noexcept { return (boost::asio::get_associated_executor)( d_.handler(), d_->ws.get_executor()); } void operator()( error_code ec = {}, std::size_t bytes_transferred = 0); friend bool asio_handler_is_continuation(response_op* op) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation( std::addressof(op->d_.handler())); } template<class Function> friend void asio_handler_invoke(Function&& f, response_op* op) { using boost::asio::asio_handler_invoke; asio_handler_invoke(f, std::addressof(op->d_.handler())); } }; template<class NextLayer, bool deflateSupported> template<class Handler> void stream<NextLayer, deflateSupported>:: response_op<Handler>:: operator()( error_code ec, std::size_t) { auto& d = *d_; BOOST_ASIO_CORO_REENTER(*this) { // Send response BOOST_ASIO_CORO_YIELD http::async_write(d.ws.next_layer(), d.res, std::move(*this)); if(! ec) ec = d.result; if(! ec) { d.ws.do_pmd_config(d.res, is_deflate_supported{}); d.ws.open(role_type::server); } d_.invoke(ec); } } //------------------------------------------------------------------------------ // read and respond to an upgrade request // template<class NextLayer, bool deflateSupported> template<class Decorator, class Handler> class stream<NextLayer, deflateSupported>::accept_op : public boost::asio::coroutine { struct data { stream<NextLayer, deflateSupported>& ws; Decorator decorator; http::request_parser<http::empty_body> p; data( Handler const&, stream<NextLayer, deflateSupported>& ws_, Decorator const& decorator_) : ws(ws_) , decorator(decorator_) { } }; handler_ptr<data, Handler> d_; public: accept_op(accept_op&&) = default; accept_op(accept_op const&) = delete; template<class DeducedHandler, class... Args> accept_op(DeducedHandler&& h, stream<NextLayer, deflateSupported>& ws, Args&&... args) : d_(std::forward<DeducedHandler>(h), ws, std::forward<Args>(args)...) { } 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<stream<NextLayer, deflateSupported>&>().get_executor())>; executor_type get_executor() const noexcept { return (boost::asio::get_associated_executor)( d_.handler(), d_->ws.get_executor()); } template<class Buffers> void run(Buffers const& buffers); void operator()( error_code ec = {}, std::size_t bytes_used = 0); friend bool asio_handler_is_continuation(accept_op* op) { using boost::asio::asio_handler_is_continuation; return asio_handler_is_continuation( std::addressof(op->d_.handler())); } template<class Function> friend void asio_handler_invoke(Function&& f, accept_op* op) { using boost::asio::asio_handler_invoke; asio_handler_invoke(f, std::addressof(op->d_.handler())); } }; template<class NextLayer, bool deflateSupported> template<class Decorator, class Handler> template<class Buffers> void stream<NextLayer, deflateSupported>:: accept_op<Decorator, Handler>:: run(Buffers const& buffers) { using boost::asio::buffer_copy; using boost::asio::buffer_size; auto& d = *d_; error_code ec; boost::optional<typename static_buffer_base::mutable_buffers_type> mb; auto const len = buffer_size(buffers); try { mb.emplace(d.ws.rd_buf_.prepare(len)); } catch(std::length_error const&) { ec = error::buffer_overflow; return (*this)(ec); } d.ws.rd_buf_.commit( buffer_copy(*mb, buffers)); (*this)(ec); } template<class NextLayer, bool deflateSupported> template<class Decorator, class Handler> void stream<NextLayer, deflateSupported>:: accept_op<Decorator, Handler>:: operator()(error_code ec, std::size_t) { auto& d = *d_; BOOST_ASIO_CORO_REENTER(*this) { if(ec) { BOOST_ASIO_CORO_YIELD boost::asio::post( d.ws.get_executor(), bind_handler(std::move(*this), ec)); } else { BOOST_ASIO_CORO_YIELD http::async_read( d.ws.next_layer(), d.ws.rd_buf_, d.p, std::move(*this)); if(ec == http::error::end_of_stream) ec = error::closed; if(! ec) { // Arguments from our state must be // moved to the stack before releasing // the handler. auto& ws = d.ws; auto const req = d.p.release(); auto const decorator = d.decorator; #if 1 return response_op<Handler>{ d_.release_handler(), ws, req, decorator}(ec); #else // VFALCO This *should* work but breaks // coroutine invariants in the unit test. // Also it calls reset() when it shouldn't. return ws.async_accept_ex( req, decorator, d_.release_handler()); #endif } } d_.invoke(ec); } } //------------------------------------------------------------------------------ template<class NextLayer, bool deflateSupported> void stream<NextLayer, deflateSupported>:: accept() { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); error_code ec; accept(ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> template<class ResponseDecorator> void stream<NextLayer, deflateSupported>:: accept_ex(ResponseDecorator const& decorator) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> void stream<NextLayer, deflateSupported>:: accept(error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); reset(); do_accept(&default_decorate_res, ec); } template<class NextLayer, bool deflateSupported> template<class ResponseDecorator> void stream<NextLayer, deflateSupported>:: accept_ex(ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); reset(); do_accept(decorator, ec); } template<class NextLayer, bool deflateSupported> template<class ConstBufferSequence> typename std::enable_if<! http::detail::is_header< ConstBufferSequence>::value>::type stream<NextLayer, deflateSupported>:: accept(ConstBufferSequence const& buffers) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); error_code ec; accept(buffers, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> template< class ConstBufferSequence, class ResponseDecorator> typename std::enable_if<! http::detail::is_header< ConstBufferSequence>::value>::type stream<NextLayer, deflateSupported>:: accept_ex( ConstBufferSequence const& buffers, ResponseDecorator const &decorator) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(buffers, decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> template<class ConstBufferSequence> typename std::enable_if<! http::detail::is_header< ConstBufferSequence>::value>::type stream<NextLayer, deflateSupported>:: accept( ConstBufferSequence const& buffers, error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); using boost::asio::buffer_copy; using boost::asio::buffer_size; reset(); boost::optional<typename static_buffer_base::mutable_buffers_type> mb; try { mb.emplace(rd_buf_.prepare( buffer_size(buffers))); } catch(std::length_error const&) { ec = error::buffer_overflow; return; } rd_buf_.commit( buffer_copy(*mb, buffers)); do_accept(&default_decorate_res, ec); } template<class NextLayer, bool deflateSupported> template< class ConstBufferSequence, class ResponseDecorator> typename std::enable_if<! http::detail::is_header< ConstBufferSequence>::value>::type stream<NextLayer, deflateSupported>:: accept_ex( ConstBufferSequence const& buffers, ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); using boost::asio::buffer_copy; using boost::asio::buffer_size; reset(); boost::optional<typename static_buffer_base::mutable_buffers_type> mb; try { mb.emplace(rd_buf_.prepare( buffer_size(buffers))); } catch(std::length_error const&) { ec = error::buffer_overflow; return; } rd_buf_.commit(buffer_copy(*mb, buffers)); do_accept(decorator, ec); } template<class NextLayer, bool deflateSupported> template<class Body, class Allocator> void stream<NextLayer, deflateSupported>:: accept( http::request<Body, http::basic_fields<Allocator>> const& req) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); error_code ec; accept(req, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> template< class Body, class Allocator, class ResponseDecorator> void stream<NextLayer, deflateSupported>:: accept_ex( http::request<Body, http::basic_fields<Allocator>> const& req, ResponseDecorator const& decorator) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(req, decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template<class NextLayer, bool deflateSupported> template<class Body, class Allocator> void stream<NextLayer, deflateSupported>:: accept( http::request<Body, http::basic_fields<Allocator>> const& req, error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); reset(); do_accept(req, &default_decorate_res, ec); } template<class NextLayer, bool deflateSupported> template< class Body, class Allocator, class ResponseDecorator> void stream<NextLayer, deflateSupported>:: accept_ex( http::request<Body, http::basic_fields<Allocator>> const& req, ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream<next_layer_type>::value, "SyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); reset(); do_accept(req, decorator, ec); } //------------------------------------------------------------------------------ template<class NextLayer, bool deflateSupported> template< class AcceptHandler> BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code)) stream<NextLayer, deflateSupported>:: async_accept( AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); accept_op< decltype(&default_decorate_res), BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, &default_decorate_res}({}); return init.result.get(); } template<class NextLayer, bool deflateSupported> template< class ResponseDecorator, class AcceptHandler> BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code)) stream<NextLayer, deflateSupported>:: async_accept_ex( ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); accept_op< ResponseDecorator, BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, decorator}({}); return init.result.get(); } template<class NextLayer, bool deflateSupported> template< class ConstBufferSequence, class AcceptHandler> typename std::enable_if< ! http::detail::is_header<ConstBufferSequence>::value, BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code))>::type stream<NextLayer, deflateSupported>:: async_accept( ConstBufferSequence const& buffers, AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); accept_op< decltype(&default_decorate_res), BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, &default_decorate_res}.run(buffers); return init.result.get(); } template<class NextLayer, bool deflateSupported> template< class ConstBufferSequence, class ResponseDecorator, class AcceptHandler> typename std::enable_if< ! http::detail::is_header<ConstBufferSequence>::value, BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code))>::type stream<NextLayer, deflateSupported>:: async_accept_ex( ConstBufferSequence const& buffers, ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); static_assert(boost::asio::is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); accept_op< ResponseDecorator, BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, decorator}.run(buffers); return init.result.get(); } template<class NextLayer, bool deflateSupported> template< class Body, class Allocator, class AcceptHandler> BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code)) stream<NextLayer, deflateSupported>:: async_accept( http::request<Body, http::basic_fields<Allocator>> const& req, AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); using boost::asio::asio_handler_is_continuation; response_op< BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, req, &default_decorate_res}(); return init.result.get(); } template<class NextLayer, bool deflateSupported> template< class Body, class Allocator, class ResponseDecorator, class AcceptHandler> BOOST_ASIO_INITFN_RESULT_TYPE( AcceptHandler, void(error_code)) stream<NextLayer, deflateSupported>:: async_accept_ex( http::request<Body, http::basic_fields<Allocator>> const& req, ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream<next_layer_type>::value, "AsyncStream requirements not met"); static_assert(detail::is_response_decorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); BOOST_BEAST_HANDLER_INIT( AcceptHandler, void(error_code)); reset(); using boost::asio::asio_handler_is_continuation; response_op< BOOST_ASIO_HANDLER_TYPE( AcceptHandler, void(error_code))>{ std::move(init.completion_handler), *this, req, decorator}(); return init.result.get(); } //------------------------------------------------------------------------------ template<class NextLayer, bool deflateSupported> template<class Decorator> void stream<NextLayer, deflateSupported>:: do_accept( Decorator const& decorator, error_code& ec) { http::request_parser<http::empty_body> p; http::read(next_layer(), rd_buf_, p, ec); if(ec == http::error::end_of_stream) ec = error::closed; if(ec) return; do_accept(p.get(), decorator, ec); } template<class NextLayer, bool deflateSupported> template<class Body, class Allocator, class Decorator> void stream<NextLayer, deflateSupported>:: do_accept( http::request<Body, http::basic_fields<Allocator>> const& req, Decorator const& decorator, error_code& ec) { error_code result; auto const res = build_response(req, decorator, result); http::write(stream_, res, ec); if(ec) return; ec = result; if(ec) { // VFALCO TODO Respect keep alive setting, perform // teardown if Connection: close. return; } do_pmd_config(res, is_deflate_supported{}); open(role_type::server); } } // websocket } // beast } // boost #endif