// // Copyright (c) 2013-2016 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_HANDSHAKE_IPP #define BEAST_WEBSOCKET_IMPL_HANDSHAKE_IPP #include #include #include #include #include #include #include #include #include namespace beast { namespace websocket { //------------------------------------------------------------------------------ // send the upgrade request and process the response // template template class stream::handshake_op { struct data { bool cont; stream& ws; std::string key; http::request req; http::response resp; int state = 0; data(Handler& handler, stream& ws_, boost::string_ref const& host, boost::string_ref const& resource) : cont(beast_asio_helpers:: is_continuation(handler)) , ws(ws_) , req(ws.build_request(host, resource, key)) { ws.reset(); } }; handler_ptr d_; public: handshake_op(handshake_op&&) = default; handshake_op(handshake_op const&) = default; template handshake_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, handshake_op* op) { return beast_asio_helpers:: allocate(size, op->d_.handler()); } friend void asio_handler_deallocate( void* p, std::size_t size, handshake_op* op) { return beast_asio_helpers:: deallocate(p, size, op->d_.handler()); } friend bool asio_handler_is_continuation(handshake_op* op) { return op->d_->cont; } template friend void asio_handler_invoke(Function&& f, handshake_op* op) { return beast_asio_helpers:: invoke(f, op->d_.handler()); } }; template template void stream::handshake_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 http upgrade d.state = 1; // VFALCO Do we need the ability to move // a message on the async_write? pmd_read( d.ws.pmd_config_, d.req.fields); http::async_write(d.ws.stream_, d.req, std::move(*this)); return; } // sent upgrade case 1: // read http response d.state = 2; http::async_read(d.ws.next_layer(), d.ws.stream_.buffer(), d.resp, std::move(*this)); return; // got response case 2: { d.ws.do_response(d.resp, d.key, ec); // call handler d.state = 99; break; } } } d_.invoke(ec); } template template typename async_completion< HandshakeHandler, void(error_code)>::result_type stream:: async_handshake(boost::string_ref const& host, boost::string_ref const& resource, HandshakeHandler&& handler) { static_assert(is_AsyncStream::value, "AsyncStream requirements not met"); beast::async_completion< HandshakeHandler, void(error_code) > completion(handler); handshake_op{ completion.handler, *this, host, resource}; return completion.result.get(); } template void stream:: handshake(boost::string_ref const& host, boost::string_ref const& resource) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); error_code ec; handshake(host, resource, ec); if(ec) throw system_error{ec}; } template void stream:: handshake(boost::string_ref const& host, boost::string_ref const& resource, error_code& ec) { static_assert(is_SyncStream::value, "SyncStream requirements not met"); reset(); std::string key; { auto const req = build_request(host, resource, key); pmd_read(pmd_config_, req.fields); http::write(stream_, req, ec); } if(ec) return; http::response res; http::read(next_layer(), stream_.buffer(), res, ec); if(ec) return; do_response(res, key, ec); } //------------------------------------------------------------------------------ } // websocket } // beast #endif