diff --git a/include/fc/network/http/websocket.hpp b/include/fc/network/http/websocket.hpp index 9a4f97455..0c765c1f3 100644 --- a/include/fc/network/http/websocket.hpp +++ b/include/fc/network/http/websocket.hpp @@ -15,6 +15,8 @@ namespace fc { namespace http { class websocket_tls_client_impl; } // namespace detail + // TODO refactor code, move stuff used by server or client only to derived class(es), + // E.G. it seems get_request_header and on_http* are for server only. class websocket_connection { public: @@ -32,7 +34,11 @@ namespace fc { namespace http { virtual std::string get_request_header(const std::string& key) = 0; + const std::string& get_remote_endpoint_string()const { return _remote_endpoint; } + fc::signal closed; + protected: + std::string _remote_endpoint; // for logging private: boost::any _session_data; std::function _on_message; @@ -45,7 +51,7 @@ namespace fc { namespace http { class websocket_server { public: - websocket_server(); + websocket_server(const std::string& forward_header_key); ~websocket_server(); void on_connection( const on_connection_handler& handler); @@ -66,15 +72,15 @@ namespace fc { namespace http { 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( const std::string& server_pem, + const std::string& ssl_password, + const std::string& forward_header_key ); ~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; @@ -91,19 +97,11 @@ namespace fc { namespace http { void close(); void synchronous_close(); + void append_header(const std::string& key, const std::string& value); private: std::unique_ptr my; std::unique_ptr smy; - }; - class websocket_tls_client - { - public: - websocket_tls_client( const std::string& ca_filename = "_default" ); - ~websocket_tls_client(); - - websocket_connection_ptr connect( const std::string& uri ); - private: - std::unique_ptr my; + std::vector> headers; }; } } diff --git a/src/log/logger.cpp b/src/log/logger.cpp index 019793ba7..d1004f5ee 100644 --- a/src/log/logger.cpp +++ b/src/log/logger.cpp @@ -99,6 +99,13 @@ namespace fc { void logger::add_appender( const appender::ptr& a ) { my->_appenders.push_back(a); } + void logger::remove_appender( const appender::ptr& a ) + { + auto item = std::find(my->_appenders.begin(), my->_appenders.end(), a); + if (item != my->_appenders.end()) + my->_appenders.erase(item); + } + std::vector logger::get_appenders()const { return my->_appenders; diff --git a/src/network/http/websocket.cpp b/src/network/http/websocket.cpp index 77ff2335e..b2997d944 100644 --- a/src/network/http/websocket.cpp +++ b/src/network/http/websocket.cpp @@ -73,14 +73,6 @@ namespace fc { namespace http { 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; @@ -124,14 +116,6 @@ namespace fc { namespace http { 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; @@ -164,8 +148,6 @@ namespace fc { namespace http { 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; @@ -184,10 +166,6 @@ namespace fc { namespace http { transport_type; }; - - - - using websocketpp::connection_hdl; typedef websocketpp::server websocket_server_type; typedef websocketpp::server websocket_tls_server_type; @@ -196,8 +174,9 @@ namespace fc { namespace http { class websocket_connection_impl : public websocket_connection { public: - websocket_connection_impl( T con ) - :_ws_connection(con){ + websocket_connection_impl( T con ) : _ws_connection(con) + { + _remote_endpoint = con->get_remote_endpoint(); } virtual ~websocket_connection_impl() @@ -206,8 +185,8 @@ namespace fc { namespace http { virtual void send_message( const std::string& message )override { - idump((message)); - //std::cerr<<"send: "<send( message ); FC_ASSERT( !ec, "websocket send failed: ${msg}", ("msg",ec.message() ) ); } @@ -224,34 +203,62 @@ namespace fc { namespace http { T _ws_connection; }; + template + class possibly_proxied_websocket_connection : public websocket_connection_impl + { + public: + possibly_proxied_websocket_connection( T con, const std::string& forward_header_key ) + : websocket_connection_impl(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() + { + } + }; + typedef websocketpp::lib::shared_ptr context_ptr; class websocket_server_impl { public: - websocket_server_impl() - :_server_thread( fc::thread::current() ) + 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 ){ - _server_thread.async( [&](){ - auto new_con = std::make_shared>( _server.get_con_from_hdl(hdl) ); - _on_connection( _connections[hdl] = new_con ); - }).wait(); + _server_thread.async( [this, hdl](){ + auto new_con = std::make_shared>( _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() ); - wdump(("server")(msg->get_payload())); auto payload = msg->get_payload(); std::shared_ptr con = current_con->second; + wlog( "[IN] ${remote_endpoint} ${msg}", + ("remote_endpoint",con->get_remote_endpoint_string()) ("msg",payload) ); ++_pending_messages; - auto f = fc::async([this,con,payload](){ if( _pending_messages ) --_pending_messages; con->on_message( payload ); }); - if( _pending_messages > 100 ) + auto f = fc::async([this,con,payload](){ + if( _pending_messages ) + --_pending_messages; + con->on_message( payload ); + }); + if( _pending_messages > 100 ) f.wait(); }).wait(); }); @@ -263,17 +270,23 @@ namespace fc { namespace http { _server.set_http_handler( [&]( connection_hdl hdl ){ _server_thread.async( [&](){ - auto current_con = std::make_shared>( _server.get_con_from_hdl(hdl) ); + auto con = _server.get_con_from_hdl(hdl); + auto current_con = std::make_shared>( con, _forward_header_key ); _on_connection( current_con ); - auto con = _server.get_con_from_hdl(hdl); con->defer_http_response(); + std::string remote_endpoint = current_con->get_remote_endpoint_string(); std::string request_body = con->get_request_body(); - wdump(("server")(request_body)); + wlog( "[HTTP-IN] ${remote_endpoint} ${msg}", + ("remote_endpoint",remote_endpoint) ("msg",request_body) ); - fc::async([current_con, request_body, con] { + fc::async([current_con, request_body, con, remote_endpoint] { fc::http::reply response = current_con->on_http(request_body); - idump( (response) ); + ilog( "[HTTP-OUT] ${remote_endpoint} ${status} ${msg}", + ("remote_endpoint",remote_endpoint) + ("status",response.status) + ("msg",response.body_as_string) ); con->set_body( std::move( response.body_as_string ) ); con->set_status( websocketpp::http::status_code::value(response.status) ); con->send_http_response(); @@ -340,15 +353,16 @@ namespace fc { namespace http { on_connection_handler _on_connection; fc::promise::ptr _closed; 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 { public: - websocket_tls_server_impl( const string& server_pem, const string& ssl_password ) - :_server_thread( fc::thread::current() ) + 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) { - //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); @@ -372,7 +386,9 @@ namespace fc { namespace http { _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) ); + auto new_con = std::make_shared>( _server.get_con_from_hdl(hdl), + _forward_header_key ); _on_connection( _connections[hdl] = new_con ); }).wait(); }); @@ -382,6 +398,8 @@ namespace fc { namespace http { assert( current_con != _connections.end() ); auto received = msg->get_payload(); std::shared_ptr con = current_con->second; + wlog( "[IN] ${remote_endpoint} ${msg}", + ("remote_endpoint",con->get_remote_endpoint_string()) ("msg",received) ); fc::async([con,received](){ con->on_message( received ); }); }).wait(); }); @@ -389,14 +407,21 @@ namespace fc { namespace http { _server.set_http_handler( [&]( connection_hdl hdl ){ _server_thread.async( [&](){ - auto current_con = std::make_shared>( _server.get_con_from_hdl(hdl) ); + auto con = _server.get_con_from_hdl(hdl); + auto current_con = std::make_shared>( con, _forward_header_key ); 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() ); - idump((response)); + std::string remote_endpoint = current_con->get_remote_endpoint_string(); + std::string request_body = con->get_request_body(); + wlog( "[HTTP-IN] ${remote_endpoint} ${msg}", + ("remote_endpoint",remote_endpoint) ("msg",request_body) ); + auto response = current_con->on_http( request_body ); + ilog( "[HTTP-OUT] ${remote_endpoint} ${status} ${msg}", + ("remote_endpoint",remote_endpoint) + ("status",response.status) + ("msg",response.body_as_string) ); con->set_body( std::move( response.body_as_string ) ); con->set_status( websocketpp::http::status_code::value( response.status ) ); } catch ( const fc::exception& e ) @@ -428,6 +453,7 @@ namespace fc { namespace http { } }); } + ~websocket_tls_server_impl() { if( _server.is_listening() ) @@ -444,26 +470,14 @@ namespace fc { namespace http { websocket_tls_server_type _server; on_connection_handler _on_connection; fc::promise::ptr _closed; + std::string _forward_header_key; // A header like "X-Forwarded-For" (XFF), with data IP:port }; - - - - - - - - - - - - typedef websocketpp::client websocket_client_type; typedef websocketpp::client 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 class generic_websocket_client_impl @@ -477,7 +491,6 @@ namespace fc { namespace http { typename websocketpp::client::message_ptr msg ){ _client_thread.async( [&](){ wdump((msg->get_payload())); - //std::cerr<<"recv: "<get_payload()<<"\n"; auto received = msg->get_payload(); fc::async( [=](){ if( _connection ) @@ -598,7 +611,8 @@ namespace fc { namespace http { } // namespace detail - websocket_server::websocket_server():my( new detail::websocket_server_impl() ) {} + 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 ) @@ -636,9 +650,11 @@ namespace fc { namespace http { 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) ) + {} - - 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 ) @@ -660,12 +676,11 @@ namespace fc { namespace http { } - 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( 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 ) @@ -674,7 +689,6 @@ namespace fc { namespace http { return secure_connect(uri); FC_ASSERT( uri.substr(0,3) == "ws:" ); - // wlog( "connecting to ${uri}", ("uri",uri)); websocketpp::lib::error_code ec; my->_uri = uri; @@ -683,13 +697,17 @@ namespace fc { namespace http { 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->_connection = std::make_shared>( con ); my->_closed = promise::create("websocket::closed"); my->_connected->set_value(); }); auto con = my->_client.get_connection( uri, ec ); + std::for_each(headers.begin(), headers.end(), [con](std::pair in) { + con->append_header(in.first, in.second); + }); if( ec ) FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); my->_client.connect(con); @@ -697,31 +715,36 @@ namespace fc { namespace http { return my->_connection; } FC_CAPTURE_AND_RETHROW( (uri) ) } + // TODO most code in this function is same as ::connect, best refactor 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:" ); - // wlog( "connecting to ${uri}", ("uri",uri)); websocketpp::lib::error_code ec; smy->_uri = uri; smy->_connected = promise::create("websocket::connect"); smy->_client.set_open_handler( [=]( websocketpp::connection_hdl hdl ){ + smy->_hdl = hdl; auto con = smy->_client.get_con_from_hdl(hdl); - smy->_connection = std::make_shared>( con ); + smy->_connection = std::make_shared>( con ); smy->_closed = promise::create("websocket::closed"); smy->_connected->set_value(); }); auto con = smy->_client.get_connection( uri, ec ); + std::for_each(headers.begin(), headers.end(), [con](std::pair in) { + con->append_header(in.first, in.second); + }); if( ec ) FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); smy->_client.connect(con); smy->_connected->wait(); return smy->_connection; - } FC_CAPTURE_AND_RETHROW( (uri) ) } + } FC_CAPTURE_AND_RETHROW( (uri) ) } void websocket_client::close() { @@ -736,28 +759,9 @@ namespace fc { namespace http { my->_closed->wait(); } - websocket_connection_ptr websocket_tls_client::connect( const std::string& uri ) - { try { - // wlog( "connecting to ${uri}", ("uri",uri)); - websocketpp::lib::error_code ec; - - my->_connected = promise::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>( con ); - my->_closed = promise::create("websocket::closed"); - my->_connected->set_value(); - }); - - auto con = my->_client.get_connection( uri, ec ); - if( ec ) - { - FC_ASSERT( !ec, "error: ${e}", ("e",ec.message()) ); - } - my->_client.connect(con); - my->_connected->wait(); - return my->_connection; - } FC_CAPTURE_AND_RETHROW( (uri) ) } + void websocket_client::append_header(const std::string& key, const std::string& value) + { + headers.push_back( std::make_pair(key, value) ); + } } } // fc::http diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9f7dbfdff..268e2d01e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,12 @@ target_link_libraries( hmac_test fc ) add_executable( ecc_test crypto/ecc_test.cpp ) target_link_libraries( ecc_test fc ) +add_executable( ws_test_server ws_test_server.cpp ) +target_link_libraries( ws_test_server fc ) + +add_executable( ws_test_client ws_test_client.cpp ) +target_link_libraries( ws_test_client fc ) + #add_executable( test_aes aes_test.cpp ) #target_link_libraries( test_aes fc ${rt_library} ${pthread_library} ) #add_executable( test_sleep sleep.cpp ) diff --git a/tests/api_tests.cpp b/tests/api_tests.cpp index 50421a030..305d763ed 100644 --- a/tests/api_tests.cpp +++ b/tests/api_tests.cpp @@ -69,7 +69,7 @@ BOOST_AUTO_TEST_CASE(login_test) { try { fc::api calc_api( std::make_shared() ); - auto server = std::make_shared(); + auto server = std::make_shared(""); server->on_connection([&]( const websocket_connection_ptr& c ){ auto wsc = std::make_shared(c, MAX_DEPTH); auto login = std::make_shared(); @@ -116,7 +116,7 @@ BOOST_AUTO_TEST_CASE(optionals_test) { BOOST_CHECK_EQUAL(oapi->bar("a", "b", "c"), "[\"a\",\"b\",\"c\"]"); BOOST_CHECK_EQUAL(oapi->bar("a", {}, "c"), "[\"a\",null,\"c\"]"); - auto server = std::make_shared(); + auto server = std::make_shared(""); server->on_connection([&]( const websocket_connection_ptr& c ){ auto wsc = std::make_shared(c, MAX_DEPTH); wsc->register_api(fc::api(optionals)); diff --git a/tests/network/http/websocket_test.cpp b/tests/network/http/websocket_test.cpp index 8c726e574..d70fa0da1 100644 --- a/tests/network/http/websocket_test.cpp +++ b/tests/network/http/websocket_test.cpp @@ -3,21 +3,112 @@ #include #include +#include +#include BOOST_AUTO_TEST_SUITE(fc_network) BOOST_AUTO_TEST_CASE(websocket_test) { + // set up logging + fc::appender::ptr ca(new fc::console_appender); + fc::logger l = fc::logger::get("rpc"); + l.add_appender( ca ); + fc::http::websocket_client client; fc::http::websocket_connection_ptr s_conn, c_conn; int port; { - fc::http::websocket_server server; + // even with this key set, if the remote does not provide it, it will revert to + // the remote endpoint + fc::http::websocket_server server("MyProxyHeaderKey"); server.on_connection([&]( const fc::http::websocket_connection_ptr& c ){ s_conn = c; c->on_message_handler([&](const std::string& s){ c->send_message("echo: " + s); }); + c->on_http_handler([&](const std::string& s){ + fc::http::reply reply; + reply.body_as_string = "http-echo: " + s; + return reply; + }); + }); + + server.listen( 0 ); + port = server.get_listening_port(); + + server.start_accept(); + + std::string echo; + c_conn = client.connect( "ws://localhost:" + fc::to_string(port) ); + c_conn->on_message_handler([&](const std::string& s){ + echo = s; + }); + c_conn->send_message( "hello world" ); + fc::usleep( fc::milliseconds(100) ); + BOOST_CHECK_EQUAL("echo: hello world", echo); + c_conn->send_message( "again" ); + fc::usleep( fc::milliseconds(100) ); + BOOST_CHECK_EQUAL("echo: again", echo); + + s_conn->close(0, "test"); + fc::usleep( fc::milliseconds(100) ); + BOOST_CHECK_THROW(c_conn->send_message( "again" ), fc::exception); + + c_conn = client.connect( "ws://localhost:" + fc::to_string(port) ); + c_conn->on_message_handler([&](const std::string& s){ + echo = s; + }); + c_conn->send_message( "hello world" ); + fc::usleep( fc::milliseconds(100) ); + BOOST_CHECK_EQUAL("echo: hello world", echo); + + // Testing on_http + std::string server_endpoint_string = "127.0.0.1:" + fc::to_string(port); + fc::ip::endpoint server_endpoint = fc::ip::endpoint::from_string( server_endpoint_string ); + fc::http::connection c_http_conn; + c_http_conn.connect_to( server_endpoint ); + + std::string url = "http://localhost:" + fc::to_string(port); + fc::http::reply reply = c_http_conn.request( "POST", url, "hello world again" ); + std::string reply_body( reply.body.begin(), reply.body.end() ); + BOOST_CHECK_EQUAL(200, reply.status); + BOOST_CHECK_EQUAL("http-echo: hello world again", reply_body ); + } + + BOOST_CHECK_THROW(c_conn->send_message( "again" ), fc::assert_exception); + BOOST_CHECK_THROW(client.connect( "ws://localhost:" + fc::to_string(port) ), fc::exception); + l.remove_appender(ca); +} + +BOOST_AUTO_TEST_CASE(websocket_test_with_proxy_header) +{ + // set up logging + fc::appender::ptr ca(new fc::console_appender); + fc::logger l = fc::logger::get("rpc"); + auto old_log_level = l.get_log_level(); + l.set_log_level( fc::log_level::info ); + l.add_appender( ca ); + + fc::http::websocket_client client; + // add the proxy header element + client.append_header("MyProxyHeaderKey", "MyServer:8080"); + + fc::http::websocket_connection_ptr s_conn, c_conn; + int port; + { + // the server will be on the lookout for the key in the header + fc::http::websocket_server server("MyProxyHeaderKey"); + server.on_connection([&]( const fc::http::websocket_connection_ptr& c ){ + s_conn = c; + c->on_message_handler([&](const std::string& s){ + c->send_message("echo: " + s); + }); + c->on_http_handler([&](const std::string& s){ + fc::http::reply reply; + reply.body_as_string = "http-echo: " + s; + return reply; + }); }); server.listen( 0 ); @@ -48,10 +139,27 @@ BOOST_AUTO_TEST_CASE(websocket_test) c_conn->send_message( "hello world" ); fc::usleep( fc::milliseconds(100) ); BOOST_CHECK_EQUAL("echo: hello world", echo); + + // Testing on_http + std::string server_endpoint_string = "127.0.0.1:" + fc::to_string(port); + fc::ip::endpoint server_endpoint = fc::ip::endpoint::from_string( server_endpoint_string ); + fc::http::connection c_http_conn; + c_http_conn.connect_to( server_endpoint ); + + std::string url = "http://localhost:" + fc::to_string(port); + fc::http::header fwd("MyProxyHeaderKey", "MyServer:8080"); + fc::http::headers headers {fwd}; + fc::http::reply reply = c_http_conn.request( "POST", url, "hello world again", headers ); + std::string reply_body( reply.body.begin(), reply.body.end() ); + BOOST_CHECK_EQUAL(200, reply.status); + BOOST_CHECK_EQUAL("http-echo: hello world again", reply_body ); } BOOST_CHECK_THROW(c_conn->send_message( "again" ), fc::assert_exception); BOOST_CHECK_THROW(client.connect( "ws://localhost:" + fc::to_string(port) ), fc::exception); + + l.remove_appender(ca); + l.set_log_level(old_log_level); } BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/ws_test_client.cpp b/tests/ws_test_client.cpp new file mode 100644 index 000000000..f18db13f6 --- /dev/null +++ b/tests/ws_test_client.cpp @@ -0,0 +1,41 @@ +#include +#include + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + try + { + // set up logging + fc::appender::ptr ca(new fc::console_appender); + fc::logger l = fc::logger::get("rpc"); + l.add_appender( ca ); + + fc::http::websocket_client client; + fc::http::websocket_connection_ptr s_conn, c_conn; + std::string url = argv[1]; + wlog( "Connecting to server at url ${url}", ("url", url) ); + c_conn = client.connect( "ws://" + url ); + + std::string echo; + c_conn->on_message_handler([&](const std::string& s){ + echo = s; + }); + c_conn->send_message( "hello world" ); + fc::usleep( fc::milliseconds(500) ); + if (echo != std::string("echo: hello world") ) + wlog( "Test1 failed, echo value: [${echo}]", ("echo", echo) ); + c_conn->send_message( "again" ); + fc::usleep( fc::milliseconds(500) ); + if ("echo: again" != echo) + wlog( "Test2 failed, echo value: [${echo}]", ("echo", echo) ); + } + catch (const websocketpp::exception& ex) + { + elog( "websocketpp::exception thrown: ${err}", ("err", ex.what()) ); + } +} diff --git a/tests/ws_test_server.cpp b/tests/ws_test_server.cpp new file mode 100644 index 000000000..02be0edd2 --- /dev/null +++ b/tests/ws_test_server.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + // set up logging + fc::appender::ptr ca(new fc::console_appender); + fc::logger l = fc::logger::get("rpc"); + l.add_appender( ca ); + + fc::http::websocket_server server("MyForwardHeaderKey"); + + server.on_connection([&]( const fc::http::websocket_connection_ptr& c ){ + c->on_message_handler([&](const std::string& s){ + c->send_message("echo: " + s); + }); + }); + + server.listen( 0 ); + server.start_accept(); + + wlog( "Port: ${port}", ("port", server.get_listening_port()) ); + + while( true ) + { + fc::usleep( fc::microseconds(100) ); + } +}