Fix the protocol abstraction so different protocols can be supported (#48)
This commit is contained in:
Родитель
f8ab4dfd2a
Коммит
9927ad4db0
|
@ -11,6 +11,7 @@
|
|||
#include "trace_level.h"
|
||||
#include "log_writer.h"
|
||||
#include "signalr_client_config.h"
|
||||
#include "transfer_format.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
@ -31,7 +32,7 @@ namespace signalr
|
|||
|
||||
SIGNALRCLIENT_API void __cdecl start(std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
|
||||
SIGNALRCLIENT_API void __cdecl send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept;
|
||||
SIGNALRCLIENT_API void __cdecl send(const std::string& data, transfer_format transfer_format, 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);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <cstddef>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
@ -34,6 +35,11 @@ namespace signalr
|
|||
*/
|
||||
value();
|
||||
|
||||
/**
|
||||
* Create an object representing a value_type::null value.
|
||||
*/
|
||||
value(std::nullptr_t);
|
||||
|
||||
/**
|
||||
* Create an object representing a default value for the given value_type.
|
||||
*/
|
||||
|
@ -64,6 +70,11 @@ namespace signalr
|
|||
*/
|
||||
value(const char* val);
|
||||
|
||||
/**
|
||||
* Create an object representing a value_type::string with the given string value.
|
||||
*/
|
||||
value(const char* val, size_t length);
|
||||
|
||||
/**
|
||||
* Create an object representing a value_type::array with the given vector of value's.
|
||||
*/
|
||||
|
|
|
@ -14,11 +14,11 @@ namespace signalr
|
|||
public:
|
||||
virtual ~websocket_client() {};
|
||||
|
||||
virtual void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
virtual void start(const std::string& url, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void stop(std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void send(const std::string& payload, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
virtual void send(const std::string& payload, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void receive(std::function<void(const std::string&, std::exception_ptr)> callback) = 0;
|
||||
};
|
||||
|
|
|
@ -10,17 +10,17 @@ namespace signalr
|
|||
{
|
||||
// dtor_clear_arguments will be passed when closing any pending callbacks when the `callback_manager` is
|
||||
// destroyed (i.e. in the dtor)
|
||||
callback_manager::callback_manager(const signalr::value& dtor_clear_arguments)
|
||||
callback_manager::callback_manager(const char* dtor_clear_arguments)
|
||||
: m_dtor_clear_arguments(dtor_clear_arguments)
|
||||
{ }
|
||||
|
||||
callback_manager::~callback_manager()
|
||||
{
|
||||
clear(m_dtor_clear_arguments);
|
||||
clear(m_dtor_clear_arguments.data());
|
||||
}
|
||||
|
||||
// note: callback must not throw except for the `on_progress` callback which will never be invoked from the dtor
|
||||
std::string callback_manager::register_callback(const std::function<void(const signalr::value&)>& callback)
|
||||
std::string callback_manager::register_callback(const std::function<void(const char*, const signalr::value&)>& callback)
|
||||
{
|
||||
auto callback_id = get_callback_id();
|
||||
|
||||
|
@ -35,9 +35,9 @@ namespace signalr
|
|||
|
||||
|
||||
// invokes a callback and stops tracking it if remove callback set to true
|
||||
bool callback_manager::invoke_callback(const std::string& callback_id, const signalr::value& arguments, bool remove_callback)
|
||||
bool callback_manager::invoke_callback(const std::string& callback_id, const char* error, const signalr::value& arguments, bool remove_callback)
|
||||
{
|
||||
std::function<void(const signalr::value& arguments)> callback;
|
||||
std::function<void(const char*, const signalr::value& arguments)> callback;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
@ -56,7 +56,7 @@ namespace signalr
|
|||
}
|
||||
}
|
||||
|
||||
callback(arguments);
|
||||
callback(error, arguments);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -69,14 +69,14 @@ namespace signalr
|
|||
}
|
||||
}
|
||||
|
||||
void callback_manager::clear(const signalr::value& arguments)
|
||||
void callback_manager::clear(const char* error)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_map_lock);
|
||||
|
||||
for (auto& kvp : m_callbacks)
|
||||
{
|
||||
kvp.second(arguments);
|
||||
kvp.second(error, signalr::value());
|
||||
}
|
||||
|
||||
m_callbacks.clear();
|
||||
|
|
|
@ -15,22 +15,22 @@ namespace signalr
|
|||
class callback_manager
|
||||
{
|
||||
public:
|
||||
explicit callback_manager(const signalr::value& dtor_error);
|
||||
explicit callback_manager(const char* dtor_error);
|
||||
~callback_manager();
|
||||
|
||||
callback_manager(const callback_manager&) = delete;
|
||||
callback_manager& operator=(const callback_manager&) = delete;
|
||||
|
||||
std::string register_callback(const std::function<void(const signalr::value&)>& callback);
|
||||
bool invoke_callback(const std::string& callback_id, const signalr::value& arguments, bool remove_callback);
|
||||
std::string register_callback(const std::function<void(const char*, const signalr::value&)>& callback);
|
||||
bool invoke_callback(const std::string& callback_id, const char* error, const signalr::value& arguments, bool remove_callback);
|
||||
bool remove_callback(const std::string& callback_id);
|
||||
void clear(const signalr::value& arguments);
|
||||
void clear(const char* error);
|
||||
|
||||
private:
|
||||
std::atomic<int> m_id { 0 };
|
||||
std::unordered_map<std::string, std::function<void(const signalr::value&)>> m_callbacks;
|
||||
std::unordered_map<std::string, std::function<void(const char*, const signalr::value&)>> m_callbacks;
|
||||
std::mutex m_map_lock;
|
||||
const signalr::value m_dtor_clear_arguments;
|
||||
std::string m_dtor_clear_arguments;
|
||||
|
||||
std::string get_callback_id();
|
||||
};
|
||||
|
|
|
@ -22,9 +22,9 @@ namespace signalr
|
|||
m_pImpl->start(callback);
|
||||
}
|
||||
|
||||
void connection::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
void connection::send(const std::string& data, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
m_pImpl->send(data, callback);
|
||||
m_pImpl->send(data, transfer_format, callback);
|
||||
}
|
||||
|
||||
void connection::set_message_received(const message_received_handler& message_received_callback)
|
||||
|
|
|
@ -455,7 +455,7 @@ namespace signalr
|
|||
auto query_string = "id=" + m_connection_token;
|
||||
auto connect_url = url_builder::build_connect(url, transport->get_transport_type(), query_string);
|
||||
|
||||
transport->start(connect_url, transfer_format::text, [callback, logger](std::exception_ptr exception)
|
||||
transport->start(connect_url, [callback, logger](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
|
@ -479,6 +479,7 @@ namespace signalr
|
|||
|
||||
void connection_impl::process_response(std::string&& response)
|
||||
{
|
||||
// TODO: log binary data better
|
||||
m_logger.log(trace_level::messages,
|
||||
std::string("processing message: ").append(response));
|
||||
|
||||
|
@ -504,7 +505,7 @@ namespace signalr
|
|||
}
|
||||
}
|
||||
|
||||
void connection_impl::send(const std::string& data, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
void connection_impl::send(const std::string& data, transfer_format transfer_format, 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
|
||||
|
@ -524,7 +525,7 @@ namespace signalr
|
|||
|
||||
logger.log(trace_level::info, std::string("sending data: ").append(data));
|
||||
|
||||
transport->send(data, [logger, callback](std::exception_ptr exception)
|
||||
transport->send(data, transfer_format, [logger, callback](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace signalr
|
|||
~connection_impl();
|
||||
|
||||
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 send(const std::string &data, transfer_format transfer_format, 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;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#ifdef USE_CPPRESTSDK
|
||||
#include "default_websocket_client.h"
|
||||
#include <signalrclient/signalr_exception.h>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
@ -28,7 +29,7 @@ namespace signalr
|
|||
: m_underlying_client(create_client_config(signalr_client_config))
|
||||
{ }
|
||||
|
||||
void default_websocket_client::start(const std::string& url, transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
void default_websocket_client::start(const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
m_underlying_client.connect(utility::conversions::to_string_t(url))
|
||||
.then([callback](pplx::task<void> task)
|
||||
|
@ -62,10 +63,18 @@ namespace signalr
|
|||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::send(const std::string& payload, std::function<void(std::exception_ptr)> callback)
|
||||
void default_websocket_client::send(const std::string& payload, signalr::transfer_format transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
web::websockets::client::websocket_outgoing_message msg;
|
||||
msg.set_utf8_message(payload);
|
||||
|
||||
if (transfer_format == signalr::transfer_format::binary)
|
||||
{
|
||||
throw signalr_exception("binary isn't supported currently");
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.set_utf8_message(payload);
|
||||
}
|
||||
m_underlying_client.send(msg)
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
|
@ -88,8 +97,15 @@ namespace signalr
|
|||
{
|
||||
try
|
||||
{
|
||||
|
||||
std::string msg;
|
||||
auto response = task.get();
|
||||
auto msg = response.extract_string().get();
|
||||
if (response.message_type() == web::websockets::client::websocket_message_type::binary_message)
|
||||
{
|
||||
throw signalr_exception("binary isn't supported currently");
|
||||
}
|
||||
msg = response.extract_string().get();
|
||||
|
||||
callback(msg, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
|
|
|
@ -20,9 +20,9 @@ namespace signalr
|
|||
public:
|
||||
explicit default_websocket_client(const signalr_client_config& signalr_client_config = {}) noexcept;
|
||||
|
||||
void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback);
|
||||
void start(const std::string& url, std::function<void(std::exception_ptr)> callback);
|
||||
void stop(std::function<void(std::exception_ptr)> callback);
|
||||
void send(const std::string& payload, std::function<void(std::exception_ptr)> callback);
|
||||
void send(const std::string& payload, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback);
|
||||
void receive(std::function<void(const std::string&, std::exception_ptr)> callback);
|
||||
private:
|
||||
web::websockets::client::websocket_client m_underlying_client;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace signalr
|
|||
{
|
||||
namespace handshake
|
||||
{
|
||||
std::string write_handshake(std::shared_ptr<hub_protocol>& protocol)
|
||||
std::string write_handshake(const std::unique_ptr<hub_protocol>& protocol)
|
||||
{
|
||||
auto map = std::map<std::string, signalr::value>
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace signalr
|
|||
{
|
||||
namespace handshake
|
||||
{
|
||||
std::string write_handshake(std::shared_ptr<hub_protocol>&);
|
||||
std::string write_handshake(const std::unique_ptr<hub_protocol>&);
|
||||
std::tuple<std::string, signalr::value> parse_handshake(const std::string&);
|
||||
}
|
||||
}
|
|
@ -17,16 +17,17 @@ namespace signalr
|
|||
// unnamed namespace makes it invisble outside this translation unit
|
||||
namespace
|
||||
{
|
||||
static std::function<void(const signalr::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
static std::function<void(const char*, const signalr::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
const std::function<void(const signalr::value&)>& set_result,
|
||||
const std::function<void(const std::exception_ptr e)>& set_exception);
|
||||
}
|
||||
|
||||
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
|
||||
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const std::string& url,
|
||||
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
|
||||
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
|
||||
{
|
||||
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, trace_level, log_writer, http_client, websocket_factory, skip_negotiation));
|
||||
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url,
|
||||
trace_level, log_writer, http_client, websocket_factory, skip_negotiation));
|
||||
|
||||
connection->initialize();
|
||||
|
||||
|
@ -38,8 +39,8 @@ namespace signalr
|
|||
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
|
||||
: m_connection(connection_impl::create(url, trace_level, log_writer,
|
||||
http_client, websocket_factory, skip_negotiation)), m_logger(log_writer, trace_level),
|
||||
m_callback_manager(signalr::value(std::map<std::string, signalr::value> { { std::string("error"), std::string("connection went out of scope before invocation result was received") } })),
|
||||
m_handshakeReceived(false), m_disconnected([]() noexcept {}), m_protocol(std::make_shared<json_hub_protocol>())
|
||||
m_callback_manager("connection went out of scope before invocation result was received"),
|
||||
m_handshakeReceived(false), m_disconnected([]() noexcept {}), m_protocol(std::unique_ptr<json_hub_protocol>(new json_hub_protocol()))
|
||||
{}
|
||||
|
||||
void hub_connection_impl::initialize()
|
||||
|
@ -64,7 +65,7 @@ namespace signalr
|
|||
// start may be waiting on the handshake response so we complete it here, this no-ops if already set
|
||||
connection->m_handshakeTask->set(std::make_exception_ptr(signalr_exception("connection closed while handshake was in progress.")));
|
||||
|
||||
connection->m_callback_manager.clear(signalr::value(std::map<std::string, signalr::value> { { std::string("error"), std::string("connection was stopped before invocation result was received") } }));
|
||||
connection->m_callback_manager.clear("connection was stopped before invocation result was received");
|
||||
|
||||
connection->m_disconnected();
|
||||
}
|
||||
|
@ -139,7 +140,7 @@ namespace signalr
|
|||
|
||||
auto handshake_request = handshake::write_handshake(connection->m_protocol);
|
||||
|
||||
connection->m_connection->send(handshake_request, [weak_connection, callback](std::exception_ptr exception)
|
||||
connection->m_connection->send(handshake_request, connection->m_protocol->transfer_format(), [weak_connection, callback](std::exception_ptr exception)
|
||||
{
|
||||
auto connection = weak_connection.lock();
|
||||
if (!connection)
|
||||
|
@ -262,25 +263,15 @@ namespace signalr
|
|||
|
||||
for (const auto& val : messages)
|
||||
{
|
||||
if (!val.is_map())
|
||||
{
|
||||
m_logger.log(trace_level::info, std::string("unexpected response received from the server: ")
|
||||
.append(response));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &obj = val.as_map();
|
||||
|
||||
switch ((int)obj.at("type").as_double())
|
||||
switch (val->message_type)
|
||||
{
|
||||
case message_type::invocation:
|
||||
{
|
||||
auto method = obj.at("target").as_string();
|
||||
auto event = m_subscriptions.find(method);
|
||||
auto invocation = static_cast<invocation_message*>(val.get());
|
||||
auto event = m_subscriptions.find(invocation->target);
|
||||
if (event != m_subscriptions.end())
|
||||
{
|
||||
const auto& args = obj.at("arguments");
|
||||
const auto& args = invocation->arguments;
|
||||
event->second(args);
|
||||
}
|
||||
else
|
||||
|
@ -297,13 +288,8 @@ namespace signalr
|
|||
break;
|
||||
case message_type::completion:
|
||||
{
|
||||
auto error = obj.find("error");
|
||||
auto result = obj.find("result");
|
||||
if (error != obj.end() && result != obj.end())
|
||||
{
|
||||
// TODO: error
|
||||
}
|
||||
invoke_callback(obj);
|
||||
auto completion = static_cast<completion_message*>(val.get());
|
||||
invoke_callback(completion);
|
||||
break;
|
||||
}
|
||||
case message_type::cancel_invocation:
|
||||
|
@ -330,23 +316,19 @@ namespace signalr
|
|||
}
|
||||
}
|
||||
|
||||
bool hub_connection_impl::invoke_callback(const signalr::value& message)
|
||||
bool hub_connection_impl::invoke_callback(completion_message* completion)
|
||||
{
|
||||
if (!message.is_map())
|
||||
const char* error = nullptr;
|
||||
if (!completion->error.empty())
|
||||
{
|
||||
throw signalr_exception("expected object");
|
||||
error = completion->error.data();
|
||||
}
|
||||
|
||||
auto& invocationId = message.as_map().at("invocationId");
|
||||
if (!invocationId.is_string())
|
||||
// TODO: consider transferring ownership of 'result' so that if we run user callbacks on a different thread we don't need to
|
||||
// worry about object lifetime
|
||||
if (!m_callback_manager.invoke_callback(completion->invocation_id, error, completion->result, true))
|
||||
{
|
||||
throw signalr_exception("invocationId is not a string");
|
||||
}
|
||||
|
||||
auto& id = invocationId.as_string();
|
||||
if (!m_callback_manager.invoke_callback(id, message, true))
|
||||
{
|
||||
m_logger.log(trace_level::info, std::string("no callback found for id: ").append(id));
|
||||
m_logger.log(trace_level::info, std::string("no callback found for id: ").append(completion->invocation_id));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -385,24 +367,13 @@ namespace signalr
|
|||
void hub_connection_impl::invoke_hub_method(const std::string& method_name, const signalr::value& arguments,
|
||||
const std::string& callback_id, std::function<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception) noexcept
|
||||
{
|
||||
auto map = std::map<std::string, signalr::value>
|
||||
{
|
||||
{ "type", signalr::value((double)message_type::invocation) },
|
||||
{ "target", signalr::value(method_name) },
|
||||
{ "arguments", arguments }
|
||||
};
|
||||
|
||||
if (!callback_id.empty())
|
||||
{
|
||||
map["invocationId"] = signalr::value(callback_id);
|
||||
}
|
||||
|
||||
auto message = m_protocol->write_message(signalr::value(std::move(map)));
|
||||
invocation_message invocation(callback_id, method_name, arguments);
|
||||
auto message = m_protocol->write_message(&invocation);
|
||||
|
||||
// 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(message, [set_completion, set_exception, weak_hub_connection, callback_id](std::exception_ptr exception)
|
||||
m_connection->send(message, m_protocol->transfer_format(), [set_completion, set_exception, weak_hub_connection, callback_id](std::exception_ptr exception)
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
|
@ -448,29 +419,21 @@ namespace signalr
|
|||
// unnamed namespace makes it invisble outside this translation unit
|
||||
namespace
|
||||
{
|
||||
static std::function<void(const signalr::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
static std::function<void(const char* error, const signalr::value&)> create_hub_invocation_callback(const logger& logger,
|
||||
const std::function<void(const signalr::value&)>& set_result,
|
||||
const std::function<void(const std::exception_ptr)>& set_exception)
|
||||
{
|
||||
return [logger, set_result, set_exception](const signalr::value& message)
|
||||
return [logger, set_result, set_exception](const char* error, const signalr::value& message)
|
||||
{
|
||||
assert(message.is_map());
|
||||
const auto& map = message.as_map();
|
||||
auto found = map.find("result");
|
||||
if (found != map.end())
|
||||
if (error != nullptr)
|
||||
{
|
||||
set_result(found->second);
|
||||
}
|
||||
else if ((found = map.find("error")) != map.end())
|
||||
{
|
||||
assert(found->second.is_string());
|
||||
set_exception(
|
||||
std::make_exception_ptr(
|
||||
hub_exception(found->second.as_string())));
|
||||
hub_exception(error)));
|
||||
}
|
||||
else
|
||||
{
|
||||
set_result(signalr::value());
|
||||
set_result(message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ namespace signalr
|
|||
class hub_connection_impl : public std::enable_shared_from_this<hub_connection_impl>
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<hub_connection_impl> create(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
|
||||
static std::shared_ptr<hub_connection_impl> create(const std::string& url,
|
||||
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
|
||||
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation = false);
|
||||
|
||||
hub_connection_impl(const hub_connection_impl&) = delete;
|
||||
|
@ -59,7 +59,7 @@ namespace signalr
|
|||
std::shared_ptr<completion_event> m_handshakeTask;
|
||||
std::function<void()> m_disconnected;
|
||||
signalr_client_config m_signalr_client_config;
|
||||
std::shared_ptr<hub_protocol> m_protocol;
|
||||
std::unique_ptr<hub_protocol> m_protocol;
|
||||
|
||||
std::mutex m_stop_callback_lock;
|
||||
std::vector<std::function<void(std::exception_ptr)>> m_stop_callbacks;
|
||||
|
@ -70,6 +70,6 @@ namespace signalr
|
|||
|
||||
void invoke_hub_method(const std::string& method_name, const signalr::value& arguments, const std::string& callback_id,
|
||||
std::function<void()> set_completion, std::function<void(const std::exception_ptr)> set_exception) noexcept;
|
||||
bool invoke_callback(const signalr::value& message);
|
||||
bool invoke_callback(completion_message* completion);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,16 +5,74 @@
|
|||
#pragma once
|
||||
|
||||
#include "signalrclient/signalr_value.h"
|
||||
#include "signalrclient/transfer_format.h"
|
||||
#include "message_type.h"
|
||||
#include <memory>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
struct hub_message
|
||||
{
|
||||
hub_message(signalr::message_type message_type) : message_type(message_type) {}
|
||||
|
||||
virtual ~hub_message() {}
|
||||
|
||||
signalr::message_type message_type;
|
||||
};
|
||||
|
||||
struct hub_invocation_message : hub_message
|
||||
{
|
||||
hub_invocation_message(const std::string& invocation_id, signalr::message_type message_type)
|
||||
: hub_message(message_type), invocation_id(invocation_id)
|
||||
{ }
|
||||
|
||||
std::string invocation_id;
|
||||
};
|
||||
|
||||
struct invocation_message : hub_invocation_message
|
||||
{
|
||||
invocation_message(const std::string& invocation_id, const std::string& target,
|
||||
const signalr::value& args, const std::vector<std::string>& stream_ids = std::vector<std::string>())
|
||||
: hub_invocation_message(invocation_id, signalr::message_type::invocation), target(target), arguments(args), stream_ids(stream_ids)
|
||||
{ }
|
||||
|
||||
invocation_message(std::string&& invocation_id, std::string&& target,
|
||||
signalr::value&& args, std::vector<std::string>&& stream_ids = std::vector<std::string>())
|
||||
: hub_invocation_message(invocation_id, signalr::message_type::invocation), target(target), arguments(args), stream_ids(stream_ids)
|
||||
{ }
|
||||
|
||||
std::string target;
|
||||
signalr::value arguments;
|
||||
std::vector<std::string> stream_ids;
|
||||
};
|
||||
|
||||
struct completion_message : hub_invocation_message
|
||||
{
|
||||
completion_message(const std::string& invocation_id, const std::string& error, const signalr::value& result)
|
||||
: hub_invocation_message(invocation_id, signalr::message_type::completion), error(error), result(result)
|
||||
{ }
|
||||
|
||||
completion_message(std::string&& invocation_id, std::string&& error, signalr::value&& result)
|
||||
: hub_invocation_message(invocation_id, signalr::message_type::completion), error(error), result(result)
|
||||
{ }
|
||||
|
||||
std::string error;
|
||||
signalr::value result;
|
||||
};
|
||||
|
||||
struct ping_message : hub_message
|
||||
{
|
||||
ping_message() : hub_message(signalr::message_type::ping) {}
|
||||
};
|
||||
|
||||
class hub_protocol
|
||||
{
|
||||
public:
|
||||
virtual std::string write_message(const signalr::value&) const = 0;
|
||||
virtual std::vector<signalr::value> parse_messages(const std::string&) const = 0;
|
||||
virtual std::string write_message(const hub_message*) const = 0;
|
||||
virtual std::vector<std::unique_ptr<hub_message>> parse_messages(const std::string&) const = 0;
|
||||
virtual const std::string& name() const = 0;
|
||||
virtual int version() const = 0;
|
||||
virtual signalr::transfer_format transfer_format() const = 0;
|
||||
virtual ~hub_protocol() {}
|
||||
};
|
||||
}
|
|
@ -10,20 +10,67 @@
|
|||
|
||||
namespace signalr
|
||||
{
|
||||
std::string signalr::json_hub_protocol::write_message(const signalr::value& hub_message) const
|
||||
std::string signalr::json_hub_protocol::write_message(const hub_message* hub_message) const
|
||||
{
|
||||
return Json::writeString(getJsonWriter(), createJson(hub_message)) + record_separator;
|
||||
Json::Value object(Json::ValueType::objectValue);
|
||||
|
||||
#pragma warning (push)
|
||||
#pragma warning (disable: 4061)
|
||||
switch (hub_message->message_type)
|
||||
{
|
||||
case message_type::invocation:
|
||||
{
|
||||
auto invocation = static_cast<invocation_message const*>(hub_message);
|
||||
object["type"] = (int)invocation->message_type;
|
||||
if (!invocation->invocation_id.empty())
|
||||
{
|
||||
object["invocationId"] = invocation->invocation_id;
|
||||
}
|
||||
object["target"] = invocation->target;
|
||||
object["arguments"] = createJson(invocation->arguments);
|
||||
// TODO: streamIds
|
||||
|
||||
break;
|
||||
}
|
||||
case message_type::completion:
|
||||
{
|
||||
auto completion = static_cast<completion_message const*>(hub_message);
|
||||
object["type"] = (int)completion->message_type;
|
||||
object["invocationId"] = completion->invocation_id;
|
||||
if (!completion->error.empty())
|
||||
{
|
||||
object["error"] = completion->error;
|
||||
}
|
||||
else
|
||||
{
|
||||
object["result"] = createJson(completion->result);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case message_type::ping:
|
||||
{
|
||||
auto ping = static_cast<ping_message const*>(hub_message);
|
||||
object["type"] = (int)ping->message_type;
|
||||
break;
|
||||
}
|
||||
// TODO: other message types
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#pragma warning (pop)
|
||||
|
||||
return Json::writeString(getJsonWriter(), object) + record_separator;
|
||||
}
|
||||
|
||||
std::vector<signalr::value> json_hub_protocol::parse_messages(const std::string& message) const
|
||||
std::vector<std::unique_ptr<hub_message>> json_hub_protocol::parse_messages(const std::string& message) const
|
||||
{
|
||||
std::vector<signalr::value> vec;
|
||||
std::vector<std::unique_ptr<hub_message>> vec;
|
||||
size_t offset = 0;
|
||||
auto pos = message.find(record_separator, offset);
|
||||
while (pos != std::string::npos)
|
||||
{
|
||||
auto hub_message = parse_message(message.c_str() + offset, pos - offset);
|
||||
vec.push_back(std::move(hub_message));
|
||||
vec.emplace_back(std::move(hub_message));
|
||||
|
||||
offset = pos + 1;
|
||||
pos = message.find(record_separator, offset);
|
||||
|
@ -33,7 +80,7 @@ namespace signalr
|
|||
return vec;
|
||||
}
|
||||
|
||||
signalr::value json_hub_protocol::parse_message(const char* begin, size_t length) const
|
||||
std::unique_ptr<hub_message> json_hub_protocol::parse_message(const char* begin, size_t length) const
|
||||
{
|
||||
Json::Value root;
|
||||
auto reader = getJsonReader();
|
||||
|
@ -44,6 +91,7 @@ namespace signalr
|
|||
throw signalr_exception(errors);
|
||||
}
|
||||
|
||||
// TODO: manually go through the json object to avoid short-lived allocations
|
||||
auto value = createValue(root);
|
||||
|
||||
if (!value.is_map())
|
||||
|
@ -59,6 +107,8 @@ namespace signalr
|
|||
throw signalr_exception("Field 'type' not found");
|
||||
}
|
||||
|
||||
std::unique_ptr<hub_message> hub_message;
|
||||
|
||||
switch ((int)found->second.as_double())
|
||||
{
|
||||
case message_type::invocation:
|
||||
|
@ -68,12 +118,97 @@ namespace signalr
|
|||
{
|
||||
throw signalr_exception("Field 'target' not found for 'invocation' message");
|
||||
}
|
||||
if (!found->second.is_string())
|
||||
{
|
||||
throw signalr_exception("Expected 'target' to be of type 'string'");
|
||||
}
|
||||
|
||||
|
||||
found = obj.find("arguments");
|
||||
if (found == obj.end())
|
||||
{
|
||||
throw signalr_exception("Field 'arguments' not found for 'invocation' message");
|
||||
}
|
||||
if (!found->second.is_array())
|
||||
{
|
||||
throw signalr_exception("Expected 'arguments' to be of type 'array'");
|
||||
}
|
||||
|
||||
std::string invocation_id;
|
||||
found = obj.find("invocationId");
|
||||
if (found == obj.end())
|
||||
{
|
||||
invocation_id = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!found->second.is_string())
|
||||
{
|
||||
throw signalr_exception("Expected 'invocationId' to be of type 'string'");
|
||||
}
|
||||
invocation_id = found->second.as_string();
|
||||
}
|
||||
|
||||
hub_message = std::unique_ptr<signalr::hub_message>(new invocation_message(invocation_id,
|
||||
obj.find("target")->second.as_string(), obj.find("arguments")->second.as_array()));
|
||||
|
||||
break;
|
||||
}
|
||||
case message_type::completion:
|
||||
{
|
||||
bool has_result = false;
|
||||
signalr::value result;
|
||||
found = obj.find("result");
|
||||
if (found != obj.end())
|
||||
{
|
||||
has_result = true;
|
||||
result = found->second;
|
||||
}
|
||||
|
||||
std::string error;
|
||||
found = obj.find("error");
|
||||
if (found == obj.end())
|
||||
{
|
||||
error = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (found->second.is_string())
|
||||
{
|
||||
error = found->second.as_string();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw signalr_exception("Expected 'error' to be of type 'string'");
|
||||
}
|
||||
}
|
||||
|
||||
found = obj.find("invocationId");
|
||||
if (found == obj.end())
|
||||
{
|
||||
throw signalr_exception("Field 'invocationId' not found for 'completion' message");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!found->second.is_string())
|
||||
{
|
||||
throw signalr_exception("Expected 'invocationId' to be of type 'string'");
|
||||
}
|
||||
}
|
||||
|
||||
if (!error.empty() && has_result)
|
||||
{
|
||||
throw signalr_exception("The 'error' and 'result' properties are mutually exclusive.");
|
||||
}
|
||||
|
||||
hub_message = std::unique_ptr<signalr::hub_message>(new completion_message(obj.find("invocationId")->second.as_string(),
|
||||
error, result));
|
||||
|
||||
break;
|
||||
}
|
||||
case message_type::ping:
|
||||
{
|
||||
hub_message = std::unique_ptr<signalr::hub_message>(new ping_message());
|
||||
break;
|
||||
}
|
||||
// TODO: other message types
|
||||
|
@ -83,6 +218,6 @@ namespace signalr
|
|||
break;
|
||||
}
|
||||
|
||||
return value;
|
||||
return hub_message;
|
||||
}
|
||||
}
|
|
@ -12,20 +12,27 @@ namespace signalr
|
|||
class json_hub_protocol : public hub_protocol
|
||||
{
|
||||
public:
|
||||
std::string write_message(const signalr::value&) const;
|
||||
std::vector<signalr::value> parse_messages(const std::string&) const;
|
||||
std::string write_message(const hub_message*) const;
|
||||
std::vector<std::unique_ptr<hub_message>> parse_messages(const std::string&) const;
|
||||
|
||||
const std::string& name() const
|
||||
{
|
||||
return m_protocol_name;
|
||||
}
|
||||
|
||||
int version() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
signalr::transfer_format transfer_format() const
|
||||
{
|
||||
return signalr::transfer_format::text;
|
||||
}
|
||||
|
||||
~json_hub_protocol() {}
|
||||
private:
|
||||
signalr::value parse_message(const char* begin, size_t length) const;
|
||||
std::unique_ptr<hub_message> parse_message(const char* begin, size_t length) const;
|
||||
|
||||
std::string m_protocol_name = "json";
|
||||
};
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace signalr
|
|||
|
||||
value::value() : mType(value_type::null) {}
|
||||
|
||||
value::value(std::nullptr_t) : mType(value_type::null) {}
|
||||
|
||||
value::value(value_type t) : mType(t)
|
||||
{
|
||||
switch (mType)
|
||||
|
@ -82,6 +84,11 @@ namespace signalr
|
|||
new (&mStorage.string) std::string(val);
|
||||
}
|
||||
|
||||
value::value(const char* val, size_t length) : mType(value_type::string)
|
||||
{
|
||||
new (&mStorage.string) std::string(val, length);
|
||||
}
|
||||
|
||||
value::value(const std::vector<value>& val) : mType(value_type::array)
|
||||
{
|
||||
new (&mStorage.array) std::vector<value>(val);
|
||||
|
|
|
@ -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) noexcept = 0;
|
||||
virtual void start(const std::string& url, 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(const std::string& payload, std::function<void(std::exception_ptr)> callback) noexcept = 0;
|
||||
virtual void send(const std::string& payload, signalr::transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) noexcept = 0;
|
||||
|
||||
virtual void on_receive(std::function<void(std::string&&, std::exception_ptr)> callback) = 0;
|
||||
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace signalr
|
|||
}
|
||||
}
|
||||
|
||||
void websocket_transport::start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
void websocket_transport::start(const std::string& url, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
signalr::uri uri(url);
|
||||
assert(uri.scheme() == "ws" || uri.scheme() == "wss");
|
||||
|
@ -178,7 +178,7 @@ namespace signalr
|
|||
|
||||
auto transport = shared_from_this();
|
||||
|
||||
websocket_client->start(url, format, [transport, receive_loop_cts, callback](std::exception_ptr exception)
|
||||
websocket_client->start(url, [transport, receive_loop_cts, callback](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -261,9 +261,9 @@ namespace signalr
|
|||
m_process_response_callback = callback;
|
||||
}
|
||||
|
||||
void websocket_transport::send(const std::string& payload, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
void websocket_transport::send(const std::string& payload, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) noexcept
|
||||
{
|
||||
safe_get_websocket_client()->send(payload, [callback](std::exception_ptr exception)
|
||||
safe_get_websocket_client()->send(payload, transfer_format, [callback](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
|
|
|
@ -25,11 +25,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) noexcept override;
|
||||
void start(const std::string& url, 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(const std::string& payload, std::function<void(std::exception_ptr)> callback) noexcept override;
|
||||
void send(const std::string& payload, transfer_format transfer_format, std::function<void(std::exception_ptr)> callback) noexcept override;
|
||||
|
||||
void on_receive(std::function<void(std::string&&, std::exception_ptr)>) override;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ set (SOURCES
|
|||
handshake_tests.cpp
|
||||
hub_connection_tests.cpp
|
||||
hub_exception_tests.cpp
|
||||
json_hub_protocol_tests.cpp
|
||||
logger_tests.cpp
|
||||
memory_log_writer.cpp
|
||||
negotiate_tests.cpp
|
||||
|
|
|
@ -9,26 +9,27 @@ using namespace signalr;
|
|||
|
||||
TEST(callback_manager_register_callback, register_returns_unique_callback_ids)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
auto callback_id1 = callback_mgr.register_callback([](const signalr::value&){});
|
||||
auto callback_id2 = callback_mgr.register_callback([](const signalr::value&){});
|
||||
callback_manager callback_mgr{ "" };
|
||||
auto callback_id1 = callback_mgr.register_callback([](const char*, const signalr::value&){});
|
||||
auto callback_id2 = callback_mgr.register_callback([](const char*, const signalr::value&){});
|
||||
|
||||
ASSERT_NE(callback_id1, callback_id2);
|
||||
}
|
||||
|
||||
TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_removes_callback_if_remove_callback_true)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
callback_manager callback_mgr{ "" };
|
||||
|
||||
int callback_argument;
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_argument](const signalr::value& argument)
|
||||
[&callback_argument](const char* error, const signalr::value& argument)
|
||||
{
|
||||
ASSERT_EQ(nullptr, error);
|
||||
callback_argument = (int)argument.as_double();
|
||||
});
|
||||
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, signalr::value(42.0), true);
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, nullptr, signalr::value(42.0), true);
|
||||
|
||||
ASSERT_TRUE(callback_found);
|
||||
ASSERT_EQ(42, callback_argument);
|
||||
|
@ -37,17 +38,18 @@ TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_removes_callb
|
|||
|
||||
TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_does_not_remove_callback_if_remove_callback_false)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
callback_manager callback_mgr{ "" };
|
||||
|
||||
int callback_argument;
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_argument](const signalr::value& argument)
|
||||
{
|
||||
callback_argument = (int)argument.as_double();
|
||||
});
|
||||
[&callback_argument](const char* error, const signalr::value& argument)
|
||||
{
|
||||
ASSERT_EQ(nullptr, error);
|
||||
callback_argument = (int)argument.as_double();
|
||||
});
|
||||
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, signalr::value(42.0), false);
|
||||
auto callback_found = callback_mgr.invoke_callback(callback_id, nullptr, signalr::value(42.0), false);
|
||||
|
||||
ASSERT_TRUE(callback_found);
|
||||
ASSERT_EQ(42, callback_argument);
|
||||
|
@ -56,8 +58,8 @@ TEST(callback_manager_invoke_callback, invoke_callback_invokes_and_does_not_remo
|
|||
|
||||
TEST(callback_manager_invoke_callback, invoke_callback_returns_false_for_invalid_callback_id)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
auto callback_found = callback_mgr.invoke_callback("42", signalr::value(), true);
|
||||
callback_manager callback_mgr{ "" };
|
||||
auto callback_found = callback_mgr.invoke_callback("42", nullptr, signalr::value(), true);
|
||||
|
||||
ASSERT_FALSE(callback_found);
|
||||
}
|
||||
|
@ -67,10 +69,10 @@ TEST(callback_manager_remove, remove_removes_callback_and_returns_true_for_valid
|
|||
auto callback_called = false;
|
||||
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
callback_manager callback_mgr{ "" };
|
||||
|
||||
auto callback_id = callback_mgr.register_callback(
|
||||
[&callback_called](const signalr::value&)
|
||||
[&callback_called](const char*, const signalr::value&)
|
||||
{
|
||||
callback_called = true;
|
||||
});
|
||||
|
@ -83,26 +85,27 @@ TEST(callback_manager_remove, remove_removes_callback_and_returns_true_for_valid
|
|||
|
||||
TEST(callback_manager_remove, remove_returns_false_for_invalid_callback_id)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
callback_manager callback_mgr{ "" };
|
||||
ASSERT_FALSE(callback_mgr.remove_callback("42"));
|
||||
}
|
||||
|
||||
TEST(callback_manager_clear, clear_invokes_all_callbacks)
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value() };
|
||||
callback_manager callback_mgr{ "" };
|
||||
auto invocation_count = 0;
|
||||
|
||||
for (auto i = 0; i < 10; i++)
|
||||
{
|
||||
callback_mgr.register_callback(
|
||||
[&invocation_count](const signalr::value& argument)
|
||||
{
|
||||
invocation_count++;
|
||||
ASSERT_EQ(42, argument.as_double());
|
||||
});
|
||||
[&invocation_count](const char* error, const signalr::value& argument)
|
||||
{
|
||||
invocation_count++;
|
||||
ASSERT_STREQ("clearing callback", error);
|
||||
ASSERT_TRUE(argument.is_null());
|
||||
});
|
||||
}
|
||||
|
||||
callback_mgr.clear(signalr::value(42.0));
|
||||
callback_mgr.clear("clearing callback");
|
||||
|
||||
ASSERT_EQ(10, invocation_count);
|
||||
}
|
||||
|
@ -110,21 +113,20 @@ TEST(callback_manager_clear, clear_invokes_all_callbacks)
|
|||
TEST(callback_manager_dtor, clear_invokes_all_callbacks)
|
||||
{
|
||||
auto invocation_count = 0;
|
||||
bool parameter_correct = true;
|
||||
|
||||
{
|
||||
callback_manager callback_mgr{ signalr::value(42.0) };
|
||||
callback_manager callback_mgr{ "error" };
|
||||
for (auto i = 0; i < 10; i++)
|
||||
{
|
||||
callback_mgr.register_callback(
|
||||
[&invocation_count, ¶meter_correct](const signalr::value& argument)
|
||||
[&invocation_count](const char* error, const signalr::value& argument)
|
||||
{
|
||||
invocation_count++;
|
||||
parameter_correct &= argument.as_double() == 42;
|
||||
ASSERT_STREQ("error", error);
|
||||
ASSERT_TRUE(argument.is_null());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ASSERT_EQ(10, invocation_count);
|
||||
ASSERT_TRUE(parameter_correct);
|
||||
}
|
||||
|
|
|
@ -348,7 +348,7 @@ TEST(connection_impl_send, send_fails_if_transport_fails_when_receiving_messages
|
|||
|
||||
mre.get();
|
||||
|
||||
connection->send("message", [&mre](std::exception_ptr exception)
|
||||
connection->send("message", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -1055,7 +1055,7 @@ TEST(connection_impl_send, message_sent)
|
|||
|
||||
mre.get();
|
||||
|
||||
connection->send(message, [&mre](std::exception_ptr exception)
|
||||
connection->send(message, transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -1071,7 +1071,7 @@ TEST(connection_impl_send, send_throws_if_connection_not_connected)
|
|||
connection_impl::create(create_uri(), trace_level::none, std::make_shared<memory_log_writer>());
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
connection->send("whatever", [&mre](std::exception_ptr exception)
|
||||
connection->send("whatever", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -1106,7 +1106,7 @@ TEST(connection_impl_send, exceptions_from_send_logged_and_propagated)
|
|||
|
||||
mre.get();
|
||||
|
||||
connection->send("Test message", [&mre](std::exception_ptr exception)
|
||||
connection->send("Test message", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
|
|
@ -70,7 +70,7 @@ TEST(handshake, extra_fields_are_fine)
|
|||
|
||||
TEST(handshake, writes_protocol_and_version)
|
||||
{
|
||||
auto protocol = std::shared_ptr<hub_protocol>(new json_hub_protocol());
|
||||
auto protocol = std::unique_ptr<hub_protocol>(new json_hub_protocol());
|
||||
auto message = handshake::write_handshake(protocol);
|
||||
|
||||
ASSERT_STREQ("{\"protocol\":\"json\",\"version\":1}\x1e", message.c_str());
|
||||
|
|
|
@ -0,0 +1,232 @@
|
|||
// Licensed to the .NET Foundation under one or more agreements.
|
||||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "json_hub_protocol.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
void assert_signalr_value_equality(const signalr::value& expected, const signalr::value& actual)
|
||||
{
|
||||
ASSERT_EQ(expected.type(), actual.type());
|
||||
switch (expected.type())
|
||||
{
|
||||
case value_type::string:
|
||||
ASSERT_STREQ(expected.as_string().data(), actual.as_string().data());
|
||||
break;
|
||||
case value_type::boolean:
|
||||
ASSERT_EQ(expected.as_bool(), actual.as_bool());
|
||||
break;
|
||||
case value_type::float64:
|
||||
ASSERT_DOUBLE_EQ(expected.as_double(), actual.as_double());
|
||||
break;
|
||||
case value_type::map:
|
||||
{
|
||||
auto& expected_map = expected.as_map();
|
||||
auto& actual_map = actual.as_map();
|
||||
ASSERT_EQ(expected_map.size(), actual_map.size());
|
||||
for (auto& pair : expected_map)
|
||||
{
|
||||
const auto& actual_found = actual_map.find(pair.first);
|
||||
ASSERT_FALSE(actual_found == actual_map.end());
|
||||
assert_signalr_value_equality(pair.second, actual_found->second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case value_type::array:
|
||||
{
|
||||
auto& expected_array = expected.as_array();
|
||||
auto& actual_array = actual.as_array();
|
||||
ASSERT_EQ(expected_array.size(), actual_array.size());
|
||||
for (auto i = 0; i < expected_array.size(); ++i)
|
||||
{
|
||||
assert_signalr_value_equality(expected_array[i], actual_array[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case value_type::null:
|
||||
break;
|
||||
default:
|
||||
ASSERT_TRUE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void assert_hub_message_equality(hub_message* expected, hub_message* actual)
|
||||
{
|
||||
ASSERT_EQ(expected->message_type, actual->message_type);
|
||||
switch (expected->message_type)
|
||||
{
|
||||
case message_type::invocation:
|
||||
{
|
||||
auto expected_message = reinterpret_cast<invocation_message*>(expected);
|
||||
auto actual_message = reinterpret_cast<invocation_message*>(actual);
|
||||
|
||||
ASSERT_STREQ(expected_message->invocation_id.data(), actual_message->invocation_id.data());
|
||||
ASSERT_STREQ(expected_message->target.data(), actual_message->target.data());
|
||||
assert_signalr_value_equality(expected_message->arguments, actual_message->arguments);
|
||||
//stream_ids
|
||||
break;
|
||||
}
|
||||
case message_type::completion:
|
||||
{
|
||||
auto expected_message = reinterpret_cast<completion_message*>(expected);
|
||||
auto actual_message = reinterpret_cast<completion_message*>(actual);
|
||||
|
||||
ASSERT_STREQ(expected_message->invocation_id.data(), actual_message->invocation_id.data());
|
||||
ASSERT_STREQ(expected_message->error.data(), actual_message->error.data());
|
||||
assert_signalr_value_equality(expected_message->result, actual_message->result);
|
||||
|
||||
break;
|
||||
}
|
||||
case message_type::ping:
|
||||
{
|
||||
// No fields on ping messages currently
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ASSERT_TRUE(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::shared_ptr<hub_message>>> protocol_test_data
|
||||
{
|
||||
// invocation message without invocation id
|
||||
{ "{\"arguments\":[1,\"Foo\"],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value(1.f), value("Foo") })) },
|
||||
|
||||
// invocation message with multiple arguments
|
||||
{ "{\"arguments\":[1,\"Foo\"],\"invocationId\":\"123\",\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("123", "Target", std::vector<value>{ value(1.f), value("Foo") })) },
|
||||
|
||||
// invocation message with bool argument
|
||||
{ "{\"arguments\":[true],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value(true) })) },
|
||||
|
||||
// invocation message with null argument
|
||||
{ "{\"arguments\":[null],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value(nullptr) })) },
|
||||
|
||||
// invocation message with no arguments
|
||||
{ "{\"arguments\":[],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{})) },
|
||||
|
||||
// invocation message with non-ascii string argument
|
||||
/*{ "{\"arguments\":[\"\xD7\x9E\xD7\x97\xD7\xA8\xD7\x95\xD7\x96\xD7\xAA\x20\xD7\x9B\xD7\x9C\xD7\xA9\xD7\x94\xD7\x99\"],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value("\xD7\x9E\xD7\x97\xD7\xA8\xD7\x95\xD7\x96\xD7\xAA\x20\xD7\x9B\xD7\x9C\xD7\xA9\xD7\x94\xD7\x99") })) },*/
|
||||
|
||||
// invocation message with object argument
|
||||
{ "{\"arguments\":[{\"property\":5}],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value(std::map<std::string, value>{ {"property", value(5.f)} }) })) },
|
||||
|
||||
// invocation message with array argument
|
||||
{ "{\"arguments\":[[1,5]],\"target\":\"Target\",\"type\":1}\x1e",
|
||||
std::shared_ptr<hub_message>(new invocation_message("", "Target", std::vector<value>{ value(std::vector<value>{value(1.f), value(5.f)}) })) },
|
||||
|
||||
// ping message
|
||||
{ "{\"type\":6}\x1e",
|
||||
std::shared_ptr<hub_message>(new ping_message()) },
|
||||
|
||||
// completion message with error
|
||||
{ "{\"error\":\"error\",\"invocationId\":\"1\",\"type\":3}\x1e",
|
||||
std::shared_ptr<hub_message>(new completion_message("1", "error", value())) },
|
||||
|
||||
// completion message with result
|
||||
{ "{\"invocationId\":\"1\",\"result\":42,\"type\":3}\x1e",
|
||||
std::shared_ptr<hub_message>(new completion_message("1", "", value(42.f))) },
|
||||
};
|
||||
|
||||
TEST(json_hub_protocol, write_message)
|
||||
{
|
||||
for (auto& data : protocol_test_data)
|
||||
{
|
||||
auto output = json_hub_protocol().write_message(data.second.get());
|
||||
ASSERT_STREQ(data.first.data(), output.data());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_hub_protocol, parse_message)
|
||||
{
|
||||
for (auto& data : protocol_test_data)
|
||||
{
|
||||
auto output = json_hub_protocol().parse_messages(data.first);
|
||||
ASSERT_EQ(1, output.size());
|
||||
assert_hub_message_equality(data.second.get(), output[0].get());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(json_hub_protocol, parsing_field_order_does_not_matter)
|
||||
{
|
||||
invocation_message message = invocation_message("123", "Target", std::vector<value>{value(true)});
|
||||
auto output = json_hub_protocol().parse_messages("{\"type\":1,\"invocationId\":\"123\",\"arguments\":[true],\"target\":\"Target\"}\x1e");
|
||||
ASSERT_EQ(1, output.size());
|
||||
assert_hub_message_equality(&message, output[0].get());
|
||||
|
||||
output = json_hub_protocol().parse_messages("{\"target\":\"Target\",\"invocationId\":\"123\",\"type\":1,\"arguments\":[true]}\x1e");
|
||||
ASSERT_EQ(1, output.size());
|
||||
assert_hub_message_equality(&message, output[0].get());
|
||||
|
||||
output = json_hub_protocol().parse_messages("{\"target\":\"Target\",\"arguments\":[true],\"type\":1,\"invocationId\":\"123\"}\x1e");
|
||||
ASSERT_EQ(1, output.size());
|
||||
assert_hub_message_equality(&message, output[0].get());
|
||||
}
|
||||
|
||||
TEST(json_hub_protocol, can_parse_multiple_messages)
|
||||
{
|
||||
auto output = json_hub_protocol().parse_messages(std::string("{\"arguments\":[],\"target\":\"Target\",\"type\":1}\x1e") +
|
||||
"{\"invocationId\":\"1\",\"result\":42,\"type\":3}\x1e");
|
||||
ASSERT_EQ(2, output.size());
|
||||
|
||||
invocation_message invocation = invocation_message("", "Target", std::vector<value>{});
|
||||
assert_hub_message_equality(&invocation, output[0].get());
|
||||
|
||||
completion_message completion = completion_message("1", "", value(42.f));
|
||||
assert_hub_message_equality(&completion, output[1].get());
|
||||
}
|
||||
|
||||
TEST(json_hub_protocol, extra_items_ignored_when_parsing)
|
||||
{
|
||||
invocation_message message = invocation_message("", "Target", std::vector<value>{value(true)});
|
||||
auto output = json_hub_protocol().parse_messages("{\"type\":1,\"arguments\":[true],\"target\":\"Target\",\"extra\":\"ignored\"}\x1e");
|
||||
ASSERT_EQ(1, output.size());
|
||||
assert_hub_message_equality(&message, output[0].get());
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> invalid_messages
|
||||
{
|
||||
{ "\x1e", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 1\n A valid JSON document must be either an array or an object value.\n" },
|
||||
{ "foo\x1e", "* Line 1, Column 1\n Syntax error: value, object or array expected.\n* Line 1, Column 2\n Extra non-whitespace after JSON value.\n" },
|
||||
{ "[42]\x1e", "Message was not a 'map' type" },
|
||||
{ "{\x1e", "* Line 1, Column 2\n Missing '}' or object member name\n" },
|
||||
|
||||
{ "{\"arguments\":[],\"target\":\"send\",\"invocationId\":42}\x1e", "Field 'type' not found" },
|
||||
|
||||
{ "{\"type\":1}\x1e", "Field 'target' not found for 'invocation' message" },
|
||||
{ "{\"type\":1,\"target\":\"send\",\"invocationId\":42}\x1e", "Field 'arguments' not found for 'invocation' message" },
|
||||
{ "{\"type\":1,\"target\":\"send\",\"arguments\":[],\"invocationId\":42}\x1e", "Expected 'invocationId' to be of type 'string'" },
|
||||
{ "{\"type\":1,\"target\":\"send\",\"arguments\":42,\"invocationId\":\"42\"}\x1e", "Expected 'arguments' to be of type 'array'" },
|
||||
{ "{\"type\":1,\"target\":true,\"arguments\":[],\"invocationId\":\"42\"}\x1e", "Expected 'target' to be of type 'string'" },
|
||||
|
||||
{ "{\"type\":3}\x1e", "Field 'invocationId' not found for 'completion' message" },
|
||||
{ "{\"type\":3,\"invocationId\":42}\x1e", "Expected 'invocationId' to be of type 'string'" },
|
||||
{ "{\"type\":3,\"invocationId\":\"42\",\"error\":[]}\x1e", "Expected 'error' to be of type 'string'" },
|
||||
{ "{\"type\":3,\"invocationId\":\"42\",\"error\":\"foo\",\"result\":true}\x1e", "The 'error' and 'result' properties are mutually exclusive." },
|
||||
};
|
||||
|
||||
TEST(json_hub_protocol, invalid_messages_throw)
|
||||
{
|
||||
for (auto& pair : invalid_messages)
|
||||
{
|
||||
try
|
||||
{
|
||||
json_hub_protocol().parse_messages(pair.first);
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (const std::exception& exception)
|
||||
{
|
||||
ASSERT_STREQ(pair.second.data(), exception.what());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -51,7 +51,7 @@ test_websocket_client::~test_websocket_client()
|
|||
}
|
||||
}
|
||||
|
||||
void test_websocket_client::start(const std::string& url, transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
void test_websocket_client::start(const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_receive_lock);
|
||||
m_stopped = false;
|
||||
|
@ -99,7 +99,7 @@ void test_websocket_client::stop(std::function<void(std::exception_ptr)> callbac
|
|||
}).detach();
|
||||
}
|
||||
|
||||
void test_websocket_client::send(const std::string& payload, std::function<void(std::exception_ptr)> callback)
|
||||
void test_websocket_client::send(const std::string& payload, signalr::transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
handshake_sent.cancel();
|
||||
auto local_copy = m_send_function;
|
||||
|
|
|
@ -18,11 +18,11 @@ public:
|
|||
test_websocket_client();
|
||||
~test_websocket_client();
|
||||
|
||||
void start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback);
|
||||
void start(const std::string& url, std::function<void(std::exception_ptr)> callback);
|
||||
|
||||
void stop(std::function<void(std::exception_ptr)> callback);
|
||||
|
||||
void send(const std::string& payload, std::function<void(std::exception_ptr)> callback);
|
||||
void send(const std::string& payload, signalr::transfer_format transfer_format, std::function<void(std::exception_ptr)> callback);
|
||||
|
||||
void receive(std::function<void(const std::string&, std::exception_ptr)> callback);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&) { return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://fakeuri.org/connect?param=42", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -59,7 +59,7 @@ TEST(websocket_transport_connect, connect_propagates_exceptions)
|
|||
try
|
||||
{
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ TEST(websocket_transport_connect, connect_logs_exceptions)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, logger(writer, trace_level::errors));
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -111,7 +111,7 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -119,7 +119,7 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans
|
|||
|
||||
try
|
||||
{
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -138,7 +138,7 @@ TEST(websocket_transport_connect, can_connect_after_disconnecting)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -150,7 +150,7 @@ TEST(websocket_transport_connect, can_connect_after_disconnecting)
|
|||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -172,13 +172,13 @@ TEST(websocket_transport_send, send_creates_and_sends_websocket_messages)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://url", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->send("ABC", [&mre](std::exception_ptr exception)
|
||||
ws_transport->send("ABC", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -202,7 +202,7 @@ TEST(websocket_transport_disconnect, disconnect_closes_websocket)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://url", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -231,7 +231,7 @@ TEST(websocket_transport_stop, propogates_exception_from_close)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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 exception)
|
||||
ws_transport->start("ws://url", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -264,7 +264,7 @@ TEST(websocket_transport_disconnect, disconnect_logs_exceptions)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, logger(writer, trace_level::errors));
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://url", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -309,7 +309,7 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&){ return client; }, signalr_client_config{}, 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", [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
|
@ -323,7 +323,7 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
|||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -383,7 +383,7 @@ void receive_loop_logs_exception_runner(const T& e, const std::string& expected_
|
|||
auto ws_transport = websocket_transport::create([&](const signalr_client_config&) { return client; }, signalr_client_config{}, logger(writer, trace_level));
|
||||
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
ws_transport->start("ws://url", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -434,7 +434,7 @@ 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 exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
@ -478,7 +478,7 @@ 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 exception)
|
||||
ws_transport->start("ws://fakeuri.org", [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set(exception);
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче