// // 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 namespace beast { namespace websocket { //------------------------------------------------------------------------------ // Respond to an upgrade HTTP request template template class stream::response_op { struct data { bool cont; stream& ws; http::response_header res; error_code final_ec; int state = 0; template data(Handler&, stream& ws_, http::header const& req, bool cont_) : cont(cont_) , ws(ws_) , res(ws_.build_response(req)) { // can't call stream::reset() here // otherwise accept_op will malfunction // if(res.status != 101) final_ec = error::handshake_failed; } }; 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) { return beast_asio_helpers:: allocate(size, op->d_.handler()); } friend void asio_handler_deallocate( void* p, std::size_t size, response_op* op) { return beast_asio_helpers:: deallocate(p, size, 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) { return beast_asio_helpers:: invoke(f, 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; ec = d.final_ec; if(! ec) { pmd_read( d.ws.pmd_config_, d.res.fields); d.ws.open(detail::role_type::server); } break; } } d_.invoke(ec); } //------------------------------------------------------------------------------ // read and respond to an upgrade request // template template class stream::accept_op { struct data { bool cont; stream& ws; http::header_parser p; int state = 0; template data(Handler& handler, stream& ws_, Buffers const& buffers) : cont(beast_asio_helpers:: is_continuation(handler)) , ws(ws_) { using boost::asio::buffer_copy; using boost::asio::buffer_size; ws.reset(); 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)...) { (*this)(error_code{}, 0, false); } void operator()(error_code const& ec) { (*this)(ec, 0); } void operator()(error_code ec, std::size_t bytes_used, bool again = true); friend void* asio_handler_allocate( std::size_t size, accept_op* op) { return beast_asio_helpers:: allocate(size, op->d_.handler()); } friend void asio_handler_deallocate( void* p, std::size_t size, accept_op* op) { return beast_asio_helpers:: deallocate(p, size, op->d_.handler()); } friend bool asio_handler_is_continuation(accept_op* op) { return op->d_->cont; } template friend void asio_handler_invoke(Function&& f, accept_op* op) { return beast_asio_helpers:: invoke(f, op->d_.handler()); } }; template template void stream::accept_op:: operator()(error_code ec, std::size_t bytes_used, bool again) { auto& d = *d_; d.cont = d.cont || again; if(ec) goto upcall; switch(d.state) { case 0: // read message d.state = 1; http::async_read_some(d.ws.next_layer(), d.ws.stream_.buffer(), d.p, std::move(*this)); return; case 1: { BOOST_ASSERT(d.p.got_header()); d.ws.stream_.buffer().consume(bytes_used); // Arguments from our state must be // moved to the stack before releasing // the handler. auto& ws = d.ws; auto m = d.p.release(); response_op{ d_.release_handler(), ws, std::move(m), true}; return; } } upcall: d_.invoke(ec); } template template typename async_completion< AcceptHandler, void(error_code)>::result_type stream:: async_accept(AcceptHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements requirements not met"); return async_accept(boost::asio::null_buffers{}, std::forward(handler)); } template template typename async_completion< AcceptHandler, void(error_code)>::result_type stream:: async_accept(ConstBufferSequence const& bs, AcceptHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); beast::async_completion< AcceptHandler, void(error_code) > completion{handler}; accept_op{ completion.handler, *this, bs}; return completion.result.get(); } template template typename async_completion< AcceptHandler, void(error_code)>::result_type stream:: async_accept(http::header const& req, AcceptHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements requirements not met"); beast::async_completion< AcceptHandler, void(error_code) > completion{handler}; reset(); response_op{ completion.handler, *this, req, beast_asio_helpers:: is_continuation(completion.handler)}; return completion.result.get(); } template void stream:: accept() { static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; accept(boost::asio::null_buffers{}, ec); if(ec) throw system_error{ec}; } template void stream:: accept(error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); accept(boost::asio::null_buffers{}, ec); } template template void stream:: accept(ConstBufferSequence const& buffers) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); static_assert(is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); error_code ec; accept(buffers, ec); if(ec) throw system_error{ec}; } template template void stream:: accept(ConstBufferSequence const& buffers, error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); static_assert(beast::is_ConstBufferSequence< ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); using boost::asio::buffer_copy; using boost::asio::buffer_size; reset(); stream_.buffer().commit(buffer_copy( stream_.buffer().prepare( buffer_size(buffers)), buffers)); http::header_parser p; auto const bytes_used = http::read_some( next_layer(), stream_.buffer(), p, ec); if(ec) return; BOOST_ASSERT(p.got_header()); stream_.buffer().consume(bytes_used); accept(p.get(), ec); } template template void stream:: accept(http::header const& request) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; accept(request, ec); if(ec) throw system_error{ec}; } template template void stream:: accept(http::header const& req, error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); reset(); auto const res = build_response(req); http::write(stream_, res, ec); if(ec) return; if(res.status != 101) { ec = error::handshake_failed; // VFALCO TODO Respect keep alive setting, perform // teardown if Connection: close. return; } pmd_read(pmd_config_, req.fields); open(detail::role_type::server); } } // websocket } // beast #endif