peerplays-fc/src/network/http/websocket.cpp

791 lines
32 KiB
C++
Raw Normal View History

#include <fc/network/http/websocket.hpp>
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/config/asio.hpp>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_client.hpp>
#include <websocketpp/client.hpp>
#include <websocketpp/logger/stub.hpp>
2019-02-26 18:55:42 +00:00
#ifdef HAS_ZLIB
2018-07-11 07:57:34 +00:00
#include <websocketpp/extensions/permessage_deflate/enabled.hpp>
2019-02-26 18:55:42 +00:00
#else
#include <websocketpp/extensions/permessage_deflate/disabled.hpp>
#endif
2018-07-11 07:57:34 +00:00
2019-05-24 12:33:46 +00:00
#include <fc/io/json.hpp>
#include <fc/optional.hpp>
2019-05-24 12:33:46 +00:00
#include <fc/reflect/variant.hpp>
#include <fc/rpc/websocket_api.hpp>
#include <fc/variant.hpp>
#include <fc/thread/thread.hpp>
#include <fc/asio.hpp>
#include <boost/algorithm/string.hpp>
#if WIN32
#include <wincrypt.h>
#endif
2015-05-27 19:04:34 +00:00
#ifdef DEFAULT_LOGGER
# undef DEFAULT_LOGGER
#endif
#define DEFAULT_LOGGER "rpc"
namespace fc { namespace http {
namespace detail {
#if WIN32
// taken from https://stackoverflow.com/questions/39772878/reliable-way-to-get-root-ca-certificates-on-windows/40710806
static void add_windows_root_certs(boost::asio::ssl::context &ctx)
{
HCERTSTORE hStore = CertOpenSystemStore( 0, "ROOT" );
if( hStore == NULL )
return;
X509_STORE *store = X509_STORE_new();
PCCERT_CONTEXT pContext = NULL;
while( (pContext = CertEnumCertificatesInStore( hStore, pContext )) != NULL )
{
X509 *x509 = d2i_X509( NULL, (const unsigned char **)&pContext->pbCertEncoded,
pContext->cbCertEncoded);
if( x509 != NULL )
{
X509_STORE_add_cert( store, x509 );
X509_free( x509 );
}
}
CertFreeCertificateContext( pContext );
CertCloseStore( hStore, 0 );
SSL_CTX_set_cert_store( ctx.native_handle(), store );
}
#endif
struct asio_with_stub_log : public websocketpp::config::asio {
typedef asio_with_stub_log type;
typedef asio base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::basic_socket::endpoint
socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
static const long timeout_open_handshake = 0;
2018-07-11 07:57:34 +00:00
// permessage_compress extension
struct permessage_deflate_config {};
2019-02-26 18:55:42 +00:00
#ifdef HAS_ZLIB
2018-07-11 07:57:34 +00:00
typedef websocketpp::extensions::permessage_deflate::enabled <permessage_deflate_config> permessage_deflate_type;
2019-02-26 18:55:42 +00:00
#else
typedef websocketpp::extensions::permessage_deflate::disabled <permessage_deflate_config> permessage_deflate_type;
#endif
2018-07-11 07:57:34 +00:00
};
struct asio_tls_with_stub_log : public websocketpp::config::asio_tls {
typedef asio_with_stub_log type;
typedef asio_tls base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
2015-05-12 18:50:08 +00:00
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
};
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
static const long timeout_open_handshake = 0;
2015-05-12 18:50:08 +00:00
};
struct asio_tls_stub_log : public websocketpp::config::asio_tls {
typedef asio_tls_stub_log type;
typedef asio_tls base;
2015-05-12 18:50:08 +00:00
typedef base::concurrency_type concurrency_type;
2015-05-12 18:50:08 +00:00
typedef base::request_type request_type;
typedef base::response_type response_type;
2015-05-12 18:50:08 +00:00
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
2015-05-12 18:50:08 +00:00
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
2015-05-12 18:50:08 +00:00
typedef base::rng_type rng_type;
2015-05-12 18:50:08 +00:00
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::tls_socket::endpoint socket_type;
};
2015-05-12 18:50:08 +00:00
typedef websocketpp::transport::asio::endpoint<transport_config>
transport_type;
};
using websocketpp::connection_hdl;
typedef websocketpp::server<asio_with_stub_log> websocket_server_type;
typedef websocketpp::server<asio_tls_stub_log> websocket_tls_server_type;
template<typename T>
class websocket_connection_impl : public websocket_connection
{
public:
websocket_connection_impl( T con ) : _ws_connection(con)
{
_remote_endpoint = con->get_remote_endpoint();
2015-05-06 20:34:55 +00:00
}
virtual ~websocket_connection_impl()
2019-05-23 21:05:05 +00:00
{
}
virtual void send_message( const std::string& message )override
{
ilog( "[OUT] ${remote_endpoint} ${msg}",
("remote_endpoint",_remote_endpoint) ("msg",message) );
auto ec = _ws_connection->send( message );
FC_ASSERT( !ec, "websocket send failed: ${msg}", ("msg",ec.message() ) );
}
virtual void close( int64_t code, const std::string& reason )override
{
_ws_connection->close(code,reason);
}
virtual std::string get_request_header(const std::string& key)override
{
return _ws_connection->get_request_header(key);
}
T _ws_connection;
};
template<typename T>
class possibly_proxied_websocket_connection : public websocket_connection_impl<T>
{
public:
possibly_proxied_websocket_connection( T con, const std::string& forward_header_key )
: websocket_connection_impl<T>(con)
{
// By calling the parent's constructor, _remote_endpoint has been initialized.
// Now try to extract remote address from the header, if found, overwrite it
if( !forward_header_key.empty() )
{
const std::string value = this->get_request_header( forward_header_key );
if( !value.empty() )
this->_remote_endpoint = value;
}
}
virtual ~possibly_proxied_websocket_connection()
{
}
};
2015-05-12 18:50:08 +00:00
typedef websocketpp::lib::shared_ptr<boost::asio::ssl::context> context_ptr;
class websocket_server_impl
{
public:
websocket_server_impl( const std::string& forward_header_key )
:_server_thread( fc::thread::current() ), _forward_header_key(forward_header_key)
{
_server.clear_access_channels( websocketpp::log::alevel::all );
_server.init_asio(&fc::asio::default_io_service());
_server.set_reuse_addr(true);
_server.set_open_handler( [&]( connection_hdl hdl ){
2019-06-21 19:03:00 +00:00
_server_thread.async( [this, hdl](){
auto new_con = std::make_shared<possibly_proxied_websocket_connection<
websocket_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl),
_forward_header_key );
_on_connection( _connections[hdl] = new_con );
}).wait();
});
_server.set_message_handler( [&]( connection_hdl hdl, websocket_server_type::message_ptr msg ){
_server_thread.async( [&](){
auto current_con = _connections.find(hdl);
assert( current_con != _connections.end() );
2015-05-29 16:11:21 +00:00
auto payload = msg->get_payload();
2019-05-23 21:05:05 +00:00
std::shared_ptr<websocket_connection> con = current_con->second;
wlog( "[IN] ${remote_endpoint} ${msg}",
("remote_endpoint",con->get_remote_endpoint()) ("msg",payload) );
2015-09-07 21:46:08 +00:00
++_pending_messages;
auto f = fc::async([this,con,payload](){
if( _pending_messages )
--_pending_messages;
con->on_message( payload );
});
if( _pending_messages > 100 )
f.wait();
}).wait();
});
2015-05-12 18:50:08 +00:00
2016-07-19 01:20:40 +00:00
_server.set_socket_init_handler( [&](websocketpp::connection_hdl hdl, boost::asio::ip::tcp::socket& s ) {
boost::asio::ip::tcp::no_delay option(true);
s.lowest_layer().set_option(option);
} );
2015-05-12 18:50:08 +00:00
_server.set_http_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto con = _server.get_con_from_hdl(hdl);
auto current_con = std::make_shared<possibly_proxied_websocket_connection<
websocket_server_type::connection_ptr>>( con, _forward_header_key );
2015-05-12 18:50:08 +00:00
_on_connection( current_con );
con->defer_http_response();
std::string remote_endpoint = current_con->get_remote_endpoint();
std::string request_body = con->get_request_body();
wlog( "[IN] ${remote_endpoint} ${msg}",
("remote_endpoint",remote_endpoint) ("msg",request_body) );
fc::async([current_con, request_body, con, remote_endpoint] {
2019-05-24 12:33:46 +00:00
fc::http::reply response = current_con->on_http(request_body);
ilog( "[OUT] ${remote_endpoint} ${msg}",
("remote_endpoint",remote_endpoint) ("msg",response) );
2019-05-24 12:33:46 +00:00
con->set_body( std::move( response.body_as_string ) );
con->set_status( websocketpp::http::status_code::value(response.status) );
con->send_http_response();
current_con->closed();
}, "call on_http");
2015-05-12 18:50:08 +00:00
}).wait();
});
_server.set_close_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
else
{
wlog( "unknown connection closed" );
}
2015-08-14 18:08:16 +00:00
if( _connections.empty() && _closed )
_closed->set_value();
}).wait();
});
_server.set_fail_handler( [&]( connection_hdl hdl ){
if( _server.is_listening() )
{
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
else
{
wlog( "unknown connection failed" );
}
2015-08-14 18:08:16 +00:00
if( _connections.empty() && _closed )
_closed->set_value();
}).wait();
}
});
}
~websocket_server_impl()
2015-08-14 18:08:16 +00:00
{
if( _server.is_listening() )
_server.stop_listening();
2015-08-14 18:08:16 +00:00
2019-04-26 12:21:11 +00:00
if( _connections.size() )
_closed = promise<void>::create();
auto cpy_con = _connections;
for( auto item : cpy_con )
_server.close( item.first, 0, "server exit" );
2015-08-14 18:08:16 +00:00
if( _closed ) _closed->wait();
}
typedef std::map<connection_hdl, websocket_connection_ptr,std::owner_less<connection_hdl> > con_map;
con_map _connections;
fc::thread& _server_thread;
websocket_server_type _server;
on_connection_handler _on_connection;
fc::promise<void>::ptr _closed;
2015-09-07 21:46:08 +00:00
uint32_t _pending_messages = 0;
std::string _forward_header_key; // A header like "X-Forwarded-For" (XFF), with data IP:port
};
class websocket_tls_server_impl
2015-05-12 18:50:08 +00:00
{
public:
websocket_tls_server_impl( const string& server_pem, const string& ssl_password,
const std::string& forward_header_key )
:_server_thread( fc::thread::current() ), _forward_header_key(forward_header_key)
2015-05-12 18:50:08 +00:00
{
{
_server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr {
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
ctx->set_password_callback([=](std::size_t max_length, boost::asio::ssl::context::password_purpose){ return ssl_password;});
ctx->use_certificate_chain_file(server_pem);
ctx->use_private_key_file(server_pem, boost::asio::ssl::context::pem);
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
return ctx;
});
}
_server.clear_access_channels( websocketpp::log::alevel::all );
_server.init_asio(&fc::asio::default_io_service());
_server.set_reuse_addr(true);
_server.set_open_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto new_con = std::make_shared<possibly_proxied_websocket_connection<
websocket_tls_server_type::connection_ptr>>( _server.get_con_from_hdl(hdl),
_forward_header_key );
_on_connection( _connections[hdl] = new_con );
}).wait();
});
_server.set_message_handler( [&]( connection_hdl hdl, websocket_server_type::message_ptr msg ){
_server_thread.async( [&](){
auto current_con = _connections.find(hdl);
assert( current_con != _connections.end() );
auto received = msg->get_payload();
2019-05-23 21:05:05 +00:00
std::shared_ptr<websocket_connection> con = current_con->second;
wlog( "[IN] ${remote_endpoint} ${msg}",
("remote_endpoint",con->get_remote_endpoint()) ("msg",received) );
2019-05-23 21:05:05 +00:00
fc::async([con,received](){ con->on_message( received ); });
}).wait();
});
_server.set_http_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
auto con = _server.get_con_from_hdl(hdl);
auto current_con = std::make_shared<possibly_proxied_websocket_connection<
websocket_tls_server_type::connection_ptr>>( con, _forward_header_key );
try{
_on_connection( current_con );
std::string remote_endpoint = current_con->get_remote_endpoint();
std::string request_body = con->get_request_body();
wlog( "[IN] ${remote_endpoint} ${msg}",
("remote_endpoint",remote_endpoint) ("msg",request_body) );
2020-05-01 21:57:21 +00:00
auto response = current_con->on_http( request_body );
ilog( "[OUT] ${remote_endpoint} ${msg}",
("remote_endpoint",remote_endpoint) ("msg",response) );
2019-05-24 12:33:46 +00:00
con->set_body( std::move( response.body_as_string ) );
con->set_status( websocketpp::http::status_code::value( response.status ) );
} catch ( const fc::exception& e )
{
2019-05-23 21:05:05 +00:00
edump((e.to_detail_string()));
}
current_con->closed();
}).wait();
2015-05-12 18:50:08 +00:00
});
_server.set_close_handler( [&]( connection_hdl hdl ){
_server_thread.async( [&](){
_connections[hdl]->closed();
_connections.erase( hdl );
}).wait();
});
_server.set_fail_handler( [&]( connection_hdl hdl ){
if( _server.is_listening() )
{
_server_thread.async( [&](){
if( _connections.find(hdl) != _connections.end() )
{
_connections[hdl]->closed();
_connections.erase( hdl );
}
}).wait();
}
});
}
2019-05-24 16:15:02 +00:00
~websocket_tls_server_impl()
{
if( _server.is_listening() )
_server.stop_listening();
auto cpy_con = _connections;
for( auto item : cpy_con )
_server.close( item.first, 0, "server exit" );
2015-05-12 18:50:08 +00:00
}
typedef std::map<connection_hdl, websocket_connection_ptr,std::owner_less<connection_hdl> > con_map;
con_map _connections;
fc::thread& _server_thread;
websocket_tls_server_type _server;
on_connection_handler _on_connection;
fc::promise<void>::ptr _closed;
std::string _forward_header_key; // A header like "X-Forwarded-For" (XFF), with data IP:port
2015-05-12 18:50:08 +00:00
};
typedef websocketpp::client<asio_with_stub_log> websocket_client_type;
typedef websocketpp::client<asio_tls_stub_log> websocket_tls_client_type;
typedef websocket_client_type::connection_ptr websocket_client_connection_type;
typedef websocket_tls_client_type::connection_ptr websocket_tls_client_connection_type;
using websocketpp::connection_hdl;
template<typename T>
class generic_websocket_client_impl
{
public:
generic_websocket_client_impl()
:_client_thread( fc::thread::current() )
{
_client.clear_access_channels( websocketpp::log::alevel::all );
2019-05-25 14:23:54 +00:00
_client.set_message_handler( [&]( connection_hdl hdl,
typename websocketpp::client<T>::message_ptr msg ){
_client_thread.async( [&](){
2019-05-23 21:05:05 +00:00
wdump((msg->get_payload()));
2015-05-29 16:11:21 +00:00
auto received = msg->get_payload();
fc::async( [=](){
if( _connection )
_connection->on_message(received);
});
}).wait();
});
_client.set_close_handler( [=]( connection_hdl hdl ){
_client_thread.async( [&](){ if( _connection ) {_connection->closed(); _connection.reset();} } ).wait();
if( _closed ) _closed->set_value();
});
_client.set_fail_handler( [=]( connection_hdl hdl ){
auto con = _client.get_con_from_hdl(hdl);
auto message = con->get_ec().message();
if( _connection )
_client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait();
if( _connected && !_connected->ready() )
_connected->set_exception( exception_ptr( new FC_EXCEPTION( exception, "${message}", ("message",message)) ) );
if( _closed )
_closed->set_value();
});
_client.init_asio( &fc::asio::default_io_service() );
}
virtual ~generic_websocket_client_impl()
{
2019-05-25 12:30:05 +00:00
if( _connection )
{
_connection->close(0, "client closed");
_connection.reset();
}
2019-05-25 12:30:05 +00:00
if( _closed )
_closed->wait();
}
fc::promise<void>::ptr _connected;
fc::promise<void>::ptr _closed;
fc::thread& _client_thread;
websocketpp::client<T> _client;
websocket_connection_ptr _connection;
std::string _uri;
fc::optional<connection_hdl> _hdl;
};
class websocket_client_impl : public generic_websocket_client_impl<asio_with_stub_log>
{};
class websocket_tls_client_impl : public generic_websocket_client_impl<asio_tls_stub_log>
{
public:
websocket_tls_client_impl( const std::string& ca_filename )
: generic_websocket_client_impl()
{
// ca_filename has special values:
// "_none" disables cert checking (potentially insecure!)
// "_default" uses default CA's provided by OS
//
// We need ca_filename to be copied into the closure, as the referenced object might be destroyed by the caller by the time
// tls_init_handler() is called. According to [1], capture-by-value results in the desired behavior (i.e. creation of
// a copy which is stored in the closure) on standards compliant compilers, but some compilers on some optimization levels
// are buggy and are not standards compliant in this situation. Also, keep in mind this is the opinion of a single forum
// poster and might be wrong.
//
// To be safe, the following line explicitly creates a non-reference string which is captured by value, which should have the
// correct behavior on all compilers.
//
// [1] http://www.cplusplus.com/forum/general/142165/
// [2] http://stackoverflow.com/questions/21443023/capturing-a-reference-by-reference-in-a-c11-lambda
//
std::string ca_filename_copy = ca_filename;
_client.set_tls_init_handler( [=](websocketpp::connection_hdl) {
context_ptr ctx = websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv1);
try {
ctx->set_options(boost::asio::ssl::context::default_workarounds |
boost::asio::ssl::context::no_sslv2 |
boost::asio::ssl::context::no_sslv3 |
boost::asio::ssl::context::single_dh_use);
setup_peer_verify( ctx, ca_filename_copy );
} catch (std::exception& e) {
edump((e.what()));
std::cout << e.what() << std::endl;
}
return ctx;
});
}
virtual ~websocket_tls_client_impl() {}
std::string get_host()const
{
return websocketpp::uri( _uri ).get_host();
}
void setup_peer_verify( context_ptr& ctx, const std::string& ca_filename )
{
if( ca_filename == "_none" )
return;
ctx->set_verify_mode( boost::asio::ssl::verify_peer );
if( ca_filename == "_default" )
{
#if WIN32
add_windows_root_certs( *ctx );
#else
ctx->set_default_verify_paths();
#endif
}
else
ctx->load_verify_file( ca_filename );
ctx->set_verify_depth(10);
ctx->set_verify_callback( boost::asio::ssl::rfc2818_verification( get_host() ) );
}
};
} // namespace detail
websocket_server::websocket_server( const std::string& forward_header_key )
:my( new detail::websocket_server_impl( forward_header_key ) ) {}
websocket_server::~websocket_server(){}
void websocket_server::on_connection( const on_connection_handler& handler )
{
my->_on_connection = handler;
}
void websocket_server::listen( uint16_t port )
{
my->_server.listen(port);
}
void websocket_server::listen( const fc::ip::endpoint& ep )
{
my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
}
uint16_t websocket_server::get_listening_port()
{
websocketpp::lib::asio::error_code ec;
return my->_server.get_local_endpoint(ec).port();
}
void websocket_server::start_accept() {
my->_server.start_accept();
}
void websocket_server::stop_listening()
{
my->_server.stop_listening();
}
void websocket_server::close()
{
for (auto& connection : my->_connections)
my->_server.close(connection.first, websocketpp::close::status::normal, "Goodbye");
}
websocket_tls_server::websocket_tls_server( const string& server_pem, const string& ssl_password,
const std::string& forward_header_key )
:my( new detail::websocket_tls_server_impl(server_pem, ssl_password, forward_header_key) )
{}
2015-05-12 18:50:08 +00:00
websocket_tls_server::~websocket_tls_server(){}
void websocket_tls_server::on_connection( const on_connection_handler& handler )
{
my->_on_connection = handler;
2015-05-12 18:50:08 +00:00
}
void websocket_tls_server::listen( uint16_t port )
{
my->_server.listen(port);
2015-05-12 18:50:08 +00:00
}
void websocket_tls_server::listen( const fc::ip::endpoint& ep )
{
my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
2015-05-12 18:50:08 +00:00
}
void websocket_tls_server::start_accept() {
my->_server.start_accept();
2015-05-12 18:50:08 +00:00
}
websocket_tls_client::websocket_tls_client( const std::string& ca_filename ):my( new detail::websocket_tls_client_impl( ca_filename ) ) {}
websocket_tls_client::~websocket_tls_client(){ }
websocket_client::websocket_client( const std::string& ca_filename )
:my( new detail::websocket_client_impl() ),
smy(new detail::websocket_tls_client_impl( ca_filename ))
{}
websocket_client::~websocket_client(){ }
websocket_connection_ptr websocket_client::connect( const std::string& uri )
{ try {
if( uri.substr(0,4) == "wss:" )
return secure_connect(uri);
FC_ASSERT( uri.substr(0,3) == "ws:" );
websocketpp::lib::error_code ec;
my->_uri = uri;
2019-04-26 12:21:11 +00:00
my->_connected = promise<void>::create("websocket::connect");
2015-05-12 18:50:08 +00:00
my->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){
my->_hdl = hdl;
auto con = my->_client.get_con_from_hdl(hdl);
my->_connection = std::make_shared<detail::websocket_connection_impl<
detail::websocket_client_connection_type>>( con );
2019-04-26 12:21:11 +00:00
my->_closed = promise<void>::create("websocket::closed");
my->_connected->set_value();
});
2015-05-12 18:50:08 +00:00
auto con = my->_client.get_connection( uri, ec );
2015-05-12 18:50:08 +00:00
std::for_each(headers.begin(), headers.end(), [con](std::pair<std::string, std::string> in) {
con->append_header(in.first, in.second);
});
2019-05-23 21:05:05 +00:00
if( ec ) FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) );
2015-05-12 18:50:08 +00:00
my->_client.connect(con);
my->_connected->wait();
return my->_connection;
} FC_CAPTURE_AND_RETHROW( (uri) ) }
2015-05-12 18:50:08 +00:00
websocket_connection_ptr websocket_client::secure_connect( const std::string& uri )
{ try {
if( uri.substr(0,3) == "ws:" )
return connect(uri);
FC_ASSERT( uri.substr(0,4) == "wss:" );
websocketpp::lib::error_code ec;
2015-05-12 18:50:08 +00:00
smy->_uri = uri;
2019-04-26 12:21:11 +00:00
smy->_connected = promise<void>::create("websocket::connect");
2015-05-12 18:50:08 +00:00
smy->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){
auto con = smy->_client.get_con_from_hdl(hdl);
smy->_connection = std::make_shared<detail::websocket_connection_impl<
detail::websocket_tls_client_connection_type>>( con );
2019-04-26 12:21:11 +00:00
smy->_closed = promise<void>::create("websocket::closed");
smy->_connected->set_value();
});
2015-05-12 18:50:08 +00:00
auto con = smy->_client.get_connection( uri, ec );
if( ec )
2019-05-23 21:05:05 +00:00
FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) );
smy->_client.connect(con);
smy->_connected->wait();
return smy->_connection;
} FC_CAPTURE_AND_RETHROW( (uri) ) }
void websocket_client::close()
{
if (my->_hdl)
my->_client.close(*my->_hdl, websocketpp::close::status::normal, "Goodbye");
}
void websocket_client::synchronous_close()
{
close();
if (my->_closed)
my->_closed->wait();
}
void websocket_client::append_header(const std::string& key, const std::string& value)
{
2020-05-01 21:57:21 +00:00
headers.push_back( std::make_pair(key, value) );
}
websocket_connection_ptr websocket_tls_client::connect( const std::string& uri )
{ try {
websocketpp::lib::error_code ec;
2019-04-26 12:21:11 +00:00
my->_connected = promise<void>::create("websocket::connect");
my->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){
auto con = my->_client.get_con_from_hdl(hdl);
my->_connection = std::make_shared<detail::websocket_connection_impl<
detail::websocket_tls_client_connection_type>>( con );
2019-04-26 12:21:11 +00:00
my->_closed = promise<void>::create("websocket::closed");
my->_connected->set_value();
});
auto con = my->_client.get_connection( uri, ec );
if( ec )
{
2019-05-23 21:05:05 +00:00
FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) );
}
my->_client.connect(con);
my->_connected->wait();
return my->_connection;
} FC_CAPTURE_AND_RETHROW( (uri) ) }
} } // fc::http