From fb62b6421cc9a265a0173cbc1eb947cc9e51b46c Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 12 May 2015 14:50:08 -0400 Subject: [PATCH] adding ssl support to websocket --- include/fc/network/http/websocket.hpp | 26 ++- include/fc/rpc/websocket_api.hpp | 18 +- src/network/http/websocket.cpp | 246 +++++++++++++++++++++++++- 3 files changed, 283 insertions(+), 7 deletions(-) diff --git a/include/fc/network/http/websocket.hpp b/include/fc/network/http/websocket.hpp index 5c253aa..3df3c90 100644 --- a/include/fc/network/http/websocket.hpp +++ b/include/fc/network/http/websocket.hpp @@ -9,6 +9,7 @@ namespace fc { namespace http { namespace detail { class websocket_server_impl; + class websocket_tls_server_impl; class websocket_client_impl; } // namespace detail; @@ -19,16 +20,19 @@ namespace fc { namespace http { virtual void send_message( const std::string& message ) = 0; virtual void close( int64_t code, const std::string& reason ){}; void on_message( const std::string& message ) { _on_message(message); } + string on_http( const std::string& message ) { return _on_http(message); } void on_message_handler( const std::function& h ) { _on_message = h; } + void on_http_handler( const std::function& h ) { _on_http = h; } void set_session_data( fc::any d ){ _session_data = std::move(d); } fc::any& get_session_data() { return _session_data; } fc::signal closed; private: - fc::any _session_data; - std::function _on_message; + fc::any _session_data; + std::function _on_message; + std::function _on_http; }; typedef std::shared_ptr websocket_connection_ptr; @@ -50,6 +54,24 @@ namespace fc { namespace http { std::unique_ptr my; }; + + class websocket_tls_server + { + public: + websocket_tls_server( const std::string& server_pem = std::string(), + const std::string& ssl_password = std::string()); + ~websocket_tls_server(); + + void on_connection( const on_connection_handler& handler); + void listen( uint16_t port ); + void listen( const fc::ip::endpoint& ep ); + void start_accept(); + + private: + friend class detail::websocket_tls_server_impl; + std::unique_ptr my; + }; + class websocket_client { public: diff --git a/include/fc/rpc/websocket_api.hpp b/include/fc/rpc/websocket_api.hpp index b4ab6c5..7280298 100644 --- a/include/fc/rpc/websocket_api.hpp +++ b/include/fc/rpc/websocket_api.hpp @@ -40,7 +40,8 @@ namespace fc { namespace rpc { return this->receive_call( 0, method_name, args ); }); - _connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg); } ); + _connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg,true); } ); + _connection.on_http_handler( [&]( const std::string& msg ){ return on_message(msg,false); } ); _connection.closed.connect( [this](){ closed(); } ); } @@ -66,7 +67,7 @@ namespace fc { namespace rpc { protected: - void on_message( const std::string& message ) + std::string on_message( const std::string& message, bool send_message = true ) { try { auto var = fc::json::from_string(message); @@ -78,14 +79,21 @@ namespace fc { namespace rpc { auto result = _rpc_state.local_call( call.method, call.params ); if( call.id ) { - _connection.send_message( fc::json::to_string( response( *call.id, result ) ) ); + auto reply = fc::json::to_string( response( *call.id, result ) ); + if( send_message ) + _connection.send_message( reply ); + return reply; } } catch ( const fc::exception& e ) { if( call.id ) { - _connection.send_message( fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ) ); + auto reply = fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ); + if( send_message ) + _connection.send_message( reply ); + + return reply; } } } @@ -96,7 +104,9 @@ namespace fc { namespace rpc { } } catch ( const fc::exception& e ) { wdump((e.to_detail_string())); + return e.to_detail_string(); } + return string(); } fc::http::websocket_connection& _connection; fc::rpc::state _rpc_state; diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp index 0250c8a..63ccd89 100644 --- a/src/network/http/websocket.cpp +++ b/src/network/http/websocket.cpp @@ -57,12 +57,87 @@ namespace fc { namespace http { static const long timeout_open_handshake = 0; }; + 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; + + /// Custom Logging policies + /*typedef websocketpp::log::syslog elog_type; + typedef websocketpp::log::syslog alog_type; + */ + //typedef base::alog_type alog_type; + //typedef base::elog_type elog_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::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + + static const long timeout_open_handshake = 0; + }; + struct asio_tls_stub_log : public websocketpp::config::asio_tls { + typedef asio_tls_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 base::alog_type alog_type; + //typedef base::elog_type elog_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::tls_socket::endpoint socket_type; + }; + + typedef websocketpp::transport::asio::endpoint + transport_type; + }; + using websocketpp::connection_hdl; - typedef websocketpp::server websocket_server_type; + typedef websocketpp::server websocket_server_type; + typedef websocketpp::server websocket_tls_server_type; template class websocket_connection_impl : public websocket_connection @@ -90,12 +165,15 @@ namespace fc { namespace http { T _ws_connection; }; + typedef websocketpp::lib::shared_ptr context_ptr; + class websocket_server_impl { public: websocket_server_impl() :_server_thread( fc::thread::current() ) { + _server.clear_access_channels( websocketpp::log::alevel::all ); _server.init_asio(&fc::asio::default_io_service()); _server.set_reuse_addr(true); @@ -113,6 +191,24 @@ namespace fc { namespace http { current_con->second->on_message( msg->get_payload() ); }).wait(); }); + + _server.set_http_handler( [&]( connection_hdl hdl ){ + _server_thread.async( [&](){ + + auto current_con = std::make_shared>( _server.get_con_from_hdl(hdl) ); + _on_connection( current_con ); + + auto con = _server.get_con_from_hdl(hdl); + wdump(("server")(con->get_request_body())); + auto response = current_con->on_http( con->get_request_body() ); + + con->set_body( response ); + con->set_status( websocketpp::http::status_code::ok ); + current_con->closed(); + + }).wait(); + }); + _server.set_close_handler( [&]( connection_hdl hdl ){ _server_thread.async( [&](){ _connections[hdl]->closed(); @@ -148,6 +244,121 @@ namespace fc { namespace http { fc::promise::ptr _closed; }; + class websocket_tls_server_impl + { + public: + websocket_tls_server_impl( const string& server_pem, const string& ssl_password ) + :_server_thread( fc::thread::current() ) + { + //if( server_pem.size() ) + { + _server.set_tls_init_handler( [=]( websocketpp::connection_hdl hdl ) -> context_ptr { + context_ptr ctx = websocketpp::lib::make_shared(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>( _server.get_con_from_hdl(hdl) ); + _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() ); + wdump(("server")(msg->get_payload())); + current_con->second->on_message( msg->get_payload() ); + }).wait(); + }); + + _server.set_http_handler( [&]( connection_hdl hdl ){ + _server_thread.async( [&](){ + + auto current_con = std::make_shared>( _server.get_con_from_hdl(hdl) ); + try{ + _on_connection( current_con ); + + auto con = _server.get_con_from_hdl(hdl); + wdump(("server")(con->get_request_body())); + auto response = current_con->on_http( con->get_request_body() ); + + con->set_body( response ); + con->set_status( websocketpp::http::status_code::ok ); + } catch ( const fc::exception& e ) + { + edump((e.to_detail_string())); + } + current_con->closed(); + + }).wait(); + }); + + _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(); + } + }); + } + ~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" ); + } + + typedef std::map > con_map; + + con_map _connections; + fc::thread& _server_thread; + websocket_tls_server_type _server; + on_connection_handler _on_connection; + fc::promise::ptr _closed; + }; + + + + + + + + + + + + + typedef websocketpp::client websocket_client_type; typedef websocket_client_type::connection_ptr websocket_client_connection_type; @@ -220,6 +431,39 @@ namespace fc { namespace http { my->_server.start_accept(); } + + + + websocket_tls_server::websocket_tls_server( const string& server_pem, const string& ssl_password ):my( new detail::websocket_tls_server_impl(server_pem, ssl_password) ) {} + websocket_tls_server::~websocket_tls_server(){} + + void websocket_tls_server::on_connection( const on_connection_handler& handler ) + { + my->_on_connection = handler; + } + + void websocket_tls_server::listen( uint16_t port ) + { + my->_server.listen(port); + } + 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()) ); + } + + void websocket_tls_server::start_accept() { + my->_server.start_accept(); + } + + + + + + + + + + websocket_client::websocket_client():my( new detail::websocket_client_impl() ) {} websocket_client::~websocket_client(){ } websocket_connection_ptr websocket_client::connect( const std::string& uri )