diff --git a/src/SignalR/clients/cpp/include/signalrclient/connection.h b/src/SignalR/clients/cpp/include/signalrclient/connection.h index c5f48cd3c3ff..b3af11d09f79 100644 --- a/src/SignalR/clients/cpp/include/signalrclient/connection.h +++ b/src/SignalR/clients/cpp/include/signalrclient/connection.h @@ -6,7 +6,6 @@ #include "_exports.h" #include #include -#include "pplx/pplxtasks.h" #include "connection_state.h" #include "trace_level.h" #include "log_writer.h" @@ -29,16 +28,16 @@ namespace signalr connection& operator=(const connection&) = delete; - SIGNALRCLIENT_API pplx::task __cdecl start(); + SIGNALRCLIENT_API void __cdecl start(std::function callback) noexcept; - SIGNALRCLIENT_API pplx::task __cdecl send(const std::string& data); + SIGNALRCLIENT_API void __cdecl send(const std::string& data, std::function callback) noexcept; SIGNALRCLIENT_API void __cdecl set_message_received(const message_received_handler& message_received_callback); SIGNALRCLIENT_API void __cdecl set_disconnected(const std::function& disconnected_callback); SIGNALRCLIENT_API void __cdecl set_client_config(const signalr_client_config& config); - SIGNALRCLIENT_API pplx::task __cdecl stop(); + SIGNALRCLIENT_API void __cdecl stop(std::function callback) noexcept; SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const noexcept; SIGNALRCLIENT_API std::string __cdecl get_connection_id() const; diff --git a/src/SignalR/clients/cpp/include/signalrclient/hub_connection.h b/src/SignalR/clients/cpp/include/signalrclient/hub_connection.h index 4eea630f3398..68597e5420b8 100644 --- a/src/SignalR/clients/cpp/include/signalrclient/hub_connection.h +++ b/src/SignalR/clients/cpp/include/signalrclient/hub_connection.h @@ -6,7 +6,6 @@ #include "_exports.h" #include #include -#include "pplx/pplxtasks.h" #include "cpprest/json.h" #include "connection_state.h" #include "trace_level.h" @@ -31,8 +30,8 @@ namespace signalr hub_connection& operator=(const hub_connection&) = delete; - SIGNALRCLIENT_API pplx::task __cdecl start(); - SIGNALRCLIENT_API pplx::task __cdecl stop(); + SIGNALRCLIENT_API void __cdecl start(std::function callback) noexcept; + SIGNALRCLIENT_API void __cdecl stop(std::function callback) noexcept; SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const; SIGNALRCLIENT_API std::string __cdecl get_connection_id() const; @@ -43,9 +42,9 @@ namespace signalr SIGNALRCLIENT_API void __cdecl on(const std::string& event_name, const method_invoked_handler& handler); - SIGNALRCLIENT_API pplx::task invoke(const std::string& method_name, const web::json::value& arguments = web::json::value::array()); + SIGNALRCLIENT_API void invoke(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function callback = [](const web::json::value&, std::exception_ptr) {}) noexcept; - SIGNALRCLIENT_API pplx::task send(const std::string& method_name, const web::json::value& arguments = web::json::value::array()); + SIGNALRCLIENT_API void send(const std::string& method_name, const web::json::value& arguments = web::json::value::array(), std::function callback = [](std::exception_ptr) {}) noexcept; private: std::shared_ptr m_pImpl; diff --git a/src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.cpp b/src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.cpp index d1e8896986a4..dba7bcc9644f 100644 --- a/src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.cpp +++ b/src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.cpp @@ -7,6 +7,7 @@ #include #include "hub_connection.h" #include "log_writer.h" +#include class logger : public signalr::log_writer { @@ -23,13 +24,16 @@ void send_message(signalr::hub_connection& connection, const std::string& messag args[0] = web::json::value(utility::conversions::to_string_t(message)); // if you get an internal compiler error uncomment the lambda below or install VS Update 4 - connection.invoke("Send", args) - .then([](pplx::task invoke_task) // fire and forget but we need to observe exceptions + connection.invoke("Send", args, [](const web::json::value& value, std::exception_ptr exception) { try { - auto val = invoke_task.get(); - ucout << U("Received: ") << val.serialize() << std::endl; + if (exception) + { + std::rethrow_exception(exception); + } + + ucout << U("Received: ") << value.serialize() << std::endl; } catch (const std::exception &e) { @@ -41,44 +45,63 @@ void send_message(signalr::hub_connection& connection, const std::string& messag void chat() { signalr::hub_connection connection("http://localhost:5000/default", signalr::trace_level::all, std::make_shared()); - connection.on("Send", [](const web::json::value& m) + connection.on("Send", [](const web::json::value & m) { ucout << std::endl << m.at(0).as_string() << /*U(" wrote:") << m.at(1).as_string() <<*/ std::endl << U("Enter your message: "); }); - connection.start() - .then([&connection]() + std::promise task; + connection.start([&connection, &task](std::exception_ptr exception) + { + if (exception) { - ucout << U("Enter your message:"); - for (;;) + try + { + std::rethrow_exception(exception); + } + catch (const std::exception & ex) { - std::string message; - std::getline(std::cin, message); + ucout << U("exception when starting connection: ") << ex.what() << std::endl; + } + task.set_value(); + return; + } - if (message == ":q") - { - break; - } + ucout << U("Enter your message:"); + for (;;) + { + std::string message; + std::getline(std::cin, message); - send_message(connection, message); + if (message == ":q") + { + break; } - }) - .then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid - { - return connection.stop(); - }) - .then([](pplx::task stop_task) + + send_message(connection, message); + } + + connection.stop([&task](std::exception_ptr exception) { try { - stop_task.get(); + if (exception) + { + std::rethrow_exception(exception); + } + ucout << U("connection stopped successfully") << std::endl; } - catch (const std::exception &e) + catch (const std::exception & e) { - ucout << U("exception when starting or stopping connection: ") << e.what() << std::endl; + ucout << U("exception when stopping connection: ") << e.what() << std::endl; } - }).get(); + + task.set_value(); + }); + }); + + task.get_future().get(); } int main() diff --git a/src/SignalR/clients/cpp/src/signalrclient/connection.cpp b/src/SignalR/clients/cpp/src/signalrclient/connection.cpp index 0b7bfb8f3c19..6a5a94064030 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/connection.cpp +++ b/src/SignalR/clients/cpp/src/signalrclient/connection.cpp @@ -16,14 +16,14 @@ namespace signalr // undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/ connection::~connection() = default; - pplx::task connection::start() + void connection::start(std::function callback) noexcept { - return m_pImpl->start(); + m_pImpl->start(callback); } - pplx::task connection::send(const std::string& data) + void connection::send(const std::string& data, std::function callback) noexcept { - return m_pImpl->send(data); + m_pImpl->send(data, callback); } void connection::set_message_received(const message_received_handler& message_received_callback) @@ -41,9 +41,9 @@ namespace signalr m_pImpl->set_client_config(config); } - pplx::task connection::stop() + void connection::stop(std::function callback) noexcept { - return m_pImpl->stop(); + m_pImpl->stop(callback); } connection_state connection::get_connection_state() const noexcept diff --git a/src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp b/src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp index 3ac69d7382da..4348db656278 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp +++ b/src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp @@ -74,14 +74,14 @@ namespace signalr change_state(connection_state::disconnected); } - pplx::task connection_impl::start() + void connection_impl::start(std::function callback) noexcept { { std::lock_guard lock(m_stop_lock); if (!change_state(connection_state::disconnected, connection_state::connecting)) { - return pplx::task_from_exception( - signalr_exception("cannot start a connection that is not in the disconnected state")); + callback(std::make_exception_ptr(signalr_exception("cannot start a connection that is not in the disconnected state"))); + return; } // there should not be any active transport at this point @@ -92,7 +92,19 @@ namespace signalr m_connection_id = ""; } - return start_negotiate(m_base_url, 0); + start_negotiate(m_base_url, 0) + .then([callback](pplx::task prev_task) + { + try + { + prev_task.get(); + callback(nullptr); + } + catch (...) + { + callback(std::current_exception()); + } + }); } pplx::task connection_impl::start_negotiate(const std::string& url, int redirect_count) @@ -362,7 +374,7 @@ namespace signalr } } - pplx::task connection_impl::send(const std::string& data) + void connection_impl::send(const std::string& data, std::function callback) noexcept { // To prevent an (unlikely) condition where the transport is nulled out after we checked the connection_state // and before sending data we store the pointer in the local variable. In this case `send()` will throw but @@ -372,17 +384,17 @@ namespace signalr const auto connection_state = get_connection_state(); if (connection_state != signalr::connection_state::connected || !transport) { - return pplx::task_from_exception(signalr_exception( + callback(std::make_exception_ptr(signalr_exception( std::string("cannot send data when the connection is not in the connected state. current connection state: ") - .append(translate_connection_state(connection_state)))); + .append(translate_connection_state(connection_state))))); + return; } auto logger = m_logger; logger.log(trace_level::info, std::string("sending data: ").append(data)); - pplx::task_completion_event event; - transport->send(data, [logger, event](std::exception_ptr exception) + transport->send(data, [logger, callback](std::exception_ptr exception) mutable { try { @@ -390,7 +402,7 @@ namespace signalr { std::rethrow_exception(exception); } - event.set(); + callback(nullptr); } catch (const std::exception &e) { @@ -399,21 +411,29 @@ namespace signalr std::string("error sending data: ") .append(e.what())); - event.set_exception(exception); + callback(exception); } }); - - return pplx::create_task(event); } - pplx::task connection_impl::stop() + void connection_impl::stop(std::function callback) noexcept { m_logger.log(trace_level::info, "stopping connection"); auto connection = shared_from_this(); - return shutdown() - .then([connection]() + shutdown() + .then([connection, callback](pplx::task prev_task) { + try + { + prev_task.get(); + } + catch (...) + { + callback(std::current_exception()); + return; + } + { // the lock prevents a race where the user calls `stop` on a disconnected connection and calls `start` // on a different thread at the same time. In this case we must not null out the transport if we are @@ -443,6 +463,8 @@ namespace signalr trace_level::errors, std::string("disconnected callback threw an unknown exception")); } + + callback(nullptr); }); } diff --git a/src/SignalR/clients/cpp/src/signalrclient/connection_impl.h b/src/SignalR/clients/cpp/src/signalrclient/connection_impl.h index 1972c4ebc118..b347fed9885b 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/connection_impl.h +++ b/src/SignalR/clients/cpp/src/signalrclient/connection_impl.h @@ -5,7 +5,6 @@ #include #include -#include "cpprest/http_client.h" #include "signalrclient/http_client.h" #include "signalrclient/trace_level.h" #include "signalrclient/connection_state.h" @@ -37,9 +36,9 @@ namespace signalr ~connection_impl(); - pplx::task start(); - pplx::task send(const std::string &data); - pplx::task stop(); + void start(std::function callback) noexcept; + void send(const std::string &data, std::function callback) noexcept; + void stop(std::function callback) noexcept; connection_state get_connection_state() const noexcept; std::string get_connection_id() const noexcept; diff --git a/src/SignalR/clients/cpp/src/signalrclient/hub_connection.cpp b/src/SignalR/clients/cpp/src/signalrclient/hub_connection.cpp index 3dee3cdbb006..c1f68dab6b4d 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/hub_connection.cpp +++ b/src/SignalR/clients/cpp/src/signalrclient/hub_connection.cpp @@ -17,63 +17,95 @@ namespace signalr // undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/ hub_connection::~hub_connection() = default; - pplx::task hub_connection::start() + void hub_connection::start(std::function callback) noexcept { - return m_pImpl->start(); + if (!m_pImpl) + { + callback(std::make_exception_ptr(signalr_exception("start() cannot be called on destructed hub_connection instance"))); + } + + m_pImpl->start(callback); } - pplx::task hub_connection::stop() + void hub_connection::stop(std::function callback) noexcept { - return m_pImpl->stop(); + if (!m_pImpl) + { + callback(std::make_exception_ptr(signalr_exception("stop() cannot be called on destructed hub_connection instance"))); + } + + m_pImpl->stop(callback); } void hub_connection::on(const std::string& event_name, const method_invoked_handler& handler) { if (!m_pImpl) { - throw signalr_exception("on() cannot be called on uninitialized hub_connection instance"); + throw signalr_exception("on() cannot be called on destructed hub_connection instance"); } return m_pImpl->on(event_name, handler); } - pplx::task hub_connection::invoke(const std::string& method_name, const web::json::value& arguments) + void hub_connection::invoke(const std::string& method_name, const web::json::value& arguments, std::function callback) noexcept { if (!m_pImpl) { - throw signalr_exception("invoke() cannot be called on uninitialized hub_connection instance"); + callback(web::json::value(), std::make_exception_ptr(signalr_exception("invoke() cannot be called on destructed hub_connection instance"))); + return; } - return m_pImpl->invoke(method_name, arguments); + return m_pImpl->invoke(method_name, arguments, callback); } - pplx::task hub_connection::send(const std::string& method_name, const web::json::value& arguments) + void hub_connection::send(const std::string& method_name, const web::json::value& arguments, std::function callback) noexcept { if (!m_pImpl) { - throw signalr_exception("send() cannot be called on uninitialized hub_connection instance"); + callback(std::make_exception_ptr(signalr_exception("send() cannot be called on destructed hub_connection instance"))); + return; } - return m_pImpl->send(method_name, arguments); + m_pImpl->send(method_name, arguments, callback); } connection_state hub_connection::get_connection_state() const { + if (!m_pImpl) + { + throw signalr_exception("get_connection_state() cannot be called on destructed hub_connection instance"); + } + return m_pImpl->get_connection_state(); } std::string hub_connection::get_connection_id() const { + if (!m_pImpl) + { + throw signalr_exception("get_connection_id() cannot be called on destructed hub_connection instance"); + } + return m_pImpl->get_connection_id(); } void hub_connection::set_disconnected(const std::function& disconnected_callback) { + if (!m_pImpl) + { + throw signalr_exception("set_disconnected() cannot be called on destructed hub_connection instance"); + } + m_pImpl->set_disconnected(disconnected_callback); } void hub_connection::set_client_config(const signalr_client_config& config) { + if (!m_pImpl) + { + throw signalr_exception("set_client_config() cannot be called on destructed hub_connection instance"); + } + m_pImpl->set_client_config(config); } } diff --git a/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.cpp b/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.cpp index a8d9a83f2113..b0ea676006cb 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.cpp +++ b/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.cpp @@ -96,67 +96,89 @@ namespace signalr m_subscriptions.insert(std::pair> {event_name, handler}); } - pplx::task hub_connection_impl::start() + void hub_connection_impl::start(std::function callback) noexcept { if (m_connection->get_connection_state() != connection_state::disconnected) { - throw signalr_exception( - "the connection can only be started if it is in the disconnected state"); + callback(std::make_exception_ptr(signalr_exception( + "the connection can only be started if it is in the disconnected state"))); + return; } m_connection->set_client_config(m_signalr_client_config); m_handshakeTask = pplx::task_completion_event(); m_handshakeReceived = false; std::weak_ptr weak_connection = shared_from_this(); - return m_connection->start() - .then([weak_connection](pplx::task startTask) + m_connection->start([weak_connection, callback](std::exception_ptr start_exception) { - startTask.get(); auto connection = weak_connection.lock(); if (!connection) { // The connection has been destructed - return pplx::task_from_exception(signalr_exception("the hub connection has been deconstructed")); + callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed"))); + return; } - return connection->m_connection->send("{\"protocol\":\"json\",\"version\":1}\x1e") - .then([weak_connection](pplx::task previous_task) - { - auto connection = weak_connection.lock(); - if (!connection) - { - // The connection has been destructed - return pplx::task_from_exception(signalr_exception("the hub connection has been deconstructed")); - } - previous_task.get(); - return pplx::task(connection->m_handshakeTask); - }) - .then([weak_connection](pplx::task previous_task) + + if (start_exception) + { + connection->m_connection->stop([start_exception, callback, weak_connection](std::exception_ptr) { try - { - previous_task.get(); - return previous_task; - } - catch (std::exception e) { auto connection = weak_connection.lock(); - if (connection) + if (!connection) { - return connection->m_connection->stop() - .then([e]() { - throw e; - }); + callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed"))); + return; } - throw e; + pplx::task(connection->m_handshakeTask).get(); } + catch (...) {} + + callback(start_exception); }); + return; + } + + // TODO: Generate this later when we have the protocol abstraction + auto handshake_request = "{\"protocol\":\"json\",\"version\":1}\x1e"; + connection->m_connection->send(handshake_request, [weak_connection, callback](std::exception_ptr exception) + { + auto connection = weak_connection.lock(); + if (!connection) + { + // The connection has been destructed + callback(std::make_exception_ptr(signalr_exception("the hub connection has been deconstructed"))); + return; + } + + if (exception) + { + callback(exception); + return; + } + + try + { + pplx::task(connection->m_handshakeTask).get(); + callback(nullptr); + } + catch (...) + { + auto handshake_exception = std::current_exception(); + connection->m_connection->stop([callback, handshake_exception](std::exception_ptr) + { + callback(handshake_exception); + }); + } + }); }); } - pplx::task hub_connection_impl::stop() + void hub_connection_impl::stop(std::function callback) noexcept { m_callback_manager.clear(json::value::parse(_XPLATSTR("{ \"error\" : \"connection was stopped before invocation result was received\"}"))); - return m_connection->stop(); + m_connection->stop(callback); } enum MessageType @@ -275,37 +297,29 @@ namespace signalr return true; } - pplx::task hub_connection_impl::invoke(const std::string& method_name, const json::value& arguments) + void hub_connection_impl::invoke(const std::string& method_name, const json::value& arguments, std::function callback) noexcept { _ASSERTE(arguments.is_array()); - pplx::task_completion_event tce; - const auto callback_id = m_callback_manager.register_callback( - create_hub_invocation_callback(m_logger, [tce](const json::value& result) { tce.set(result); }, - [tce](const std::exception_ptr e) { tce.set_exception(e); })); + create_hub_invocation_callback(m_logger, [callback](const json::value& result) { callback(result, nullptr); }, + [callback](const std::exception_ptr e) { callback(json::value(), e); })); invoke_hub_method(method_name, arguments, callback_id, nullptr, - [tce](const std::exception_ptr e){ tce.set_exception(e); }); - - return pplx::create_task(tce); + [callback](const std::exception_ptr e){ callback(json::value(), e); }); } - pplx::task hub_connection_impl::send(const std::string& method_name, const json::value& arguments) + void hub_connection_impl::send(const std::string& method_name, const json::value& arguments, std::function callback) noexcept { _ASSERTE(arguments.is_array()); - pplx::task_completion_event tce; - invoke_hub_method(method_name, arguments, "", - [tce]() { tce.set(); }, - [tce](const std::exception_ptr e){ tce.set_exception(e); }); - - return pplx::create_task(tce); + [callback]() { callback(nullptr); }, + [callback](const std::exception_ptr e){ callback(e); }); } void hub_connection_impl::invoke_hub_method(const std::string& method_name, const json::value& arguments, - const std::string& callback_id, std::function set_completion, std::function set_exception) + const std::string& callback_id, std::function set_completion, std::function set_exception) noexcept { json::value request; request[_XPLATSTR("type")] = json::value(1); @@ -319,28 +333,26 @@ namespace signalr // weak_ptr prevents a circular dependency leading to memory leak and other problems auto weak_hub_connection = std::weak_ptr(shared_from_this()); - m_connection->send(utility::conversions::to_utf8string(request.serialize() + _XPLATSTR('\x1e'))) - .then([set_completion, set_exception, weak_hub_connection, callback_id](pplx::task send_task) + m_connection->send(utility::conversions::to_utf8string(request.serialize() + _XPLATSTR('\x1e')), [set_completion, set_exception, weak_hub_connection, callback_id](std::exception_ptr exception) + { + if (exception) { - try + set_exception(exception); + auto hub_connection = weak_hub_connection.lock(); + if (hub_connection) { - send_task.get(); - if (callback_id.empty()) - { - // complete nonBlocking call - set_completion(); - } + hub_connection->m_callback_manager.remove_callback(callback_id); } - catch (const std::exception&) + } + else + { + if (callback_id.empty()) { - set_exception(std::current_exception()); - auto hub_connection = weak_hub_connection.lock(); - if (hub_connection) - { - hub_connection->m_callback_manager.remove_callback(callback_id); - } + // complete nonBlocking call + set_completion(); } - }); + } + }); } connection_state hub_connection_impl::get_connection_state() const noexcept @@ -383,8 +395,10 @@ namespace signalr std::make_exception_ptr( hub_exception(utility::conversions::to_utf8string(message.at(_XPLATSTR("error")).serialize())))); } - - set_result(json::value::null()); + else + { + set_result(json::value::value()); + } }; } } diff --git a/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.h b/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.h index ca3b6c586a8d..44c42b4614ac 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.h +++ b/src/SignalR/clients/cpp/src/signalrclient/hub_connection_impl.h @@ -32,11 +32,11 @@ namespace signalr void on(const std::string& event_name, const std::function& handler); - pplx::task invoke(const std::string& method_name, const json::value& arguments); - pplx::task send(const std::string& method_name, const json::value& arguments); + void invoke(const std::string& method_name, const json::value& arguments, std::function callback) noexcept; + void send(const std::string& method_name, const json::value& arguments, std::function callback) noexcept; - pplx::task start(); - pplx::task stop(); + void start(std::function callback) noexcept; + void stop(std::function callback) noexcept; connection_state get_connection_state() const noexcept; std::string get_connection_id() const; @@ -62,7 +62,7 @@ namespace signalr void process_message(const std::string& message); void invoke_hub_method(const std::string& method_name, const json::value& arguments, const std::string& callback_id, - std::function set_completion, std::function set_exception); + std::function set_completion, std::function set_exception) noexcept; bool invoke_callback(const web::json::value& message); }; } diff --git a/src/SignalR/clients/cpp/src/signalrclient/transport.h b/src/SignalR/clients/cpp/src/signalrclient/transport.h index 4bb3a5643df4..cde0ed1e8e2f 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/transport.h +++ b/src/SignalR/clients/cpp/src/signalrclient/transport.h @@ -17,11 +17,11 @@ namespace signalr virtual ~transport(); - virtual void start(const std::string& url, transfer_format format, std::function callback) = 0; - virtual void stop(std::function callback) = 0; + virtual void start(const std::string& url, transfer_format format, std::function callback) noexcept = 0; + virtual void stop(std::function callback) noexcept = 0; virtual void on_close(std::function callback) = 0; - virtual void send(std::string payload, std::function callback) = 0; + virtual void send(std::string payload, std::function callback) noexcept = 0; virtual void on_receive(std::function callback) = 0; diff --git a/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.cpp b/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.cpp index 85e4f556eecd..b00e677dcbe6 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.cpp +++ b/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.cpp @@ -142,7 +142,7 @@ namespace signalr } } - void websocket_transport::start(const std::string& url, transfer_format format, std::function callback) + void websocket_transport::start(const std::string& url, transfer_format format, std::function callback) noexcept { web::uri uri(utility::conversions::to_string_t(url)); _ASSERTE(uri.scheme() == _XPLATSTR("ws") || uri.scheme() == _XPLATSTR("wss")); @@ -152,7 +152,8 @@ namespace signalr if (!m_receive_loop_cts.get_token().is_canceled()) { - throw signalr_exception("transport already connected"); + callback(std::make_exception_ptr(signalr_exception("transport already connected"))); + return; } m_logger.log(trace_level::info, @@ -197,7 +198,7 @@ namespace signalr } } - void websocket_transport::stop(std::function callback) + void websocket_transport::stop(std::function callback) noexcept { std::shared_ptr websocket_client = nullptr; @@ -252,7 +253,7 @@ namespace signalr m_process_response_callback = callback; } - void websocket_transport::send(std::string payload, std::function callback) + void websocket_transport::send(std::string payload, std::function callback) noexcept { safe_get_websocket_client()->send(payload, [callback](std::exception_ptr exception) { diff --git a/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.h b/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.h index 8dc362c696c4..45ef6b41e1ea 100644 --- a/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.h +++ b/src/SignalR/clients/cpp/src/signalrclient/websocket_transport.h @@ -26,11 +26,11 @@ namespace signalr transport_type get_transport_type() const noexcept override; - void start(const std::string& url, transfer_format format, std::function callback) override; - void stop(std::function callback) override; + void start(const std::string& url, transfer_format format, std::function callback) noexcept override; + void stop(std::function callback) noexcept override; void on_close(std::function callback) override; - void send(std::string payload, std::function callback) override; + void send(std::string payload, std::function callback) noexcept override; void on_receive(std::function) override; diff --git a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj index 0cb16d4a30aa..9a470efcf9c9 100644 --- a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj +++ b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj @@ -30,7 +30,7 @@ _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(GoogleTestSubmoduleRoot)googletest\include;..\..\..\..\include\signalrclient;%(AdditionalIncludeDirectories) + $(GoogleTestSubmoduleRoot)googletest\include;..\..\..\..\include;%(AdditionalIncludeDirectories) ProgramDatabase @@ -75,4 +75,4 @@ - + \ No newline at end of file diff --git a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj.filters b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj.filters index 80f167f1002a..d441a9f7ab1b 100644 --- a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj.filters +++ b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/Build/VS/signalrclient-e2e-tests.vcxproj.filters @@ -39,7 +39,4 @@ Source Files - - - \ No newline at end of file diff --git a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/connection_tests.cpp b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/connection_tests.cpp index 6e9fc8b99270..b089ca9fcf06 100644 --- a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/connection_tests.cpp +++ b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/connection_tests.cpp @@ -7,8 +7,9 @@ #include #include "cpprest/details/basic_types.h" #include "cpprest/json.h" -#include "connection.h" -#include "hub_connection.h" +#include "signalrclient/connection.h" +#include "signalrclient/hub_connection.h" +#include "../signalrclienttests/test_utils.h" extern std::string url; @@ -16,13 +17,29 @@ TEST(connection_tests, connection_status_start_stop) { auto conn = std::make_shared(url + "raw-connection"); - conn->start().get(); + auto mre = manual_reset_event(); + conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(conn->get_connection_state(), signalr::connection_state::connected); - conn->stop().get(); + conn->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(conn->get_connection_state(), signalr::connection_state::disconnected); - conn->start().get(); + conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(conn->get_connection_state(), signalr::connection_state::connected); } @@ -38,14 +55,23 @@ TEST(connection_tests, send_message) received_event->set(); }); - conn->start().then([conn]() + auto mre = manual_reset_event(); + conn->start([&mre](std::exception_ptr exception) { - web::json::value obj; - obj[U("type")] = web::json::value::number(0); - obj[U("value")] = web::json::value::string(U("test")); - return conn->send(utility::conversions::to_utf8string(obj.serialize())); + mre.set(exception); + }); + + mre.get(); + + web::json::value obj; + obj[U("type")] = web::json::value::number(0); + obj[U("value")] = web::json::value::string(U("test")); + conn->send(utility::conversions::to_utf8string(obj.serialize()), [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); - }).get(); + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -64,18 +90,37 @@ TEST(connection_tests, send_message_after_connection_restart) received_event->set(); }); - conn->start().get(); + auto mre = manual_reset_event(); + conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); - conn->stop().get(); + conn->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); - conn->start().then([conn]() + conn->start([&mre](std::exception_ptr exception) { - web::json::value obj; - obj[U("type")] = web::json::value::number(0); - obj[U("value")] = web::json::value::string(U("test")); - return conn->send(utility::conversions::to_utf8string(obj.serialize())); + mre.set(exception); + }); - }).get(); + mre.get(); + + web::json::value obj; + obj[U("type")] = web::json::value::number(0); + obj[U("value")] = web::json::value::string(U("test")); + conn->send(utility::conversions::to_utf8string(obj.serialize()), [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -88,14 +133,30 @@ TEST(connection_tests, connection_id_start_stop) ASSERT_EQ("", conn->get_connection_id()); - conn->start().get(); + auto mre = manual_reset_event(); + conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); auto connection_id = conn->get_connection_id(); ASSERT_NE(connection_id, ""); - conn->stop().get(); + conn->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(conn->get_connection_id(), connection_id); - conn->start().get(); + conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_NE(conn->get_connection_id(), ""); ASSERT_NE(conn->get_connection_id(), connection_id); } diff --git a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/hub_connection_tests.cpp b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/hub_connection_tests.cpp index f434895febf0..89d1fade27df 100644 --- a/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/hub_connection_tests.cpp +++ b/src/SignalR/clients/cpp/test/signalrclient-e2e-tests/hub_connection_tests.cpp @@ -7,9 +7,10 @@ #include #include "cpprest/details/basic_types.h" #include "cpprest/json.h" -#include "connection.h" -#include "hub_connection.h" -#include "signalr_exception.h" +#include "signalrclient/connection.h" +#include "signalrclient/hub_connection.h" +#include "signalrclient/signalr_exception.h" +#include "../signalrclienttests/test_utils.h" extern std::string url; @@ -18,13 +19,29 @@ TEST(hub_connection_tests, connection_status_start_stop_start) auto hub_conn = std::make_shared(url); auto weak_hub_conn = std::weak_ptr(hub_conn); - hub_conn->start().get(); + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(hub_conn->get_connection_state(), signalr::connection_state::connected); - hub_conn->stop().get(); + hub_conn->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(hub_conn->get_connection_state(), signalr::connection_state::disconnected); - hub_conn->start().get(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(hub_conn->get_connection_state(), signalr::connection_state::connected); } @@ -40,14 +57,23 @@ TEST(hub_connection_tests, send_message) received_event->set(); }); - hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - web::json::value obj{}; - obj[0] = web::json::value(U("test")); + mre.set(exception); + }); + + mre.get(); + + web::json::value obj{}; + obj[0] = web::json::value(U("test")); - return hub_conn->send("invokeWithString", obj); + hub_conn->send("invokeWithString", obj, [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); - }).get(); + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -58,14 +84,30 @@ TEST(hub_connection_tests, send_message_return) { auto hub_conn = std::make_shared(url); - auto test = hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - web::json::value obj{}; - obj[0] = web::json::value(U("test")); + mre.set(exception); + }); + + mre.get(); - return hub_conn->invoke("returnString", obj); + web::json::value obj{}; + obj[0] = web::json::value(U("test")); - }).get(); + hub_conn->invoke("returnString", obj, [&mre](const web::json::value & value, std::exception_ptr exception) + { + if (exception) + { + mre.set(exception); + } + else + { + mre.set(value); + } + }); + + auto test = mre.get(); ASSERT_EQ(test.serialize(), U("\"test\"")); } @@ -82,18 +124,37 @@ TEST(hub_connection_tests, send_message_after_connection_restart) received_event->set(); }); - hub_conn->start().get(); + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); - hub_conn->stop().get(); + mre.get(); - hub_conn->start().then([&hub_conn]() + hub_conn->stop([&mre](std::exception_ptr exception) { - web::json::value obj{}; - obj[0] = web::json::value(U("test")); + mre.set(exception); + }); - return hub_conn->send("invokeWithString", obj); + mre.get(); - }).get(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + web::json::value obj{}; + obj[0] = web::json::value(U("test")); + + hub_conn->send("invokeWithString", obj, [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -112,11 +173,21 @@ TEST(hub_connection_tests, send_message_empty_param) received_event->set(); }); - hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - return hub_conn->invoke("invokeWithEmptyParam"); + mre.set(exception); + }); - }).get(); + mre.get(); + + + hub_conn->invoke("invokeWithEmptyParam", web::json::value::array(), [&mre](const web::json::value &, std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -135,21 +206,29 @@ TEST(hub_connection_tests, send_message_primitive_params) received_event->set(); }); - hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - web::json::value obj{}; - obj[0] = web::json::value(5); - obj[1] = web::json::value(21.05); - obj[2] = web::json::value(8.999999999); - obj[3] = web::json::value(true); - obj[4] = web::json::value('a'); - return hub_conn->send("invokeWithPrimitiveParams", obj); + mre.set(exception); + }); - }).get(); + mre.get(); + + web::json::value obj{}; + obj[0] = web::json::value(5); + obj[1] = web::json::value(21.05); + obj[2] = web::json::value(8.999999999); + obj[3] = web::json::value(true); + obj[4] = web::json::value('a'); + hub_conn->send("invokeWithPrimitiveParams", obj, [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(received_event->wait(2000)); - web::json::value obj{}; obj[0] = web::json::value(6); obj[1] = web::json::value(22.05); obj[2] = web::json::value(9.999999999); @@ -171,21 +250,30 @@ TEST(hub_connection_tests, send_message_complex_type) received_event->set(); }); - hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - web::json::value obj{}; - web::json::value person; - web::json::value address; - address[U("street")] = web::json::value::string(U("main st")); - address[U("zip")] = web::json::value::string(U("98052")); - person[U("address")] = address; - person[U("name")] = web::json::value::string(U("test")); - person[U("age")] = web::json::value::number(15); - obj[0] = person; + mre.set(exception); + }); - return hub_conn->send("invokeWithComplexType", obj); + mre.get(); - }).get(); + web::json::value obj{}; + web::json::value person; + web::json::value address; + address[U("street")] = web::json::value::string(U("main st")); + address[U("zip")] = web::json::value::string(U("98052")); + person[U("address")] = address; + person[U("name")] = web::json::value::string(U("test")); + person[U("age")] = web::json::value::number(15); + obj[0] = person; + + hub_conn->send("invokeWithComplexType", obj, [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(received_event->wait(2000)); @@ -196,21 +284,37 @@ TEST(hub_connection_tests, send_message_complex_type_return) { auto hub_conn = std::make_shared(url); - auto test = hub_conn->start().then([&hub_conn]() + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) { - web::json::value obj{}; - web::json::value person; - web::json::value address; - address[U("street")] = web::json::value::string(U("main st")); - address[U("zip")] = web::json::value::string(U("98052")); - person[U("address")] = address; - person[U("name")] = web::json::value::string(U("test")); - person[U("age")] = web::json::value::number(15); - obj[0] = person; + mre.set(exception); + }); - return hub_conn->invoke("returnComplexType", obj); + mre.get(); + + web::json::value obj{}; + web::json::value person; + web::json::value address; + address[U("street")] = web::json::value::string(U("main st")); + address[U("zip")] = web::json::value::string(U("98052")); + person[U("address")] = address; + person[U("name")] = web::json::value::string(U("test")); + person[U("age")] = web::json::value::number(15); + obj[0] = person; + + hub_conn->invoke("returnComplexType", obj, [&mre](const web::json::value & value, std::exception_ptr exception) + { + if (exception) + { + mre.set(exception); + } + else + { + mre.set(value); + } + }); - }).get(); + auto test = mre.get(); ASSERT_EQ(test.serialize(), U("{\"Address\":{\"Street\":\"main st\",\"Zip\":\"98052\"},\"Age\":15,\"Name\":\"test\"}")); } @@ -224,14 +328,31 @@ TEST(hub_connection_tests, connection_id_start_stop_start) ASSERT_EQ(u8"", hub_conn->get_connection_id()); - hub_conn->start().get(); + auto mre = manual_reset_event(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + connection_id = hub_conn->get_connection_id(); ASSERT_NE(connection_id, u8""); - hub_conn->stop().get(); + hub_conn->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(hub_conn->get_connection_id(), connection_id); - hub_conn->start().get(); + hub_conn->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_NE(hub_conn->get_connection_id(), u8""); ASSERT_NE(hub_conn->get_connection_id(), connection_id); diff --git a/src/SignalR/clients/cpp/test/signalrclienttests/connection_impl_tests.cpp b/src/SignalR/clients/cpp/test/signalrclienttests/connection_impl_tests.cpp index 9da56d17d477..afc68ee0c94a 100644 --- a/src/SignalR/clients/cpp/test/signalrclienttests/connection_impl_tests.cpp +++ b/src/SignalR/clients/cpp/test/signalrclienttests/connection_impl_tests.cpp @@ -38,11 +38,21 @@ TEST(connection_impl_start, cannot_start_non_disconnected_exception) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client); - connection->start().wait(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); try { - connection->start().get(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const signalr_exception& e) @@ -62,21 +72,20 @@ TEST(connection_impl_start, connection_state_is_connecting_when_connection_is_be auto connection = create_connection(websocket_client, writer, trace_level::errors); - auto startTask = connection->start() - // this test is not set up to connect successfully so we have to observe exceptions otherwise - // other tests may fail due to an unobserved exception from the outstanding start task - .then([](pplx::task start_task) - { - try - { - start_task.get(); - } - catch (...) - { } - }); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); ASSERT_EQ(connection->get_connection_state(), connection_state::connecting); - startTask.get(); + + try + { + mre.get(); + ASSERT_TRUE(false); + } + catch (...) { } } TEST(connection_impl_start, connection_state_is_connected_when_connection_established_succesfully) @@ -84,7 +93,12 @@ TEST(connection_impl_start, connection_state_is_connected_when_connection_establ auto websocket_client = create_test_websocket_client( /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + mre.get(); ASSERT_EQ(connection->get_connection_state(), connection_state::connected); } @@ -99,12 +113,18 @@ TEST(connection_impl_start, connection_state_is_disconnected_when_connection_can connection_impl::create(create_uri(), trace_level::none, std::make_shared(), std::move(http_client), std::make_unique()); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); + ASSERT_TRUE(false); } - catch (...) - { } + catch (...) { } ASSERT_EQ(connection->get_connection_state(), connection_state::disconnected); } @@ -118,9 +138,15 @@ TEST(connection_impl_start, throws_for_invalid_uri) auto connection = connection_impl::create(":1\t รค bad_uri&a=b", trace_level::errors, writer, create_test_http_client(), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); } catch (const std::exception&) @@ -147,13 +173,18 @@ TEST(connection_impl_start, start_sets_id_query_string) auto connection = connection_impl::create(create_uri(""), trace_level::errors, writer, create_test_http_client(), std::make_unique(websocket_client)); - try + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) { - connection->start().get(); - } - catch (...) + mre.set(exception); + }); + + try { + mre.get(); + ASSERT_TRUE(false); } + catch (...) { } ASSERT_EQ("id=f7707523-307d-4cba-9abf-3eef701241e8", query_string); } @@ -174,13 +205,18 @@ TEST(connection_impl_start, start_appends_id_query_string) auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, create_test_http_client(), std::make_unique(websocket_client)); - try + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) { - connection->start().get(); - } - catch (...) + mre.set(exception); + }); + + try { + mre.get(); + ASSERT_TRUE(false); } + catch (...) { } ASSERT_EQ("a=b&c=d&id=f7707523-307d-4cba-9abf-3eef701241e8", query_string); } @@ -198,12 +234,18 @@ TEST(connection_impl_start, start_logs_exceptions) connection_impl::create(create_uri(), trace_level::errors, writer, std::move(http_client), std::make_unique()); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); + ASSERT_TRUE(false); } - catch (...) - { } + catch (...) { } auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_FALSE(log_entries.empty()); @@ -224,9 +266,15 @@ TEST(connection_impl_start, start_propagates_exceptions_from_negotiate) connection_impl::create(create_uri(), trace_level::none, std::make_shared(), std::move(http_client), std::make_unique()); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const std::exception &e) @@ -249,9 +297,15 @@ TEST(connection_impl_start, start_fails_if_transport_connect_throws) auto connection = create_connection(websocket_client, writer, trace_level::errors); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const std::exception &e) @@ -280,11 +334,22 @@ TEST(connection_impl_send, send_fails_if_transport_fails_when_receiving_messages auto connection = create_connection(websocket_client, writer, trace_level::errors); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->send("message", [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); try { - connection->send("message").get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const std::exception &e) @@ -320,9 +385,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_request_fails) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const std::exception &e) @@ -361,9 +432,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_has_error) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const signalr_exception & e) @@ -404,9 +481,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_webs connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const signalr_exception & e) @@ -447,9 +530,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_tran connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const signalr_exception & e) @@ -490,9 +579,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_is_invalid) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const std::exception & e) @@ -539,7 +634,13 @@ TEST(connection_impl_start, negotiate_follows_redirect) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("ws://redirected/?id=f7707523-307d-4cba-9abf-3eef701241e8", connectUrl); } @@ -582,7 +683,13 @@ TEST(connection_impl_start, negotiate_redirect_uses_accessToken) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("ws://redirected/?id=f7707523-307d-4cba-9abf-3eef701241e8", connectUrl); ASSERT_EQ("Bearer secret", accessToken); @@ -610,9 +717,16 @@ TEST(connection_impl_start, negotiate_fails_after_too_many_redirects) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); + ASSERT_TRUE(false); } catch (signalr_exception e) { @@ -641,9 +755,15 @@ TEST(connection_impl_start, negotiate_fails_if_ProtocolVersion_in_response) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); } catch (signalr_exception e) { @@ -682,10 +802,25 @@ TEST(connection_impl_start, negotiate_redirect_does_not_overwrite_url) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + mre.get(); ASSERT_EQ(1, redirectCount); - connection->stop().get(); - connection->start().get(); + + connection->stop([&mre](std::exception_ptr) + { + mre.set(); + }); + mre.get(); + + connection->start([&mre](std::exception_ptr) + { + mre.set(); + }); + mre.get(); ASSERT_EQ(2, redirectCount); } @@ -724,9 +859,15 @@ TEST(connection_impl_start, negotiate_redirect_uses_own_query_string) auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); } catch (...) { @@ -757,9 +898,15 @@ TEST(connection_impl_start, start_fails_if_connect_request_times_out) connection_impl::create(create_uri(), trace_level::messages, writer, std::move(http_client), std::make_unique(websocket_client)); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start().get(); + mre.get(); ASSERT_TRUE(false); // exception not thrown } catch (const signalr_exception &e) @@ -782,7 +929,13 @@ TEST(connection_impl_process_response, process_response_logs_messages) }); auto connection = create_connection(websocket_client, writer, trace_level::messages); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); // Need to give the receive loop time to run std::make_shared()->wait(1000); @@ -809,11 +962,20 @@ TEST(connection_impl_send, message_sent) const std::string message{ "Test message" }; - connection->start() - .then([connection, message]() - { - return connection->send(message); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->send(message, [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(message, actual_message); } @@ -823,9 +985,15 @@ TEST(connection_impl_send, send_throws_if_connection_not_connected) auto connection = connection_impl::create(create_uri(), trace_level::none, std::make_shared()); + auto mre = manual_reset_event(); + connection->send("whatever", [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->send("whatever").get(); + mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } catch (const signalr_exception &e) @@ -849,13 +1017,22 @@ TEST(connection_impl_send, exceptions_from_send_logged_and_propagated) auto connection = create_connection(websocket_client, writer, trace_level::errors); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->send("Test message", [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - connection->start() - .then([connection]() - { - return connection->send("Test message"); - }).get(); + mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } @@ -907,7 +1084,13 @@ TEST(connection_impl_set_message_received, callback_invoked_when_message_receive } }); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(message_received_event->wait(5000)); @@ -949,7 +1132,13 @@ TEST(connection_impl_set_message_received, exception_from_callback_caught_and_lo } }); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(message_received_event->wait(5000)); @@ -995,7 +1184,13 @@ TEST(connection_impl_set_message_received, non_std_exception_from_callback_caugh } }); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(message_received_event->wait(5000)); @@ -1012,7 +1207,13 @@ void can_be_set_only_in_disconnected_state(std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); try { @@ -1043,7 +1244,13 @@ TEST(connection_impl_stop, stopping_disconnected_connection_is_no_op) { std::shared_ptr writer{ std::make_shared() }; auto connection = connection_impl::create(create_uri(), trace_level::all, writer); - connection->stop().get(); + auto mre = manual_reset_event(); + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(connection_state::disconnected, connection->get_connection_state()); @@ -1073,19 +1280,35 @@ TEST(connection_impl_stop, stopping_disconnecting_connection_returns_cancelled_t auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start().get(); - auto stop_task = connection->stop(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); try { - connection->stop().get(); + auto mre_stop = manual_reset_event(); + connection->stop([&mre_stop](std::exception_ptr exception) + { + mre_stop.set(exception); + }); + + mre_stop.get(); ASSERT_FALSE(true); // exception expected but not thrown } catch (const pplx::task_canceled&) { } close_event.set(); - stop_task.get(); + mre.get(); ASSERT_EQ(connection_state::disconnected, connection->get_connection_state()); @@ -1105,11 +1328,20 @@ TEST(connection_impl_stop, can_start_and_stop_connection) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_EQ(4U, log_entries.size()); @@ -1128,15 +1360,27 @@ TEST(connection_impl_stop, can_start_and_stop_connection_multiple_times) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start() - .then([connection]() + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) { - return connection->stop(); - }) - .then([connection]() + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) { - return connection->start(); - }).get(); + mre.set(exception); + }); + + mre.get(); + + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); } auto memory_writer = std::dynamic_pointer_cast(writer); @@ -1174,7 +1418,13 @@ TEST(connection_impl_stop, dtor_stops_the_connection) }); auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); } auto memory_writer = std::dynamic_pointer_cast(writer); @@ -1209,20 +1459,24 @@ TEST(connection_impl_stop, stop_cancels_ongoing_start_request) auto writer = std::shared_ptr{std::make_shared()}; auto connection = create_connection(std::make_shared(), writer, trace_level::all); - auto start_task = connection->start(); - connection->stop().get(); - disconnect_completed_event->set(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); - start_task.then([](pplx::task t) + connection->stop([disconnect_completed_event](std::exception_ptr) { - try - { - t.get(); - ASSERT_TRUE(false); // exception expected but not thrown - } - catch (const pplx::task_canceled &) - { } - }).get(); + disconnect_completed_event->set(); + }); + + try + { + mre.get(); + ASSERT_TRUE(false); // exception expected but not thrown + } + catch (const pplx::task_canceled &) + { } ASSERT_EQ(connection_state::disconnected, connection->get_connection_state()); @@ -1257,19 +1511,23 @@ TEST(connection_impl_stop, ongoing_start_request_canceled_if_connection_stopped_ auto connection = connection_impl::create(create_uri(), trace_level::all, writer, std::move(http_client), std::make_unique(websocket_client)); - auto start_task = connection->start(); - connection->stop().get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); - start_task.then([](pplx::task t) + connection->stop([](std::exception_ptr) { - try - { - t.get(); - ASSERT_TRUE(false); // exception expected but not thrown - } - catch (const pplx::task_canceled &) - { } - }).get(); + }); + + try + { + mre.get(); + ASSERT_TRUE(false); // exception expected but not thrown + } + catch (const pplx::task_canceled &) + { } auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_EQ(5U, log_entries.size()) << dump_vector(log_entries); @@ -1289,11 +1547,20 @@ TEST(connection_impl_stop, stop_invokes_disconnected_callback) auto disconnected_invoked = false; connection->set_disconnected([&disconnected_invoked](){ disconnected_invoked = true; }); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_TRUE(disconnected_invoked); } @@ -1320,11 +1587,20 @@ TEST(connection_impl_stop, std_exception_for_disconnected_callback_caught_and_lo connection->set_disconnected([](){ throw std::runtime_error("exception from disconnected"); }); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_EQ(1U, log_entries.size()); @@ -1353,11 +1629,20 @@ TEST(connection_impl_stop, exception_for_disconnected_callback_caught_and_logged connection->set_disconnected([](){ throw 42; }); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_EQ(1U, log_entries.size()); @@ -1400,11 +1685,20 @@ TEST(connection_impl_config, custom_headers_set_in_requests) signalr_client_config.set_http_headers(http_headers); connection->set_client_config(signalr_client_config); - connection->start() - .then([connection]() + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) { - return connection->stop(); - }).get(); + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(connection_state::disconnected, connection->get_connection_state()); } @@ -1426,7 +1720,12 @@ TEST(connection_impl_change_state, change_state_logs) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start().wait(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + mre.get(); auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); ASSERT_FALSE(log_entries.empty()); @@ -1449,22 +1748,21 @@ TEST(connection_id, connection_id_is_set_if_start_fails_but_negotiate_request_su auto connection = create_connection(websocket_client, writer, trace_level::errors); - auto start_task = connection->start() - // this test is not set up to connect successfully so we have to observe exceptions otherwise - // other tests may fail due to an unobserved exception from the outstanding start task - .then([](pplx::task start_task) - { - try - { - start_task.get(); - } - catch (...) - { - } - }); - ASSERT_EQ("", connection->get_connection_id()); - start_task.get(); + + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + try + { + mre.get(); + ASSERT_TRUE(false); + } + catch (...) { } + ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection->get_connection_id()); } @@ -1477,12 +1775,21 @@ TEST(connection_id, can_get_connection_id_when_connection_in_connected_state) auto connection = create_connection(websocket_client, writer, trace_level::state_changes); std::string connection_id; - connection->start() - .then([connection, &connection_id]() - mutable { - connection_id = connection->get_connection_id(); - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre, &connection_id, connection](std::exception_ptr exception) + { + connection_id = connection->get_connection_id(); + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection_id); } @@ -1495,11 +1802,20 @@ TEST(connection_id, can_get_connection_id_after_connection_has_stopped) /* receive function */ [](std::function callback){ callback("{ }\x1e", nullptr); }); auto connection = create_connection(websocket_client, writer, trace_level::state_changes); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection->get_connection_id()); } @@ -1532,29 +1848,31 @@ TEST(connection_id, connection_id_reset_when_starting_connection) connection_impl::create(create_uri(), trace_level::none, std::make_shared(), std::move(http_client), std::make_unique(websocket_client)); - connection->start() - .then([connection]() - { - return connection->stop(); - }).get(); + auto mre = manual_reset_event(); + connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection->get_connection_id()); fail_http_requests = true; - connection->start() - // this test is not set up to connect successfully so we have to observe exceptions otherwise - // other tests may fail due to an unobserved exception from the outstanding start task - .then([](pplx::task start_task) - { - try - { - start_task.get(); - } - catch (...) - { - } - }).get(); + connection->start([&mre](std::exception_ptr) + { + mre.set(); + }); + + mre.get(); ASSERT_EQ("", connection->get_connection_id()); } diff --git a/src/SignalR/clients/cpp/test/signalrclienttests/hub_connection_impl_tests.cpp b/src/SignalR/clients/cpp/test/signalrclienttests/hub_connection_impl_tests.cpp index 8739dc38f14c..39e146f915dd 100644 --- a/src/SignalR/clients/cpp/test/signalrclienttests/hub_connection_impl_tests.cpp +++ b/src/SignalR/clients/cpp/test/signalrclienttests/hub_connection_impl_tests.cpp @@ -37,9 +37,16 @@ TEST(url, negotiate_appended_to_url) std::make_shared(), std::move(http_client), std::make_unique(create_test_websocket_client())); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - hub_connection->start().get(); + mre.get(); + ASSERT_TRUE(false); } catch (const std::exception&) {} @@ -53,7 +60,13 @@ TEST(start, start_starts_connection) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(connection_state::connected, hub_connection->get_connection_state()); } @@ -66,7 +79,13 @@ TEST(start, start_sends_handshake) /* send function */ [message](const std::string& msg, std::function callback) { *message = msg; callback(nullptr); }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("{\"protocol\":\"json\",\"version\":1}\x1e", *message); @@ -86,11 +105,18 @@ TEST(start, start_waits_for_handshake_response) }); auto hub_connection = create_hub_connection(websocket_client); - auto startTask = hub_connection->start(); + auto mre = manual_reset_event(); + auto done = false; + hub_connection->start([&mre, &done](std::exception_ptr exception) + { + done = true; + mre.set(exception); + }); + pplx::task(tceWaitForSend).get(); - ASSERT_FALSE(startTask.is_done()); + ASSERT_FALSE(done); tce.set(); - startTask.get(); + mre.get(); ASSERT_EQ(connection_state::connected, hub_connection->get_connection_state()); } @@ -101,12 +127,18 @@ TEST(start, start_fails_for_handshake_response_with_error) /* receive function */ [](std::function callback) { callback("{\"error\":\"bad things\"}\x1e", nullptr); }); auto hub_connection = create_hub_connection(websocket_client); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + try { - hub_connection->start().get(); + mre.get(); ASSERT_TRUE(false); } - catch (std::exception ex) + catch (const std::exception& ex) { ASSERT_STREQ("Received an error during handshake: bad things", ex.what()); } @@ -131,13 +163,18 @@ TEST(start, start_fails_if_stop_called_before_handshake_response) }); auto hub_connection = create_hub_connection(websocket_client); - auto startTask = hub_connection->start(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + pplx::task(tceWaitForSend).get(); - hub_connection->stop(); + hub_connection->stop([](std::exception_ptr) {}); try { - startTask.get(); + mre.get(); ASSERT_TRUE(false); } catch (std::exception ex) @@ -154,8 +191,20 @@ TEST(stop, stop_stops_connection) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); - hub_connection->stop().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ(connection_state::disconnected, hub_connection->get_connection_state()); } @@ -169,8 +218,20 @@ TEST(stop, disconnected_callback_called_when_hub_connection_stops) auto disconnected_invoked = false; hub_connection->set_disconnected([&disconnected_invoked]() { disconnected_invoked = true; }); - hub_connection->start().get(); - hub_connection->stop().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_TRUE(disconnected_invoked); } @@ -184,7 +245,13 @@ TEST(stop, connection_stopped_when_going_out_of_scope) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto hub_connection = create_hub_connection(websocket_client, writer, trace_level::state_changes); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); } auto memory_writer = std::dynamic_pointer_cast(writer); @@ -226,13 +293,30 @@ TEST(stop, stop_cancels_pending_callbacks) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); - auto t = hub_connection->invoke("method", json::value::array()); - hub_connection->stop(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + auto invoke_mre = manual_reset_event(); + hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value&, std::exception_ptr exception) + { + invoke_mre.set(exception); + }); + + hub_connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); try { - t.get(); + invoke_mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } catch (const signalr_exception& e) @@ -261,17 +345,27 @@ TEST(stop, pending_callbacks_finished_if_hub_connections_goes_out_of_scope) callback(responses[call_number], nullptr); }); - pplx::task t; + auto invoke_mre = manual_reset_event(); { auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); - t = hub_connection->invoke("method", json::value::array()); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value&, std::exception_ptr exception) + { + invoke_mre.set(exception); + }); } try { - t.get(); + invoke_mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } catch (const signalr_exception& e) @@ -307,7 +401,13 @@ TEST(hub_invocation, hub_connection_invokes_users_code_on_hub_invocations) on_broadcast_event->set(); }); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(on_broadcast_event->wait(5000)); ASSERT_EQ("[\"message\",1]", *payload); @@ -333,9 +433,20 @@ TEST(send, creates_correct_payload) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); - hub_connection->send("method", json::value::array()).get(); + hub_connection->send("method", json::value::array(), [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("{\"arguments\":[],\"target\":\"method\",\"type\":1}\x1e", payload); } @@ -365,10 +476,21 @@ TEST(send, does_not_wait_for_server_response) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); // wont block waiting for server response - hub_connection->send("method", json::value::array()).get(); + hub_connection->send("method", json::value::array(), [&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + mre.get(); waitForSend.set(); } @@ -392,11 +514,23 @@ TEST(invoke, creates_correct_payload) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); try { - hub_connection->invoke("method", json::value::array()).get(); + mre.get(); + ASSERT_TRUE(false); } catch (...) { @@ -423,11 +557,22 @@ TEST(invoke, callback_not_called_if_send_throws) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); try { - hub_connection->invoke("method", json::value::array()).get(); + mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } catch (const std::runtime_error& e) @@ -438,7 +583,12 @@ TEST(invoke, callback_not_called_if_send_throws) // stop completes all outstanding callbacks so if we did not remove a callback when `invoke_void` failed an // unobserved exception exception would be thrown. Note that this would happen on a different thread and would // crash the process - hub_connection->stop().get(); + hub_connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); } TEST(invoke, invoke_returns_value_returned_from_the_server) @@ -466,13 +616,31 @@ TEST(invoke, invoke_returns_value_returned_from_the_server) }); auto hub_connection = create_hub_connection(websocket_client); - auto result = hub_connection->start() - .then([hub_connection, callback_registered_event]() + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + auto invoke_mre = manual_reset_event(); + hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value& message, std::exception_ptr exception) + { + if (exception) + { + invoke_mre.set(exception); + } + else { - auto t = hub_connection->invoke("method", json::value::array()); - callback_registered_event->set(); - return t; - }).get(); + invoke_mre.set(message); + } + }); + + callback_registered_event->set(); + + auto result = invoke_mre.get(); ASSERT_EQ(_XPLATSTR("\"abc\""), result.serialize()); } @@ -502,15 +670,25 @@ TEST(invoke, invoke_propagates_errors_from_server_as_hub_exceptions) }); auto hub_connection = create_hub_connection(websocket_client); + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); + + callback_registered_event->set(); + try { - hub_connection->start() - .then([hub_connection, callback_registered_event]() - { - auto t = hub_connection->invoke("method", json::value::array()); - callback_registered_event->set(); - return t; - }).get(); + mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } @@ -545,13 +723,23 @@ TEST(invoke, unblocks_task_when_server_completes_call) }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start() - .then([hub_connection, callback_registered_event]() + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) { - auto t = hub_connection->invoke("method", json::value::array()); - callback_registered_event->set(); - return t; - }).get(); + mre.set(exception); + }); + + callback_registered_event->set(); + + mre.get(); // should not block ASSERT_TRUE(true); @@ -592,7 +780,14 @@ TEST(receive, logs_if_callback_for_given_id_not_found) std::shared_ptr writer(std::make_shared()); auto hub_connection = create_hub_connection(websocket_client, writer, trace_level::info); - hub_connection->start().get(); + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_FALSE(message_received_event->wait(5000)); @@ -628,15 +823,25 @@ TEST(invoke_void, invoke_creates_runtime_error) }); auto hub_connection = create_hub_connection(websocket_client); + + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); + + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); + + callback_registered_event->set(); + try { - hub_connection->start() - .then([hub_connection, callback_registered_event]() - { - auto t = hub_connection->invoke("method", json::value::array()); - callback_registered_event->set(); - return t; - }).get(); + mre.get(); ASSERT_TRUE(false); // exception expected but not thrown } @@ -655,9 +860,21 @@ TEST(connection_id, can_get_connection_id) ASSERT_EQ("", hub_connection->get_connection_id()); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); auto connection_id = hub_connection->get_connection_id(); - hub_connection->stop().get(); + + hub_connection->stop([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection_id); ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", hub_connection->get_connection_id()); @@ -702,7 +919,13 @@ TEST(on, cannot_register_handler_if_connection_not_in_disconnected_state) /* receive function */ [](std::function callback) { callback("{ }\x1e", nullptr); }); auto hub_connection = create_hub_connection(websocket_client); - hub_connection->start().get(); + auto mre = manual_reset_event(); + hub_connection->start([&mre](std::exception_ptr exception) + { + mre.set(exception); + }); + + mre.get(); hub_connection->on("myfunc", [](const web::json::value&) {}); @@ -718,10 +941,16 @@ TEST(invoke, invoke_throws_when_the_underlying_connection_is_not_valid) { auto hub_connection = create_hub_connection(); + auto mre = manual_reset_event(); + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); + try { - hub_connection->invoke("method", json::value::array()).get(); - ASSERT_TRUE(true); // exception expected but not thrown + mre.get(); + ASSERT_TRUE(false); // exception expected but not thrown } catch (const signalr_exception& e) { @@ -733,10 +962,16 @@ TEST(invoke, send_throws_when_the_underlying_connection_is_not_valid) { auto hub_connection = create_hub_connection(); + auto mre = manual_reset_event(); + hub_connection->invoke("method", json::value::array(), [&mre](const json::value&, std::exception_ptr exception) + { + mre.set(exception); + }); + try { - hub_connection->invoke("method", json::value::array()).get(); - ASSERT_TRUE(true); // exception expected but not thrown + mre.get(); + ASSERT_TRUE(false); // exception expected but not thrown } catch (const signalr_exception& e) { diff --git a/src/SignalR/clients/cpp/test/signalrclienttests/test_utils.h b/src/SignalR/clients/cpp/test/signalrclienttests/test_utils.h index eb7d98c25625..bc3daf8d7dec 100644 --- a/src/SignalR/clients/cpp/test/signalrclienttests/test_utils.h +++ b/src/SignalR/clients/cpp/test/signalrclienttests/test_utils.h @@ -5,7 +5,6 @@ #include "cpprest/details/basic_types.h" #include "signalrclient/websocket_client.h" -#include "web_request_factory.h" #include "signalrclient/http_client.h" #include @@ -17,7 +16,6 @@ std::shared_ptr create_test_websocket_client( std::function)> connect_function = [](const std::string&, std::function callback) { callback(nullptr); }, std::function)> close_function = [](std::function callback) { callback(nullptr); }); -std::unique_ptr create_test_web_request_factory(); std::unique_ptr create_test_http_client(); std::string create_uri(); std::string create_uri(const std::string& query_string); @@ -33,12 +31,12 @@ class manual_reset_event m_promise.set_value(value); } - void set_exception(std::exception exception) + void set(const std::exception& exception) { m_promise.set_exception(std::make_exception_ptr(exception)); } - void set_exception(std::exception_ptr exception) + void set(std::exception_ptr exception) { m_promise.set_exception(exception); } @@ -71,14 +69,21 @@ class manual_reset_event m_promise.set_value(); } - void set_exception(std::exception exception) + void set(const std::exception& exception) { m_promise.set_exception(std::make_exception_ptr(exception)); } - void set_exception(std::exception_ptr exception) + void set(std::exception_ptr exception) { - m_promise.set_exception(exception); + if (exception != nullptr) + { + m_promise.set_exception(exception); + } + else + { + m_promise.set_value(); + } } void get() diff --git a/src/SignalR/clients/cpp/test/signalrclienttests/websocket_transport_tests.cpp b/src/SignalR/clients/cpp/test/signalrclienttests/websocket_transport_tests.cpp index 03114e44b18a..543329347ddc 100644 --- a/src/SignalR/clients/cpp/test/signalrclienttests/websocket_transport_tests.cpp +++ b/src/SignalR/clients/cpp/test/signalrclienttests/websocket_transport_tests.cpp @@ -34,9 +34,9 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::info)); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org/connect?param=42", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org/connect?param=42", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -66,7 +66,7 @@ TEST(websocket_transport_connect, connect_propagates_exceptions) auto mre = manual_reset_event(); ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set_exception(exception); + mre.set(exception); }); mre.get(); ASSERT_TRUE(false); @@ -91,7 +91,7 @@ TEST(websocket_transport_connect, connect_logs_exceptions) auto mre = manual_reset_event(); ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set_exception(exception); + mre.set(exception); }); try { @@ -116,9 +116,9 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -126,7 +126,7 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans { ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set_exception(exception); + mre.set(exception); }); mre.get(); ASSERT_TRUE(false); @@ -143,21 +143,21 @@ TEST(websocket_transport_connect, can_connect_after_disconnecting) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); } @@ -177,15 +177,15 @@ TEST(websocket_transport_send, send_creates_and_sends_websocket_messages) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->send("ABC", [&mre](std::exception_ptr) + ws_transport->send("ABC", [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -207,15 +207,15 @@ TEST(websocket_transport_disconnect, disconnect_closes_websocket) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -236,22 +236,15 @@ TEST(websocket_transport_stop, propogates_exception_from_close) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); ws_transport->stop([&mre](std::exception_ptr exception) { - if (exception) - { - mre.set_exception(exception); - } - else - { - mre.set(); - } + mre.set(exception); }); try { @@ -276,17 +269,23 @@ TEST(websocket_transport_disconnect, disconnect_logs_exceptions) auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::errors)); auto mre = manual_reset_event(); - ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); - mre.get(); + + try + { + mre.get(); + ASSERT_TRUE(false); + } + catch (...) {} auto log_entries = std::dynamic_pointer_cast(writer)->get_log_entries(); @@ -344,26 +343,26 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect) pplx::create_task(receive_task_started_tce).get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); receive_task_tce = pplx::task_completion_event(); receive_task_started_tce = pplx::task_completion_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); pplx::create_task(receive_task_started_tce).get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -385,9 +384,9 @@ TEST(websocket_transport_disconnect, disconnect_is_no_op_if_transport_not_starte auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -411,15 +410,15 @@ TEST(websocket_transport_disconnect, exceptions_from_outstanding_receive_task_ob auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared(), trace_level::none)); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); - ws_transport->stop([&mre](std::exception_ptr) + ws_transport->stop([&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -472,9 +471,9 @@ void receive_loop_logs_exception_runner(const T& e, const std::string& expected_ auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level)); auto mre = manual_reset_event(); - ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -512,9 +511,9 @@ TEST(websocket_transport_receive_loop, process_response_callback_called_when_mes ws_transport->on_receive(process_response); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get(); @@ -558,9 +557,9 @@ TEST(websocket_transport_receive_loop, error_callback_called_when_exception_thro ws_transport->on_close(error_callback); auto mre = manual_reset_event(); - ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr) + ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception) { - mre.set(); + mre.set(exception); }); mre.get();