Remove pplx::task from public API (#8747)

This commit is contained in:
BrennanConroy 2019-03-29 23:54:44 -07:00 committed by GitHub
parent 61b3504dbe
commit 9d8990a710
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1380 additions and 555 deletions

View File

@ -6,7 +6,6 @@
#include "_exports.h"
#include <memory>
#include <functional>
#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<void> __cdecl start();
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
SIGNALRCLIENT_API pplx::task<void> __cdecl send(const std::string& data);
SIGNALRCLIENT_API void __cdecl send(const std::string& data, std::function<void(std::exception_ptr)> 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<void __cdecl()>& disconnected_callback);
SIGNALRCLIENT_API void __cdecl set_client_config(const signalr_client_config& config);
SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> callback) noexcept;
SIGNALRCLIENT_API connection_state __cdecl get_connection_state() const noexcept;
SIGNALRCLIENT_API std::string __cdecl get_connection_id() const;

View File

@ -6,7 +6,6 @@
#include "_exports.h"
#include <memory>
#include <functional>
#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<void> __cdecl start();
SIGNALRCLIENT_API pplx::task<void> __cdecl stop();
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
SIGNALRCLIENT_API void __cdecl stop(std::function<void(std::exception_ptr)> 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<web::json::value> 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<void(const web::json::value&, std::exception_ptr)> callback = [](const web::json::value&, std::exception_ptr) {}) noexcept;
SIGNALRCLIENT_API pplx::task<void> 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<void(std::exception_ptr)> callback = [](std::exception_ptr) {}) noexcept;
private:
std::shared_ptr<hub_connection_impl> m_pImpl;

View File

@ -7,6 +7,7 @@
#include <sstream>
#include "hub_connection.h"
#include "log_writer.h"
#include <future>
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<web::json::value> 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<logger>());
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]()
{
ucout << U("Enter your message:");
for (;;)
{
std::string message;
std::getline(std::cin, message);
if (message == ":q")
{
break;
}
send_message(connection, message);
}
})
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
{
return connection.stop();
})
.then([](pplx::task<void> stop_task)
std::promise<void> task;
connection.start([&connection, &task](std::exception_ptr exception)
{
if (exception)
{
try
{
stop_task.get();
std::rethrow_exception(exception);
}
catch (const std::exception & ex)
{
ucout << U("exception when starting connection: ") << ex.what() << std::endl;
}
task.set_value();
return;
}
ucout << U("Enter your message:");
for (;;)
{
std::string message;
std::getline(std::cin, message);
if (message == ":q")
{
break;
}
send_message(connection, message);
}
connection.stop([&task](std::exception_ptr exception)
{
try
{
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()

View File

@ -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<void> connection::start()
void connection::start(std::function<void(std::exception_ptr)> callback) noexcept
{
return m_pImpl->start();
m_pImpl->start(callback);
}
pplx::task<void> connection::send(const std::string& data)
void connection::send(const std::string& data, std::function<void(std::exception_ptr)> 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<void> connection::stop()
void connection::stop(std::function<void(std::exception_ptr)> callback) noexcept
{
return m_pImpl->stop();
m_pImpl->stop(callback);
}
connection_state connection::get_connection_state() const noexcept

View File

@ -74,14 +74,14 @@ namespace signalr
change_state(connection_state::disconnected);
}
pplx::task<void> connection_impl::start()
void connection_impl::start(std::function<void(std::exception_ptr)> callback) noexcept
{
{
std::lock_guard<std::mutex> lock(m_stop_lock);
if (!change_state(connection_state::disconnected, connection_state::connecting))
{
return pplx::task_from_exception<void>(
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<void> prev_task)
{
try
{
prev_task.get();
callback(nullptr);
}
catch (...)
{
callback(std::current_exception());
}
});
}
pplx::task<void> connection_impl::start_negotiate(const std::string& url, int redirect_count)
@ -362,7 +374,7 @@ namespace signalr
}
}
pplx::task<void> connection_impl::send(const std::string& data)
void connection_impl::send(const std::string& data, std::function<void(std::exception_ptr)> 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<void>(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<void> 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<void> connection_impl::stop()
void connection_impl::stop(std::function<void(std::exception_ptr)> 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<void> 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);
});
}

View File

@ -5,7 +5,6 @@
#include <atomic>
#include <mutex>
#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<void> start();
pplx::task<void> send(const std::string &data);
pplx::task<void> stop();
void start(std::function<void(std::exception_ptr)> callback) noexcept;
void send(const std::string &data, std::function<void(std::exception_ptr)> callback) noexcept;
void stop(std::function<void(std::exception_ptr)> callback) noexcept;
connection_state get_connection_state() const noexcept;
std::string get_connection_id() const noexcept;

View File

@ -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<void> hub_connection::start()
void hub_connection::start(std::function<void(std::exception_ptr)> 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<void> hub_connection::stop()
void hub_connection::stop(std::function<void(std::exception_ptr)> 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<web::json::value> 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<void(const web::json::value&, std::exception_ptr)> 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<void> 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<void(std::exception_ptr)> 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<void()>& 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);
}
}

View File

@ -96,67 +96,89 @@ namespace signalr
m_subscriptions.insert(std::pair<std::string, std::function<void(const json::value &)>> {event_name, handler});
}
pplx::task<void> hub_connection_impl::start()
void hub_connection_impl::start(std::function<void(std::exception_ptr)> 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<void>();
m_handshakeReceived = false;
std::weak_ptr<hub_connection_impl> weak_connection = shared_from_this();
return m_connection->start()
.then([weak_connection](pplx::task<void> 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<void>(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<void> previous_task)
{
auto connection = weak_connection.lock();
if (!connection)
{
// The connection has been destructed
return pplx::task_from_exception<void>(signalr_exception("the hub connection has been deconstructed"));
}
previous_task.get();
return pplx::task<void>(connection->m_handshakeTask);
})
.then([weak_connection](pplx::task<void> 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<void>(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<void>(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<void> hub_connection_impl::stop()
void hub_connection_impl::stop(std::function<void(std::exception_ptr)> 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<json::value> 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<void(const web::json::value&, std::exception_ptr)> callback) noexcept
{
_ASSERTE(arguments.is_array());
pplx::task_completion_event<json::value> 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<void> 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<void(std::exception_ptr)> callback) noexcept
{
_ASSERTE(arguments.is_array());
pplx::task_completion_event<void> 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<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception)
const std::string& callback_id, std::function<void()> set_completion, std::function<void(const std::exception_ptr)> 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<hub_connection_impl>(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<void> 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());
}
};
}
}

View File

@ -32,11 +32,11 @@ namespace signalr
void on(const std::string& event_name, const std::function<void(const json::value &)>& handler);
pplx::task<json::value> invoke(const std::string& method_name, const json::value& arguments);
pplx::task<void> send(const std::string& method_name, const json::value& arguments);
void invoke(const std::string& method_name, const json::value& arguments, std::function<void(const json::value&, std::exception_ptr)> callback) noexcept;
void send(const std::string& method_name, const json::value& arguments, std::function<void(std::exception_ptr)> callback) noexcept;
pplx::task<void> start();
pplx::task<void> stop();
void start(std::function<void(std::exception_ptr)> callback) noexcept;
void stop(std::function<void(std::exception_ptr)> 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<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception);
std::function<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception) noexcept;
bool invoke_callback(const web::json::value& message);
};
}

View File

@ -17,11 +17,11 @@ namespace signalr
virtual ~transport();
virtual void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) = 0;
virtual void stop(std::function<void(std::exception_ptr)> callback) = 0;
virtual void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept = 0;
virtual void stop(std::function<void(std::exception_ptr)> callback) noexcept = 0;
virtual void on_close(std::function<void(std::exception_ptr)> callback) = 0;
virtual void send(std::string payload, std::function<void(std::exception_ptr)> callback) = 0;
virtual void send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept = 0;
virtual void on_receive(std::function<void(std::string, std::exception_ptr)> callback) = 0;

View File

@ -142,7 +142,7 @@ namespace signalr
}
}
void websocket_transport::start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback)
void websocket_transport::start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> 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<void(std::exception_ptr)> callback)
void websocket_transport::stop(std::function<void(std::exception_ptr)> callback) noexcept
{
std::shared_ptr<websocket_client> websocket_client = nullptr;
@ -252,7 +253,7 @@ namespace signalr
m_process_response_callback = callback;
}
void websocket_transport::send(std::string payload, std::function<void(std::exception_ptr)> callback)
void websocket_transport::send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept
{
safe_get_websocket_client()->send(payload, [callback](std::exception_ptr exception)
{

View File

@ -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<void(std::exception_ptr)> callback) override;
void stop(std::function<void(std::exception_ptr)> callback) override;
void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept override;
void stop(std::function<void(std::exception_ptr)> callback) noexcept override;
void on_close(std::function<void(std::exception_ptr)> callback) override;
void send(std::string payload, std::function<void(std::exception_ptr)> callback) override;
void send(std::string payload, std::function<void(std::exception_ptr)> callback) noexcept override;
void on_receive(std::function<void(std::string, std::exception_ptr)>) override;

View File

@ -30,7 +30,7 @@
<ItemDefinitionGroup>
<ClCompile>
<PreprocessorDefinitions>_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(GoogleTestSubmoduleRoot)googletest\include;..\..\..\..\include\signalrclient;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(GoogleTestSubmoduleRoot)googletest\include;..\..\..\..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
</ClCompile>
<Link>
@ -75,4 +75,4 @@
<Target Name="AfterBuild">
<Exec Command="copy /y &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).dll&quot; &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).dll&quot;" />
</Target>
</Project>
</Project>

View File

@ -39,7 +39,4 @@
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@ -7,8 +7,9 @@
#include <string>
#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<signalr::connection>(url + "raw-connection");
conn->start().get();
auto mre = manual_reset_event<void>();
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<void>();
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));
@ -64,18 +90,37 @@ TEST(connection_tests, send_message_after_connection_restart)
received_event->set();
});
conn->start().get();
conn->stop().get();
conn->start().then([conn]()
auto mre = manual_reset_event<void>();
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();
conn->stop([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
mre.get();
conn->start([&mre](std::exception_ptr exception)
{
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);
});
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<void>();
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);
}

View File

@ -7,9 +7,10 @@
#include <string>
#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<signalr::hub_connection>(url);
auto weak_hub_conn = std::weak_ptr<signalr::hub_connection>(hub_conn);
hub_conn->start().get();
auto mre = manual_reset_event<void>();
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<void>();
hub_conn->start([&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();
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));
@ -58,14 +84,30 @@ TEST(hub_connection_tests, send_message_return)
{
auto hub_conn = std::make_shared<signalr::hub_connection>(url);
auto test = hub_conn->start().then([&hub_conn]()
auto mre = manual_reset_event<web::json::value>();
hub_conn->start([&mre](std::exception_ptr exception)
{
web::json::value obj{};
obj[0] = web::json::value(U("test"));
mre.set(exception);
});
return hub_conn->invoke("returnString", obj);
mre.get();
}).get();
web::json::value obj{};
obj[0] = web::json::value(U("test"));
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();
hub_conn->stop().get();
hub_conn->start().then([&hub_conn]()
auto mre = manual_reset_event<void>();
hub_conn->start([&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->stop([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
mre.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<void>();
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<void>();
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<void>();
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<signalr::hub_connection>(url);
auto test = hub_conn->start().then([&hub_conn]()
auto mre = manual_reset_event<web::json::value>();
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();
}).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);
}
});
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<void>();
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);

View File

@ -37,9 +37,16 @@ TEST(url, negotiate_appended_to_url)
std::make_shared<trace_log_writer>(), std::move(http_client),
std::make_unique<test_transport_factory>(create_test_websocket_client()));
auto mre = manual_reset_event<void>();
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<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
auto hub_connection = create_hub_connection(websocket_client);
hub_connection->start().get();
auto mre = manual_reset_event<void>();
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<void(std::exception_ptr)> callback) { *message = msg; callback(nullptr); });
auto hub_connection = create_hub_connection(websocket_client);
hub_connection->start().get();
auto mre = manual_reset_event<void>();
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<void>();
auto done = false;
hub_connection->start([&mre, &done](std::exception_ptr exception)
{
done = true;
mre.set(exception);
});
pplx::task<void>(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<void(std::string, std::exception_ptr)> callback) { callback("{\"error\":\"bad things\"}\x1e", nullptr); });
auto hub_connection = create_hub_connection(websocket_client);
auto mre = manual_reset_event<void>();
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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
pplx::task<void>(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<void(std::string, std::exception_ptr)> 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<void>();
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<void>();
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<void(std::string, std::exception_ptr)> 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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
mre.get();
}
auto memory_writer = std::dynamic_pointer_cast<memory_log_writer>(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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
mre.get();
auto invoke_mre = manual_reset_event<void>();
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<web::json::value> t;
auto invoke_mre = manual_reset_event<void>();
{
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<void>();
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<void>();
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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
hub_connection->send("method", json::value::array()).get();
mre.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<void>();
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<void>();
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<void>();
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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
mre.set(exception);
});
mre.get();
auto invoke_mre = manual_reset_event<json::value>();
hub_connection->invoke("method", json::value::array(), [&invoke_mre](const json::value& message, std::exception_ptr exception)
{
if (exception)
{
auto t = hub_connection->invoke("method", json::value::array());
callback_registered_event->set();
return t;
}).get();
invoke_mre.set(exception);
}
else
{
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<void>();
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<void>();
hub_connection->start([&mre](std::exception_ptr exception)
{
auto t = hub_connection->invoke("method", json::value::array());
callback_registered_event->set();
return t;
}).get();
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();
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<log_writer> writer(std::make_shared<memory_log_writer>());
auto hub_connection = create_hub_connection(websocket_client, writer, trace_level::info);
hub_connection->start().get();
auto mre = manual_reset_event<void>();
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<void>();
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<void>();
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<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
auto hub_connection = create_hub_connection(websocket_client);
hub_connection->start().get();
auto mre = manual_reset_event<void>();
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<void>();
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<void>();
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)
{

View File

@ -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 <future>
@ -17,7 +16,6 @@ std::shared_ptr<signalr::websocket_client> create_test_websocket_client(
std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> connect_function = [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(nullptr); },
std::function<void(std::function<void(std::exception_ptr)>)> close_function = [](std::function<void(std::exception_ptr)> callback) { callback(nullptr); });
std::unique_ptr<signalr::web_request_factory> create_test_web_request_factory();
std::unique_ptr<signalr::http_client> create_test_http_client();
std::string create_uri();
std::string create_uri(const std::string& query_string);
@ -33,12 +31,12 @@ public:
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 @@ public:
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()

View File

@ -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<void>();
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<void>();
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<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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<void>();
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<memory_log_writer>(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<std::string>();
receive_task_started_tce = pplx::task_completion_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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_log_writer>(), trace_level::none));
auto mre = manual_reset_event<void>();
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<void>();
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<void>();
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<void>();
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();