diff --git a/include/fc/network/http/websocket.hpp b/include/fc/network/http/websocket.hpp index 8837057..8225823 100644 --- a/include/fc/network/http/websocket.hpp +++ b/include/fc/network/http/websocket.hpp @@ -50,8 +50,13 @@ namespace fc { namespace http { void on_connection( const on_connection_handler& handler); void listen( uint16_t port ); void listen( const fc::ip::endpoint& ep ); + uint16_t get_listening_port(); void start_accept(); + void stop_listening(); + void close(); + void synchronous_close(); + private: friend class detail::websocket_server_impl; std::unique_ptr my; @@ -83,6 +88,9 @@ namespace fc { namespace http { websocket_connection_ptr connect( const std::string& uri ); websocket_connection_ptr secure_connect( const std::string& uri ); + + void close(); + void synchronous_close(); private: std::unique_ptr my; std::unique_ptr smy; diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp index 81aa873..75b882b 100644 --- a/src/network/http/websocket.cpp +++ b/src/network/http/websocket.cpp @@ -431,6 +431,7 @@ namespace fc { namespace http { 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; class websocket_client_impl { @@ -484,6 +485,7 @@ namespace fc { namespace http { websocket_client_type _client; websocket_connection_ptr _connection; std::string _uri; + fc::optional _hdl; }; @@ -625,11 +627,35 @@ namespace fc { namespace http { } 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()) ); + 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(); + 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"); + } + + void websocket_server::synchronous_close() + { + close(); + while (!my->_connections.empty()) + fc::yield(); } @@ -678,6 +704,7 @@ namespace fc { namespace http { my->_connected = fc::promise::ptr( new fc::promise("websocket::connect") ); 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>( con ); my->_closed = fc::promise::ptr( new fc::promise("websocket::closed") ); @@ -717,7 +744,20 @@ namespace fc { namespace http { smy->_client.connect(con); smy->_connected->wait(); return smy->_connection; - } FC_CAPTURE_AND_RETHROW( (uri) ) } + } 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(); + } websocket_connection_ptr websocket_tls_client::connect( const std::string& uri ) { try { diff --git a/tests/api_tests.cpp b/tests/api_tests.cpp index b334691..00997ad 100644 --- a/tests/api_tests.cpp +++ b/tests/api_tests.cpp @@ -73,12 +73,14 @@ BOOST_AUTO_TEST_CASE(login_test) { c->set_session_data( wsc ); }); - server->listen( 8090 ); + server->listen( 0 ); + auto listen_port = server->get_listening_port(); server->start_accept(); try { auto client = std::make_shared(); - auto con = client->connect( "ws://localhost:8090" ); + auto con = client->connect( "ws://localhost:" + std::to_string(listen_port) ); + server->stop_listening(); auto apic = std::make_shared(con, MAX_DEPTH); auto remote_login_api = apic->get_remote_api(); auto remote_calc = remote_login_api->get_calc(); @@ -87,8 +89,10 @@ BOOST_AUTO_TEST_CASE(login_test) { BOOST_CHECK_EQUAL(remote_calc->add( 4, 5 ), 9); BOOST_CHECK(remote_triggered); + client->synchronous_close(); + server->synchronous_close(); + fc::usleep(fc::milliseconds(50)); client.reset(); - fc::usleep(fc::milliseconds(100)); server.reset(); } FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW() @@ -110,12 +114,14 @@ BOOST_AUTO_TEST_CASE(optionals_test) { c->set_session_data( wsc ); }); - server->listen( 8090 ); + server->listen( 0 ); + auto listen_port = server->get_listening_port(); server->start_accept(); try { auto client = std::make_shared(); - auto con = client->connect( "ws://localhost:8090" ); + auto con = client->connect( "ws://localhost:" + std::to_string(listen_port) ); + server->stop_listening(); auto apic = std::make_shared(*con, MAX_DEPTH); auto remote_optionals = apic->get_remote_api(); @@ -124,8 +130,10 @@ BOOST_AUTO_TEST_CASE(optionals_test) { BOOST_CHECK_EQUAL(remote_optionals->foo("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); BOOST_CHECK_EQUAL(remote_optionals->foo("a", {}, "c"), "[\"a\",null,\"c\"]"); + client->synchronous_close(); + server->synchronous_close(); + fc::usleep(fc::milliseconds(50)); client.reset(); - fc::usleep(fc::milliseconds(100)); server.reset(); } FC_LOG_AND_RETHROW() } FC_LOG_AND_RETHROW()