// // Copyright (c) 2016-2019 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_HANDSHAKE_HPP #define BOOST_BEAST_WEBSOCKET_IMPL_HANDSHAKE_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { namespace websocket { //------------------------------------------------------------------------------ // send the upgrade request and process the response // template template class stream::handshake_op : public beast::stable_async_base> , public asio::coroutine { struct data { // VFALCO This really should be two separate // composed operations, to save on memory request_type req; http::response_parser< typename response_type::body_type> p; flat_buffer fb; bool overflow = false; // could be a member of the op explicit data(request_type&& req_) : req(std::move(req_)) { } }; boost::weak_ptr wp_; detail::sec_ws_key_type key_; response_type* res_p_; data& d_; public: template handshake_op( Handler_&& h, boost::shared_ptr const& sp, request_type&& req, detail::sec_ws_key_type key, response_type* res_p) : stable_async_base>( std::forward(h), sp->stream().get_executor()) , wp_(sp) , key_(key) , res_p_(res_p) , d_(beast::allocate_stable( *this, std::move(req))) { sp->reset(); // VFALCO I don't like this (*this)({}, 0, false); } void operator()( error_code ec = {}, std::size_t bytes_used = 0, bool cont = true) { boost::ignore_unused(bytes_used); auto sp = wp_.lock(); if(! sp) { ec = net::error::operation_aborted; return this->complete(cont, ec); } auto& impl = *sp; BOOST_ASIO_CORO_REENTER(*this) { impl.change_status(status::handshake); impl.update_timer(this->get_executor()); // write HTTP request impl.do_pmd_config(d_.req); BOOST_ASIO_CORO_YIELD { BOOST_ASIO_HANDLER_LOCATION(( __FILE__, __LINE__, "websocket::async_handshake")); http::async_write(impl.stream(), d_.req, std::move(*this)); } if(impl.check_stop_now(ec)) goto upcall; // read HTTP response BOOST_ASIO_CORO_YIELD { BOOST_ASIO_HANDLER_LOCATION(( __FILE__, __LINE__, "websocket::async_handshake")); http::async_read(impl.stream(), impl.rd_buf, d_.p, std::move(*this)); } if(ec == http::error::buffer_overflow) { // If the response overflows the internal // read buffer, switch to a dynamically // allocated flat buffer. d_.fb.commit(net::buffer_copy( d_.fb.prepare(impl.rd_buf.size()), impl.rd_buf.data())); impl.rd_buf.clear(); BOOST_ASIO_CORO_YIELD { BOOST_ASIO_HANDLER_LOCATION(( __FILE__, __LINE__, "websocket::async_handshake")); http::async_read(impl.stream(), d_.fb, d_.p, std::move(*this)); } if(! ec) { // Copy any leftovers back into the read // buffer, since this represents websocket // frame data. if(d_.fb.size() <= impl.rd_buf.capacity()) { impl.rd_buf.commit(net::buffer_copy( impl.rd_buf.prepare(d_.fb.size()), d_.fb.data())); } else { ec = http::error::buffer_overflow; } } // Do this before the upcall d_.fb.clear(); } if(impl.check_stop_now(ec)) goto upcall; // success impl.reset_idle(); impl.on_response(d_.p.get(), key_, ec); if(res_p_) swap(d_.p.get(), *res_p_); upcall: this->complete(cont ,ec); } } }; template struct stream:: run_handshake_op { template void operator()( HandshakeHandler&& h, boost::shared_ptr const& sp, request_type&& req, detail::sec_ws_key_type key, response_type* res_p) { // If you get an error on the following line it means // that your handler does not meet the documented type // requirements for the handler. static_assert( beast::detail::is_invocable::value, "HandshakeHandler type requirements not met"); handshake_op< typename std::decay::type>( std::forward(h), sp, std::move(req), key, res_p); } }; //------------------------------------------------------------------------------ template template void stream:: do_handshake( response_type* res_p, string_view host, string_view target, RequestDecorator const& decorator, error_code& ec) { auto& impl = *impl_; impl.change_status(status::handshake); impl.reset(); detail::sec_ws_key_type key; { auto const req = impl.build_request( key, host, target, decorator); impl.do_pmd_config(req); http::write(impl.stream(), req, ec); } if(impl.check_stop_now(ec)) return; http::response_parser< typename response_type::body_type> p; http::read(next_layer(), impl.rd_buf, p, ec); if(ec == http::error::buffer_overflow) { // If the response overflows the internal // read buffer, switch to a dynamically // allocated flat buffer. flat_buffer fb; fb.commit(net::buffer_copy( fb.prepare(impl.rd_buf.size()), impl.rd_buf.data())); impl.rd_buf.clear(); http::read(next_layer(), fb, p, ec);; if(! ec) { // Copy any leftovers back into the read // buffer, since this represents websocket // frame data. if(fb.size() <= impl.rd_buf.capacity()) { impl.rd_buf.commit(net::buffer_copy( impl.rd_buf.prepare(fb.size()), fb.data())); } else { ec = http::error::buffer_overflow; } } } if(impl.check_stop_now(ec)) return; impl.on_response(p.get(), key, ec); if(impl.check_stop_now(ec)) return; if(res_p) *res_p = p.release(); } //------------------------------------------------------------------------------ template template BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler) stream:: async_handshake( string_view host, string_view target, HandshakeHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream type requirements not met"); detail::sec_ws_key_type key; auto req = impl_->build_request( key, host, target, &default_decorate_req); return net::async_initiate< HandshakeHandler, void(error_code)>( run_handshake_op{}, handler, impl_, std::move(req), key, nullptr); } template template BOOST_BEAST_ASYNC_RESULT1(HandshakeHandler) stream:: async_handshake( response_type& res, string_view host, string_view target, HandshakeHandler&& handler) { static_assert(is_async_stream::value, "AsyncStream type requirements not met"); detail::sec_ws_key_type key; auto req = impl_->build_request( key, host, target, &default_decorate_req); return net::async_initiate< HandshakeHandler, void(error_code)>( run_handshake_op{}, handler, impl_, std::move(req), key, &res); } template void stream:: handshake(string_view host, string_view target) { static_assert(is_sync_stream::value, "SyncStream type requirements not met"); error_code ec; handshake( host, target, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template void stream:: handshake(response_type& res, string_view host, string_view target) { static_assert(is_sync_stream::value, "SyncStream type requirements not met"); error_code ec; handshake(res, host, target, ec); if(ec) BOOST_THROW_EXCEPTION(system_error{ec}); } template void stream:: handshake(string_view host, string_view target, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream type requirements not met"); do_handshake(nullptr, host, target, &default_decorate_req, ec); } template void stream:: handshake(response_type& res, string_view host, string_view target, error_code& ec) { static_assert(is_sync_stream::value, "SyncStream type requirements not met"); do_handshake(&res, host, target, &default_decorate_req, ec); } } // websocket } // beast } // boost #endif