// // Copyright (c) 2013-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) // #ifndef BEAST_WEBSOCKET_IMPL_ACCEPT_IPP #define BEAST_WEBSOCKET_IMPL_ACCEPT_IPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace beast { namespace websocket { //------------------------------------------------------------------------------ // Respond to an upgrade HTTP request template template class stream::response_op { struct data { bool cont; stream& ws; response_type res; int state = 0; template data(Handler&, stream& ws_, http::header< true, http::basic_fields> const& req, Decorator const& decorator, bool cont_) : cont(cont_) , ws(ws_) , res(ws_.build_response(req, decorator)) { } template data(Handler&, stream& ws_, http::header< true, http::basic_fields> const& req, Buffers const& buffers, Decorator const& decorator, bool cont_) : cont(cont_) , ws(ws_) , res(ws_.build_response(req, decorator)) { using boost::asio::buffer_copy; using boost::asio::buffer_size; // VFALCO What about catch(std::length_error const&)? ws.stream_.buffer().commit(buffer_copy( ws.stream_.buffer().prepare( buffer_size(buffers)), buffers)); } }; handler_ptr d_; public: response_op(response_op&&) = default; response_op(response_op const&) = default; template response_op(DeducedHandler&& h, stream& ws, Args&&... args) : d_(std::forward(h), ws, std::forward(args)...) { (*this)(error_code{}, false); } void operator()( error_code ec, bool again = true); friend void* asio_handler_allocate( std::size_t size, response_op* op) { using boost::asio::asio_handler_allocate; return asio_handler_allocate( size, std::addressof(op->d_.handler())); } friend void asio_handler_deallocate( void* p, std::size_t size, response_op* op) { using boost::asio::asio_handler_deallocate; asio_handler_deallocate( p, size, std::addressof(op->d_.handler())); } friend bool asio_handler_is_continuation(response_op* op) { return op->d_->cont; } template 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 template void stream::response_op:: operator()(error_code ec, bool again) { auto& d = *d_; d.cont = d.cont || again; while(! ec && d.state != 99) { switch(d.state) { case 0: // send response d.state = 1; http::async_write(d.ws.next_layer(), d.res, std::move(*this)); return; // sent response case 1: d.state = 99; if(d.res.result() != http::status::switching_protocols) ec = error::handshake_failed; if(! ec) { pmd_read(d.ws.pmd_config_, d.res); d.ws.open(role_type::server); } break; } } d_.invoke(ec); } //------------------------------------------------------------------------------ // read and respond to an upgrade request // template template class stream::accept_op { struct data { stream& ws; Decorator decorator; http::request_parser p; data(Handler&, stream& ws_, Decorator const& decorator_) : ws(ws_) , decorator(decorator_) { } template data(Handler&, stream& ws_, Buffers const& buffers, Decorator const& decorator_) : ws(ws_) , decorator(decorator_) { using boost::asio::buffer_copy; using boost::asio::buffer_size; // VFALCO What about catch(std::length_error const&)? ws.stream_.buffer().commit(buffer_copy( ws.stream_.buffer().prepare( buffer_size(buffers)), buffers)); } }; handler_ptr d_; public: accept_op(accept_op&&) = default; accept_op(accept_op const&) = default; template accept_op(DeducedHandler&& h, stream& ws, Args&&... args) : d_(std::forward(h), ws, std::forward(args)...) { } void operator()(); void operator()(error_code ec); friend void* asio_handler_allocate( std::size_t size, accept_op* op) { using boost::asio::asio_handler_allocate; return asio_handler_allocate( size, std::addressof(op->d_.handler())); } friend void asio_handler_deallocate( void* p, std::size_t size, accept_op* op) { using boost::asio::asio_handler_deallocate; asio_handler_deallocate( p, size, std::addressof(op->d_.handler())); } 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 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 template void stream::accept_op:: operator()() { auto& d = *d_; http::async_read_header(d.ws.next_layer(), d.ws.stream_.buffer(), d.p, std::move(*this)); } template template void stream::accept_op:: operator()(error_code ec) { auto& d = *d_; if(! ec) { BOOST_ASSERT(d.p.is_header_done()); // 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 response_op{ d_.release_handler(), ws, req, decorator, true}; #else // VFALCO This *should* work but breaks // coroutine invariants in the unit test. // Also it calls reset() when it shouldn't. ws.async_accept_ex( req, decorator, d_.release_handler()); #endif return; } d_.invoke(ec); } //------------------------------------------------------------------------------ template void stream:: accept() { static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; accept(ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template void stream:: accept_ex(ResponseDecorator const& decorator) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template void stream:: accept(error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); reset(); do_accept(&default_decorate_res, ec); } template template void stream:: accept_ex(ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); reset(); do_accept(decorator, ec); } template template typename std::enable_if::value>::type stream:: accept(ConstBufferSequence const& buffers) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(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 template< class ConstBufferSequence, class ResponseDecorator> typename std::enable_if::value>::type stream:: accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const &decorator) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(buffers, decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template typename std::enable_if::value>::type stream:: accept(ConstBufferSequence const& buffers, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); reset(); using boost::asio::buffer_copy; using boost::asio::buffer_size; stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); do_accept(&default_decorate_res, ec); } template template< class ConstBufferSequence, class ResponseDecorator> typename std::enable_if::value>::type stream:: accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); reset(); using boost::asio::buffer_copy; using boost::asio::buffer_size; stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); do_accept(decorator, ec); } template template void stream:: accept(http::header> const& req) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); error_code ec; accept(req, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template void stream:: accept_ex(http::header> const& req, ResponseDecorator const& decorator) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(req, decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template void stream:: accept(http::header> const& req, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); reset(); do_accept(req, &default_decorate_res, ec); } template template void stream:: accept_ex(http::header> const& req, ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); reset(); do_accept(req, decorator, ec); } template template void stream:: accept(http::header> const& req, ConstBufferSequence const& buffers) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); error_code ec; accept(req, buffers, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template void stream:: accept_ex(http::header> const& req, ConstBufferSequence const& buffers, ResponseDecorator const& decorator) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); error_code ec; accept_ex(req, buffers, decorator, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template template void stream:: accept(http::header const& req, ConstBufferSequence const& buffers, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); reset(); using boost::asio::buffer_copy; using boost::asio::buffer_size; stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); do_accept(req, &default_decorate_res, ec); } template template void stream:: accept_ex(http::header> const& req, ConstBufferSequence const& buffers, ResponseDecorator const& decorator, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); reset(); using boost::asio::buffer_copy; using boost::asio::buffer_size; stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); do_accept(req, decorator, ec); } //------------------------------------------------------------------------------ template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept(AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; reset(); accept_op>{ init.completion_handler, *this, &default_decorate_res}(); return init.result.get(); } template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept_ex(ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); async_completion init{handler}; reset(); accept_op>{ init.completion_handler, *this, decorator}(); return init.result.get(); } template template typename std::enable_if< ! http::detail::is_header::value, async_return_type>::type stream:: async_accept(ConstBufferSequence const& buffers, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); async_completion init{handler}; reset(); accept_op>{ init.completion_handler, *this, buffers, &default_decorate_res}(); return init.result.get(); } template template typename std::enable_if< ! http::detail::is_header::value, async_return_type>::type stream:: async_accept_ex(ConstBufferSequence const& buffers, ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); async_completion init{handler}; reset(); accept_op>{ init.completion_handler, *this, buffers, decorator}(); return init.result.get(); } template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept(http::header> const& req, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); async_completion init{handler}; reset(); using boost::asio::asio_handler_is_continuation; response_op>{init.completion_handler, *this, req, &default_decorate_res, asio_handler_is_continuation( std::addressof(init.completion_handler))}; return init.result.get(); } template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept_ex(http::header> const& req, ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); async_completion init{handler}; reset(); using boost::asio::asio_handler_is_continuation; response_op>{ init.completion_handler, *this, req, decorator, asio_handler_is_continuation( std::addressof(init.completion_handler))}; return init.result.get(); } template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept(http::header> const& req, ConstBufferSequence const& buffers, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); async_completion init{handler}; reset(); using boost::asio::asio_handler_is_continuation; response_op>{ init.completion_handler, *this, req, buffers, &default_decorate_res, asio_handler_is_continuation( std::addressof(init.completion_handler))}; return init.result.get(); } template template async_return_type< AcceptHandler, void(error_code)> stream:: async_accept_ex(http::header> const& req, ConstBufferSequence const& buffers, ResponseDecorator const& decorator, AcceptHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream requirements requirements not met"); static_assert(is_const_buffer_sequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); static_assert(detail::is_ResponseDecorator< ResponseDecorator>::value, "ResponseDecorator requirements not met"); async_completion init{handler}; reset(); using boost::asio::asio_handler_is_continuation; response_op>{init.completion_handler, *this, req, buffers, decorator, asio_handler_is_continuation( std::addressof(init.completion_handler))}; return init.result.get(); } } // websocket } // beast #endif