C++ client low level API (#8420)
This commit is contained in:
parent
b7e122fbac
commit
969c72acfd
|
|
@ -100,6 +100,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
[ConditionalTheory]
|
||||
[MemberData(nameof(IPEndPointRegistrationDataDynamicPort))]
|
||||
[IPv6SupportedCondition]
|
||||
[Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/2074", FlakyOn.AzP.macOS)]
|
||||
public async Task RegisterIPEndPoint_DynamicPort_Success(IPEndPoint endPoint, string testUrl)
|
||||
{
|
||||
await RegisterIPEndPoint_Success(endPoint, testUrl);
|
||||
|
|
@ -447,6 +448,7 @@ namespace Microsoft.AspNetCore.Server.Kestrel.FunctionalTests
|
|||
|
||||
[ConditionalFact]
|
||||
[IPv6SupportedCondition]
|
||||
[Flaky("https://github.com/aspnet/AspNetCore-Internal/issues/1756", FlakyOn.AzP.macOS)]
|
||||
public Task DefaultsServerAddress_BindsToIPv6WithHttps()
|
||||
{
|
||||
if (!CanBindToEndpoint(IPAddress.Loopback, 5000) || !CanBindToEndpoint(IPAddress.IPv6Loopback, 5000)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <chrono>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class http_method
|
||||
{
|
||||
GET,
|
||||
POST
|
||||
};
|
||||
|
||||
class http_request
|
||||
{
|
||||
public:
|
||||
http_method method;
|
||||
std::map<std::string, std::string> headers;
|
||||
std::string content;
|
||||
std::chrono::seconds timeout;
|
||||
};
|
||||
|
||||
class http_response
|
||||
{
|
||||
public:
|
||||
http_response() {}
|
||||
http_response(http_response&& rhs) noexcept : status_code(rhs.status_code), content(std::move(rhs.content)) {}
|
||||
http_response(int code, const std::string& content) : status_code(code), content(content) {}
|
||||
|
||||
http_response& operator=(http_response&& rhs)
|
||||
{
|
||||
status_code = rhs.status_code;
|
||||
content = std::move(rhs.content);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
int status_code = 0;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
class http_client
|
||||
{
|
||||
public:
|
||||
virtual void send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual ~http_client() {}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
enum class transfer_format
|
||||
{
|
||||
text,
|
||||
binary
|
||||
};
|
||||
}
|
||||
|
|
@ -10,4 +10,4 @@ namespace signalr
|
|||
long_polling,
|
||||
websockets
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "transfer_format.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class websocket_client
|
||||
{
|
||||
public:
|
||||
virtual ~websocket_client() {};
|
||||
|
||||
virtual void start(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 send(std::string payload, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void receive(std::function<void(std::string, std::exception_ptr)> callback) = 0;
|
||||
};
|
||||
}
|
||||
|
|
@ -17,14 +17,13 @@ class logger : public signalr::log_writer
|
|||
}
|
||||
};
|
||||
|
||||
void send_message(signalr::hub_connection& connection, const std::string& name, const std::string& message)
|
||||
void send_message(signalr::hub_connection& connection, const std::string& message)
|
||||
{
|
||||
web::json::value args{};
|
||||
args[0] = web::json::value::string(utility::conversions::to_string_t(name));
|
||||
args[1] = web::json::value(utility::conversions::to_string_t(message));
|
||||
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("Invoke", args/*, [](const web::json::value&){}*/)
|
||||
connection.invoke("Send", args)
|
||||
.then([](pplx::task<web::json::value> invoke_task) // fire and forget but we need to observe exceptions
|
||||
{
|
||||
try
|
||||
|
|
@ -39,7 +38,7 @@ void send_message(signalr::hub_connection& connection, const std::string& name,
|
|||
});
|
||||
}
|
||||
|
||||
void chat(const std::string& name)
|
||||
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)
|
||||
|
|
@ -48,7 +47,7 @@ void chat(const std::string& name)
|
|||
});
|
||||
|
||||
connection.start()
|
||||
.then([&connection, name]()
|
||||
.then([&connection]()
|
||||
{
|
||||
ucout << U("Enter your message:");
|
||||
for (;;)
|
||||
|
|
@ -61,7 +60,7 @@ void chat(const std::string& name)
|
|||
break;
|
||||
}
|
||||
|
||||
send_message(connection, name, message);
|
||||
send_message(connection, message);
|
||||
}
|
||||
})
|
||||
.then([&connection]() // fine to capture by reference - we are blocking so it is guaranteed to be valid
|
||||
|
|
@ -84,11 +83,7 @@ void chat(const std::string& name)
|
|||
|
||||
int main()
|
||||
{
|
||||
ucout << U("Enter your name: ");
|
||||
std::string name;
|
||||
std::getline(std::cin, name);
|
||||
|
||||
chat(name);
|
||||
chat();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,18 +41,22 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_client_config.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h" />
|
||||
<ClInclude Include="..\..\case_insensitive_comparison_utils.h" />
|
||||
<ClInclude Include="..\..\connection_impl.h" />
|
||||
<ClInclude Include="..\..\constants.h" />
|
||||
<ClInclude Include="..\..\default_http_client.h" />
|
||||
<ClInclude Include="..\..\default_websocket_client.h" />
|
||||
<ClInclude Include="..\..\event.h" />
|
||||
<ClInclude Include="..\..\http_sender.h" />
|
||||
|
|
@ -60,13 +64,12 @@
|
|||
<ClInclude Include="..\..\callback_manager.h" />
|
||||
<ClInclude Include="..\..\logger.h" />
|
||||
<ClInclude Include="..\..\negotiation_response.h" />
|
||||
<ClInclude Include="..\..\request_sender.h" />
|
||||
<ClInclude Include="..\..\negotiate.h" />
|
||||
<ClInclude Include="..\..\stdafx.h" />
|
||||
<ClInclude Include="..\..\trace_log_writer.h" />
|
||||
<ClInclude Include="..\..\transport.h" />
|
||||
<ClInclude Include="..\..\transport_factory.h" />
|
||||
<ClInclude Include="..\..\url_builder.h" />
|
||||
<ClInclude Include="..\..\websocket_client.h" />
|
||||
<ClInclude Include="..\..\websocket_transport.h" />
|
||||
<ClInclude Include="..\..\web_request.h" />
|
||||
<ClInclude Include="..\..\web_request_factory.h" />
|
||||
|
|
@ -75,12 +78,13 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\connection.cpp" />
|
||||
<ClCompile Include="..\..\connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\default_http_client.cpp" />
|
||||
<ClCompile Include="..\..\http_sender.cpp" />
|
||||
<ClCompile Include="..\..\hub_connection.cpp" />
|
||||
<ClCompile Include="..\..\hub_connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\callback_manager.cpp" />
|
||||
<ClCompile Include="..\..\logger.cpp" />
|
||||
<ClCompile Include="..\..\request_sender.cpp" />
|
||||
<ClCompile Include="..\..\negotiate.cpp" />
|
||||
<ClCompile Include="..\..\signalr_client_config.cpp" />
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
|
|
@ -105,4 +109,4 @@
|
|||
<PackageReference Include="cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn" Version="2.9.1" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -45,9 +45,6 @@
|
|||
<ClInclude Include="..\..\negotiation_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\request_sender.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\websocket_transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -57,9 +54,6 @@
|
|||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\default_websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -111,6 +105,21 @@
|
|||
<ClInclude Include="..\..\..\..\include\signalrclient\signalr_client_config.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\default_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\negotiate.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
|
|
@ -164,7 +173,10 @@
|
|||
<ClCompile Include="..\..\signalr_client_config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\request_sender.cpp">
|
||||
<ClCompile Include="..\..\default_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\negotiate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -6,11 +6,12 @@
|
|||
#include <algorithm>
|
||||
#include "constants.h"
|
||||
#include "connection_impl.h"
|
||||
#include "request_sender.h"
|
||||
#include "negotiate.h"
|
||||
#include "url_builder.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "make_unique.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include "default_http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
|
@ -23,22 +24,30 @@ namespace signalr
|
|||
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
|
||||
{
|
||||
return connection_impl::create(url, trace_level, log_writer, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
|
||||
return connection_impl::create(url, trace_level, log_writer, nullptr, std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory)
|
||||
{
|
||||
return std::shared_ptr<connection_impl>(new connection_impl(url, trace_level,
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(web_request_factory), std::move(transport_factory)));
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(http_client), std::move(transport_factory)));
|
||||
}
|
||||
|
||||
connection_impl::connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level),
|
||||
m_transport(nullptr), m_web_request_factory(std::move(web_request_factory)), m_transport_factory(std::move(transport_factory)),
|
||||
m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
|
||||
{ }
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr),
|
||||
m_transport_factory(std::move(transport_factory)), m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
|
||||
{
|
||||
if (http_client != nullptr)
|
||||
{
|
||||
m_http_client = std::move(http_client);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_http_client = std::unique_ptr<class http_client>(new default_http_client());
|
||||
}
|
||||
}
|
||||
|
||||
connection_impl::~connection_impl()
|
||||
{
|
||||
|
|
@ -105,7 +114,7 @@ namespace signalr
|
|||
{
|
||||
return pplx::task_from_exception<negotiation_response>("connection no longer exists");
|
||||
}
|
||||
return request_sender::negotiate(*connection->m_web_request_factory, url, connection->m_signalr_client_config);
|
||||
return negotiate::negotiate(*connection->m_http_client, url, connection->m_signalr_client_config);
|
||||
}, m_disconnect_cts.get_token())
|
||||
.then([weak_connection, start_tce, redirect_count, url](negotiation_response negotiation_response)
|
||||
{
|
||||
|
|
@ -224,52 +233,54 @@ namespace signalr
|
|||
const auto& disconnect_cts = m_disconnect_cts;
|
||||
const auto& logger = m_logger;
|
||||
|
||||
auto process_response_callback =
|
||||
[weak_connection, disconnect_cts, logger](const std::string& response) mutable
|
||||
{
|
||||
// When a connection is stopped we don't wait for its transport to stop. As a result if the same connection
|
||||
// is immediately re-started the old transport can still invoke this callback. To prevent this we capture
|
||||
// the disconnect_cts by value which allows distinguishing if the message is for the running connection
|
||||
// or for the one that was already stopped. If this is the latter we just ignore it.
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray message received after connection was restarted. message: " }
|
||||
.append(response));
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = weak_connection.lock();
|
||||
if (connection)
|
||||
{
|
||||
connection->process_response(response);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
auto error_callback =
|
||||
[weak_connection, connect_request_tce, disconnect_cts, logger](const std::exception &e) mutable
|
||||
{
|
||||
// When a connection is stopped we don't wait for its transport to stop. As a result if the same connection
|
||||
// is immediately re-started the old transport can still invoke this callback. To prevent this we capture
|
||||
// the disconnect_cts by value which allows distinguishing if the error is for the running connection
|
||||
// or for the one that was already stopped. If this is the latter we just ignore it.
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray error received after connection was restarted. error: " }
|
||||
.append(e.what()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// no op after connection started successfully
|
||||
connect_request_tce.set_exception(e);
|
||||
};
|
||||
|
||||
auto transport = connection->m_transport_factory->create_transport(
|
||||
transport_type::websockets, connection->m_logger, connection->m_signalr_client_config,
|
||||
process_response_callback, error_callback);
|
||||
transport_type::websockets, connection->m_logger, connection->m_signalr_client_config);
|
||||
|
||||
transport->on_receive([disconnect_cts, connect_request_tce, logger, weak_connection](std::string message, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Rethrowing the exception so we can log it
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
// When a connection is stopped we don't wait for its transport to stop. As a result if the same connection
|
||||
// is immediately re-started the old transport can still invoke this callback. To prevent this we capture
|
||||
// the disconnect_cts by value which allows distinguishing if the error is for the running connection
|
||||
// or for the one that was already stopped. If this is the latter we just ignore it.
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray error received after connection was restarted. error: " }
|
||||
.append(e.what()));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// no op after connection started successfully
|
||||
connect_request_tce.set_exception(exception);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (disconnect_cts.get_token().is_canceled())
|
||||
{
|
||||
logger.log(trace_level::info,
|
||||
std::string{ "ignoring stray message received after connection was restarted. message: " }
|
||||
.append(message));
|
||||
return;
|
||||
}
|
||||
|
||||
auto connection = weak_connection.lock();
|
||||
if (connection)
|
||||
{
|
||||
connection->process_response(message);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
pplx::create_task([connect_request_tce, disconnect_cts, weak_connection]()
|
||||
{
|
||||
|
|
@ -300,12 +311,14 @@ namespace signalr
|
|||
auto query_string = "id=" + m_connection_id;
|
||||
auto connect_url = url_builder::build_connect(url, transport->get_transport_type(), query_string);
|
||||
|
||||
transport->connect(connect_url)
|
||||
.then([transport, connect_request_tce, logger](pplx::task<void> connect_task)
|
||||
transport->start(connect_url, transfer_format::text, [transport, connect_request_tce, logger](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
connect_task.get();
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
connect_request_tce.set();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
|
@ -368,12 +381,16 @@ namespace signalr
|
|||
|
||||
logger.log(trace_level::info, std::string("sending data: ").append(data));
|
||||
|
||||
return transport->send(data)
|
||||
.then([logger](pplx::task<void> send_task)
|
||||
pplx::task_completion_event<void> event;
|
||||
transport->send(data, [logger, event](std::exception_ptr exception)
|
||||
mutable {
|
||||
try
|
||||
{
|
||||
send_task.get();
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
event.set();
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
@ -382,9 +399,11 @@ namespace signalr
|
|||
std::string("error sending data: ")
|
||||
.append(e.what()));
|
||||
|
||||
throw;
|
||||
event.set_exception(exception);
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(event);
|
||||
}
|
||||
|
||||
pplx::task<void> connection_impl::stop()
|
||||
|
|
@ -471,7 +490,20 @@ namespace signalr
|
|||
change_state(connection_state::disconnecting);
|
||||
}
|
||||
|
||||
return m_transport->disconnect();
|
||||
pplx::task_completion_event<void> tce;
|
||||
m_transport->stop([tce](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
tce.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
tce.set();
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(tce);
|
||||
}
|
||||
|
||||
connection_state connection_impl::get_connection_state() const noexcept
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include "cpprest/http_client.h"
|
||||
#include "signalrclient/http_client.h"
|
||||
#include "signalrclient/trace_level.h"
|
||||
#include "signalrclient/connection_state.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
|
|
@ -28,7 +29,7 @@ namespace signalr
|
|||
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer);
|
||||
|
||||
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
connection_impl(const connection_impl&) = delete;
|
||||
|
||||
|
|
@ -52,7 +53,6 @@ namespace signalr
|
|||
std::atomic<connection_state> m_connection_state;
|
||||
logger m_logger;
|
||||
std::shared_ptr<transport> m_transport;
|
||||
std::unique_ptr<web_request_factory> m_web_request_factory;
|
||||
std::unique_ptr<transport_factory> m_transport_factory;
|
||||
|
||||
std::function<void(const std::string&)> m_message_received;
|
||||
|
|
@ -63,9 +63,10 @@ namespace signalr
|
|||
std::mutex m_stop_lock;
|
||||
event m_start_completed_event;
|
||||
std::string m_connection_id;
|
||||
std::unique_ptr<http_client> m_http_client;
|
||||
|
||||
connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
pplx::task<std::shared_ptr<transport>> start_transport(const std::string& url);
|
||||
pplx::task<void> send_connect_request(const std::shared_ptr<transport>& transport,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "default_http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
void default_http_client::send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback)
|
||||
{
|
||||
web::http::method method;
|
||||
if (request.method == http_method::GET)
|
||||
{
|
||||
method = U("GET");
|
||||
}
|
||||
else if (request.method == http_method::POST)
|
||||
{
|
||||
method = U("POST");
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(http_response(), std::make_exception_ptr(std::runtime_error("unknown http method")));
|
||||
return;
|
||||
}
|
||||
|
||||
web::http::http_request http_request;
|
||||
http_request.set_method(method);
|
||||
http_request.set_body(request.content);
|
||||
if (request.headers.size() > 0)
|
||||
{
|
||||
web::http::http_headers headers;
|
||||
for (auto& header : request.headers)
|
||||
{
|
||||
headers.add(utility::conversions::to_string_t(header.first), utility::conversions::to_string_t(header.second));
|
||||
}
|
||||
http_request.headers() = headers;
|
||||
}
|
||||
|
||||
auto milliseconds = std::chrono::milliseconds(request.timeout).count();
|
||||
pplx::cancellation_token_source cts;
|
||||
if (milliseconds != 0)
|
||||
{
|
||||
pplx::create_task([milliseconds, cts]()
|
||||
{
|
||||
pplx::wait((unsigned int)milliseconds);
|
||||
cts.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
web::http::client::http_client client(utility::conversions::to_string_t(url));
|
||||
client.request(http_request, cts.get_token())
|
||||
.then([callback](pplx::task<web::http::http_response> response_task)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto http_response = response_task.get();
|
||||
auto status_code = http_response.status_code();
|
||||
http_response.extract_utf8string()
|
||||
.then([callback, status_code](std::string response_body)
|
||||
{
|
||||
signalr::http_response response;
|
||||
response.content = response_body;
|
||||
response.status_code = status_code;
|
||||
callback(std::move(response), nullptr);
|
||||
});
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(http_response(), std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "signalrclient/http_client.h"
|
||||
#include "cpprest/http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class default_http_client : public http_client
|
||||
{
|
||||
public:
|
||||
void send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback) override;
|
||||
};
|
||||
}
|
||||
|
|
@ -21,30 +21,73 @@ namespace signalr
|
|||
: m_underlying_client(create_client_config(signalr_client_config))
|
||||
{ }
|
||||
|
||||
pplx::task<void> default_websocket_client::connect(const std::string& url)
|
||||
void default_websocket_client::start(std::string url, transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return m_underlying_client.connect(utility::conversions::to_string_t(url));
|
||||
}
|
||||
|
||||
pplx::task<void> default_websocket_client::send(const std::string &message)
|
||||
{
|
||||
web::websockets::client::websocket_outgoing_message msg;
|
||||
msg.set_utf8_message(message);
|
||||
return m_underlying_client.send(msg);
|
||||
}
|
||||
|
||||
pplx::task<std::string> default_websocket_client::receive()
|
||||
{
|
||||
// the caller is responsible for observing exceptions
|
||||
return m_underlying_client.receive()
|
||||
.then([](web::websockets::client::websocket_incoming_message msg)
|
||||
m_underlying_client.connect(utility::conversions::to_string_t(url))
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
return msg.extract_string();
|
||||
try
|
||||
{
|
||||
task.get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<void> default_websocket_client::close()
|
||||
void default_websocket_client::stop(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return m_underlying_client.close();
|
||||
m_underlying_client.close()
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::send(std::string payload, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
web::websockets::client::websocket_outgoing_message msg;
|
||||
msg.set_utf8_message(payload);
|
||||
m_underlying_client.send(msg)
|
||||
.then([callback](pplx::task<void> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
task.get();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void default_websocket_client::receive(std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
m_underlying_client.receive()
|
||||
.then([callback](pplx::task<web::websockets::client::websocket_incoming_message> task)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto response = task.get();
|
||||
auto msg = response.extract_string().get();
|
||||
callback(msg, nullptr);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
callback("", std::current_exception());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include "cpprest/ws_client.h"
|
||||
#include "signalrclient/signalr_client_config.h"
|
||||
#include "websocket_client.h"
|
||||
#include "signalrclient/websocket_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
|
|
@ -14,14 +14,10 @@ namespace signalr
|
|||
public:
|
||||
explicit default_websocket_client(const signalr_client_config& signalr_client_config = {}) noexcept;
|
||||
|
||||
pplx::task<void> connect(const std::string& url) override;
|
||||
|
||||
pplx::task<void> send(const std::string& message) override;
|
||||
|
||||
pplx::task<std::string> receive() override;
|
||||
|
||||
pplx::task<void> close() override;
|
||||
|
||||
void start(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 send(std::string payload, std::function<void(std::exception_ptr)> callback) override;
|
||||
void receive(std::function<void(std::string, std::exception_ptr)> callback) override;
|
||||
private:
|
||||
web::websockets::client::websocket_client m_underlying_client;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -24,15 +24,15 @@ namespace signalr
|
|||
const std::shared_ptr<log_writer>& log_writer)
|
||||
{
|
||||
return hub_connection_impl::create(url, trace_level, log_writer,
|
||||
std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
|
||||
nullptr, std::make_unique<transport_factory>());
|
||||
}
|
||||
|
||||
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::unique_ptr<web_request_factory> web_request_factory,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory)
|
||||
{
|
||||
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, trace_level,
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(web_request_factory), std::move(transport_factory)));
|
||||
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(http_client), std::move(transport_factory)));
|
||||
|
||||
connection->initialize();
|
||||
|
||||
|
|
@ -40,10 +40,10 @@ namespace signalr
|
|||
}
|
||||
|
||||
hub_connection_impl::hub_connection_impl(const std::string& url, trace_level trace_level,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<web_request_factory> web_request_factory,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory)
|
||||
: m_connection(connection_impl::create(url, trace_level, log_writer,
|
||||
std::move(web_request_factory), std::move(transport_factory))), m_logger(log_writer, trace_level),
|
||||
std::move(http_client), std::move(transport_factory))), m_logger(log_writer, trace_level),
|
||||
m_callback_manager(json::value::parse(_XPLATSTR("{ \"error\" : \"connection went out of scope before invocation result was received\"}"))),
|
||||
m_disconnected([]() noexcept {}), m_handshakeReceived(false)
|
||||
{ }
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ namespace signalr
|
|||
const std::shared_ptr<log_writer>& log_writer);
|
||||
|
||||
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::unique_ptr<web_request_factory> web_request_factory,
|
||||
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<http_client> http_client,
|
||||
std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
hub_connection_impl(const hub_connection_impl&) = delete;
|
||||
|
|
@ -46,7 +46,7 @@ namespace signalr
|
|||
|
||||
private:
|
||||
hub_connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
|
||||
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory);
|
||||
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
|
||||
|
||||
std::shared_ptr<connection_impl> m_connection;
|
||||
logger m_logger;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,103 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "negotiate.h"
|
||||
#include "url_builder.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace negotiate
|
||||
{
|
||||
pplx::task<negotiation_response> negotiate(http_client& client, const std::string& base_url,
|
||||
const signalr_client_config& config)
|
||||
{
|
||||
auto negotiate_url = url_builder::build_negotiate(base_url);
|
||||
|
||||
pplx::task_completion_event<negotiation_response> tce;
|
||||
|
||||
// TODO: signalr_client_config
|
||||
http_request request;
|
||||
request.method = http_method::POST;
|
||||
|
||||
for (auto& header : config.get_http_headers())
|
||||
{
|
||||
request.headers.insert(std::make_pair(utility::conversions::to_utf8string(header.first), utility::conversions::to_utf8string(header.second)));
|
||||
}
|
||||
|
||||
client.send(negotiate_url, request, [tce](http_response http_response, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
tce.set_exception(exception);
|
||||
return;
|
||||
}
|
||||
|
||||
if (http_response.status_code != 200)
|
||||
{
|
||||
tce.set_exception(signalr_exception("negotiate failed with status code " + std::to_string(http_response.status_code)));
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
auto negotiation_response_json = web::json::value::parse(utility::conversions::to_string_t(http_response.content));
|
||||
|
||||
negotiation_response response;
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("error")))
|
||||
{
|
||||
response.error = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("error")].as_string());
|
||||
tce.set(std::move(response));
|
||||
return;
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("connectionId")))
|
||||
{
|
||||
response.connectionId = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("connectionId")].as_string());
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("availableTransports")))
|
||||
{
|
||||
for (auto transportData : negotiation_response_json[_XPLATSTR("availableTransports")].as_array())
|
||||
{
|
||||
available_transport transport;
|
||||
transport.transport = utility::conversions::to_utf8string(transportData[_XPLATSTR("transport")].as_string());
|
||||
|
||||
for (auto format : transportData[_XPLATSTR("transferFormats")].as_array())
|
||||
{
|
||||
transport.transfer_formats.push_back(utility::conversions::to_utf8string(format.as_string()));
|
||||
}
|
||||
|
||||
response.availableTransports.push_back(transport);
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("url")))
|
||||
{
|
||||
response.url = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("url")].as_string());
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("accessToken")))
|
||||
{
|
||||
response.accessToken = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("accessToken")].as_string());
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("ProtocolVersion")))
|
||||
{
|
||||
tce.set_exception(signalr_exception("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details."));
|
||||
}
|
||||
|
||||
tce.set(std::move(response));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tce.set_exception(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
return pplx::create_task(tce);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,13 +7,13 @@
|
|||
#include "signalrclient/transport_type.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "negotiation_response.h"
|
||||
|
||||
#include "signalrclient/http_client.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace request_sender
|
||||
namespace negotiate
|
||||
{
|
||||
pplx::task<negotiation_response> negotiate(web_request_factory& request_factory, const std::string& base_url,
|
||||
pplx::task<negotiation_response> negotiate(http_client& client, const std::string& base_url,
|
||||
const signalr_client_config& signalr_client_config = signalr::signalr_client_config{});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "request_sender.h"
|
||||
#include "http_sender.h"
|
||||
#include "url_builder.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
namespace request_sender
|
||||
{
|
||||
pplx::task<negotiation_response> negotiate(web_request_factory& request_factory, const std::string& base_url,
|
||||
const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
auto negotiate_url = url_builder::build_negotiate(base_url);
|
||||
|
||||
return http_sender::post(request_factory, negotiate_url, signalr_client_config)
|
||||
.then([](std::string body)
|
||||
{
|
||||
auto negotiation_response_json = web::json::value::parse(utility::conversions::to_string_t(body));
|
||||
|
||||
negotiation_response response;
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("error")))
|
||||
{
|
||||
response.error = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("error")].as_string());
|
||||
return std::move(response);
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("connectionId")))
|
||||
{
|
||||
response.connectionId = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("connectionId")].as_string());
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("availableTransports")))
|
||||
{
|
||||
for (auto transportData : negotiation_response_json[_XPLATSTR("availableTransports")].as_array())
|
||||
{
|
||||
available_transport transport;
|
||||
transport.transport = utility::conversions::to_utf8string(transportData[_XPLATSTR("transport")].as_string());
|
||||
|
||||
for (auto format : transportData[_XPLATSTR("transferFormats")].as_array())
|
||||
{
|
||||
transport.transfer_formats.push_back(utility::conversions::to_utf8string(format.as_string()));
|
||||
}
|
||||
|
||||
response.availableTransports.push_back(transport);
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("url")))
|
||||
{
|
||||
response.url = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("url")].as_string());
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("accessToken")))
|
||||
{
|
||||
response.accessToken = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("accessToken")].as_string());
|
||||
}
|
||||
}
|
||||
|
||||
if (negotiation_response_json.has_field(_XPLATSTR("ProtocolVersion")))
|
||||
{
|
||||
throw signalr_exception("Detected a connection attempt to an ASP.NET SignalR Server. This client only supports connecting to an ASP.NET Core SignalR Server. See https://aka.ms/signalr-core-differences for details.");
|
||||
}
|
||||
|
||||
return std::move(response);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,23 +7,12 @@
|
|||
|
||||
namespace signalr
|
||||
{
|
||||
transport::transport(const logger& logger, const std::function<void(const std::string&)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback)
|
||||
: m_logger(logger), m_process_response_callback(process_response_callback), m_error_callback(error_callback)
|
||||
transport::transport(const logger& logger)
|
||||
: m_logger(logger)
|
||||
{}
|
||||
|
||||
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to
|
||||
// undefinded behavior since we are using an incomplete type. More details here: http://herbsutter.com/gotw/_100/
|
||||
transport::~transport()
|
||||
{ }
|
||||
|
||||
void transport::process_response(const std::string &message)
|
||||
{
|
||||
m_process_response_callback(message);
|
||||
}
|
||||
|
||||
void transport::error(const std::exception& e)
|
||||
{
|
||||
m_error_callback(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "pplx/pplxtasks.h"
|
||||
#include "signalrclient/transport_type.h"
|
||||
#include "signalrclient/transfer_format.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace signalr
|
||||
|
|
@ -12,28 +13,21 @@ namespace signalr
|
|||
class transport
|
||||
{
|
||||
public:
|
||||
virtual pplx::task<void> connect(const std::string &url) = 0;
|
||||
|
||||
virtual pplx::task<void> send(const std::string &data) = 0;
|
||||
|
||||
virtual pplx::task<void> disconnect() = 0;
|
||||
|
||||
virtual transport_type get_transport_type() const = 0;
|
||||
|
||||
virtual ~transport();
|
||||
|
||||
protected:
|
||||
transport(const logger& logger, const std::function<void(const std::string &)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback);
|
||||
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 on_close(std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
void process_response(const std::string &message);
|
||||
void error(const std::exception &e);
|
||||
virtual void send(std::string payload, std::function<void(std::exception_ptr)> callback) = 0;
|
||||
|
||||
virtual void on_receive(std::function<void(std::string, std::exception_ptr)> callback) = 0;
|
||||
|
||||
protected:
|
||||
transport(const logger& logger);
|
||||
|
||||
logger m_logger;
|
||||
|
||||
private:
|
||||
std::function<void(const std::string &)> m_process_response_callback;
|
||||
|
||||
std::function<void(const std::exception&)> m_error_callback;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,15 +8,13 @@
|
|||
namespace signalr
|
||||
{
|
||||
std::shared_ptr<transport> transport_factory::create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config& signalr_client_config,
|
||||
std::function<void(const std::string&)> process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback)
|
||||
const signalr_client_config& signalr_client_config)
|
||||
{
|
||||
if (transport_type == signalr::transport_type::websockets)
|
||||
{
|
||||
return websocket_transport::create(
|
||||
[signalr_client_config](){ return std::make_shared<default_websocket_client>(signalr_client_config); },
|
||||
logger, process_response_callback, error_callback);
|
||||
logger);
|
||||
}
|
||||
|
||||
throw std::runtime_error("not implemented");
|
||||
|
|
@ -24,4 +22,4 @@ namespace signalr
|
|||
|
||||
transport_factory::~transport_factory()
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,10 +14,8 @@ namespace signalr
|
|||
{
|
||||
public:
|
||||
virtual std::shared_ptr<transport> create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config& signalr_client_config,
|
||||
std::function<void(const std::string&)> process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback);
|
||||
const signalr_client_config& signalr_client_config);
|
||||
|
||||
virtual ~transport_factory();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "pplx/pplxtasks.h"
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
class websocket_client
|
||||
{
|
||||
public:
|
||||
virtual pplx::task<void> connect(const std::string& url) = 0;
|
||||
|
||||
virtual pplx::task<void> send(const std::string& message) = 0;
|
||||
|
||||
virtual pplx::task<std::string> receive() = 0;
|
||||
|
||||
virtual pplx::task<void> close() = 0;
|
||||
|
||||
virtual ~websocket_client() {};
|
||||
};
|
||||
}
|
||||
|
|
@ -5,21 +5,21 @@
|
|||
#include "websocket_transport.h"
|
||||
#include "logger.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include <future>
|
||||
|
||||
namespace signalr
|
||||
{
|
||||
std::shared_ptr<transport> websocket_transport::create(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger, const std::function<void(const std::string &)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback)
|
||||
const logger& logger)
|
||||
{
|
||||
return std::shared_ptr<transport>(
|
||||
new websocket_transport(websocket_client_factory, logger, process_response_callback, error_callback));
|
||||
new websocket_transport(websocket_client_factory, logger));
|
||||
}
|
||||
|
||||
websocket_transport::websocket_transport(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger, const std::function<void(const std::string &)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback)
|
||||
: transport(logger, process_response_callback, error_callback), m_websocket_client_factory(websocket_client_factory)
|
||||
const logger& logger)
|
||||
: transport(logger), m_websocket_client_factory(websocket_client_factory), m_close_callback([](std::exception_ptr) {}),
|
||||
m_process_response_callback([](std::string, std::exception_ptr) {})
|
||||
{
|
||||
// we use this cts to check if the receive loop is running so it should be
|
||||
// initially cancelled to indicate that the receive loop is not running
|
||||
|
|
@ -30,7 +30,9 @@ namespace signalr
|
|||
{
|
||||
try
|
||||
{
|
||||
disconnect().get();
|
||||
pplx::task_completion_event<void> event;
|
||||
stop([event](std::exception_ptr) { event.set(); });
|
||||
pplx::create_task(event).get();
|
||||
}
|
||||
catch (...) // must not throw from the destructor
|
||||
{}
|
||||
|
|
@ -41,7 +43,106 @@ namespace signalr
|
|||
return transport_type::websockets;
|
||||
}
|
||||
|
||||
pplx::task<void> websocket_transport::connect(const std::string& url)
|
||||
// Note that the connection assumes that the error callback won't be fired when the result is being processed. This
|
||||
// may no longer be true when we replace the `receive_loop` with "on_message_received" and "on_close" events if they
|
||||
// can be fired on different threads in which case we will have to lock before setting groups token and message id.
|
||||
void websocket_transport::receive_loop(pplx::cancellation_token_source cts)
|
||||
{
|
||||
auto this_transport = shared_from_this();
|
||||
auto logger = this_transport->m_logger;
|
||||
|
||||
// Passing the `std::weak_ptr<websocket_transport>` prevents from a memory leak where we would capture the shared_ptr to
|
||||
// the transport in the continuation lambda and as a result as long as the loop runs the ref count would never get to
|
||||
// zero. Now we capture the weak pointer and get the shared pointer only when the continuation runs so the ref count is
|
||||
// incremented when the shared pointer is acquired and then decremented when it goes out of scope of the continuation.
|
||||
auto weak_transport = std::weak_ptr<websocket_transport>(this_transport);
|
||||
|
||||
auto websocket_client = this_transport->safe_get_websocket_client();
|
||||
|
||||
// There are two cases when we exit the loop. The first case is implicit - we pass the cancellation_token
|
||||
// to `then` (note this is after the lambda body) and if the token is cancelled the continuation will not
|
||||
// run at all. The second - explicit - case happens if the token gets cancelled after the continuation has
|
||||
// been started in which case we just stop the loop by not scheduling another receive task.
|
||||
websocket_client->receive([weak_transport, cts, logger, websocket_client](std::string message, std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] error receiving response from websocket: ")
|
||||
.append(e.what()));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] unknown error occurred when receiving response from websocket"));
|
||||
|
||||
exception = std::make_exception_ptr(signalr_exception("unknown error"));
|
||||
}
|
||||
|
||||
cts.cancel();
|
||||
|
||||
std::promise<void> promise;
|
||||
websocket_client->stop([&promise](std::exception_ptr exception)
|
||||
{
|
||||
if (exception != nullptr)
|
||||
{
|
||||
promise.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.set_value();
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
promise.get_future().get();
|
||||
}
|
||||
// We prefer the outer exception bubbling up to the user
|
||||
// REVIEW: log here?
|
||||
catch (...) {}
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->m_close_callback(exception);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->m_process_response_callback(message, nullptr);
|
||||
|
||||
if (!cts.get_token().is_canceled())
|
||||
{
|
||||
transport->receive_loop(cts);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<websocket_client> websocket_transport::safe_get_websocket_client()
|
||||
{
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_websocket_client_lock);
|
||||
auto websocket_client = m_websocket_client;
|
||||
|
||||
return websocket_client;
|
||||
}
|
||||
}
|
||||
|
||||
void websocket_transport::start(const std::string& url, transfer_format format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
web::uri uri(utility::conversions::to_string_t(url));
|
||||
_ASSERTE(uri.scheme() == _XPLATSTR("ws") || uri.scheme() == _XPLATSTR("wss"));
|
||||
|
|
@ -66,20 +167,21 @@ namespace signalr
|
|||
}
|
||||
|
||||
pplx::cancellation_token_source receive_loop_cts;
|
||||
pplx::task_completion_event<void> connect_tce;
|
||||
|
||||
auto transport = shared_from_this();
|
||||
|
||||
websocket_client->connect(url)
|
||||
.then([transport, connect_tce, receive_loop_cts](pplx::task<void> connect_task)
|
||||
websocket_client->start(url, format, [transport, receive_loop_cts, callback](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
connect_task.get();
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
transport->receive_loop(receive_loop_cts);
|
||||
connect_tce.set();
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
transport->m_logger.log(
|
||||
trace_level::errors,
|
||||
|
|
@ -87,23 +189,15 @@ namespace signalr
|
|||
.append(e.what()));
|
||||
|
||||
receive_loop_cts.cancel();
|
||||
connect_tce.set_exception(std::current_exception());
|
||||
callback(std::current_exception());
|
||||
}
|
||||
});
|
||||
|
||||
m_receive_loop_cts = receive_loop_cts;
|
||||
|
||||
return pplx::create_task(connect_tce);
|
||||
}
|
||||
}
|
||||
|
||||
pplx::task<void> websocket_transport::send(const std::string &data)
|
||||
{
|
||||
// send will return a faulted task if client has disconnected
|
||||
return safe_get_websocket_client()->send(data);
|
||||
}
|
||||
|
||||
pplx::task<void> websocket_transport::disconnect()
|
||||
void websocket_transport::stop(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
std::shared_ptr<websocket_client> websocket_client = nullptr;
|
||||
|
||||
|
|
@ -112,7 +206,8 @@ namespace signalr
|
|||
|
||||
if (m_receive_loop_cts.get_token().is_canceled())
|
||||
{
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
m_receive_loop_cts.cancel();
|
||||
|
|
@ -121,126 +216,54 @@ namespace signalr
|
|||
}
|
||||
|
||||
auto logger = m_logger;
|
||||
auto close_callback = m_close_callback;
|
||||
|
||||
return websocket_client->close()
|
||||
.then([logger](pplx::task<void> close_task)
|
||||
mutable {
|
||||
websocket_client->stop([logger, callback, close_callback](std::exception_ptr exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
close_task.get();
|
||||
if (exception != nullptr)
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
callback(nullptr);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
catch (const std::exception & e)
|
||||
{
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] exception when closing websocket: ")
|
||||
.append(e.what()));
|
||||
|
||||
callback(exception);
|
||||
}
|
||||
|
||||
close_callback(exception);
|
||||
});
|
||||
}
|
||||
|
||||
// Note that the connection assumes that the error callback won't be fired when the result is being processed. This
|
||||
// may no longer be true when we replace the `receive_loop` with "on_message_received" and "on_close" events if they
|
||||
// can be fired on different threads in which case we will have to lock before setting groups token and message id.
|
||||
void websocket_transport::receive_loop(pplx::cancellation_token_source cts)
|
||||
void websocket_transport::on_close(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
auto this_transport = shared_from_this();
|
||||
auto logger = this_transport->m_logger;
|
||||
m_close_callback = callback;
|
||||
}
|
||||
|
||||
// Passing the `std::weak_ptr<websocket_transport>` prevents from a memory leak where we would capture the shared_ptr to
|
||||
// the transport in the continuation lambda and as a result as long as the loop runs the ref count would never get to
|
||||
// zero. Now we capture the weak pointer and get the shared pointer only when the continuation runs so the ref count is
|
||||
// incremented when the shared pointer is acquired and then decremented when it goes out of scope of the continuation.
|
||||
auto weak_transport = std::weak_ptr<websocket_transport>(this_transport);
|
||||
void websocket_transport::on_receive(std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
m_process_response_callback = callback;
|
||||
}
|
||||
|
||||
auto websocket_client = this_transport->safe_get_websocket_client();
|
||||
|
||||
websocket_client->receive()
|
||||
// There are two cases when we exit the loop. The first case is implicit - we pass the cancellation_token
|
||||
// to `then` (note this is after the lambda body) and if the token is cancelled the continuation will not
|
||||
// run at all. The second - explicit - case happens if the token gets cancelled after the continuation has
|
||||
// been started in which case we just stop the loop by not scheduling another receive task.
|
||||
.then([weak_transport, cts](std::string message)
|
||||
void websocket_transport::send(std::string payload, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
safe_get_websocket_client()->send(payload, [callback](std::exception_ptr exception)
|
||||
{
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
if (exception != nullptr)
|
||||
{
|
||||
transport->process_response(message);
|
||||
|
||||
if (!cts.get_token().is_canceled())
|
||||
{
|
||||
transport->receive_loop(cts);
|
||||
}
|
||||
callback(exception);
|
||||
}
|
||||
}, cts.get_token())
|
||||
// this continuation is used to observe exceptions from the previous tasks. It will run always - even if one of
|
||||
// the previous continuations throws or was not scheduled due to the cancellation token being set to cancelled
|
||||
.then([weak_transport, logger, websocket_client, cts](pplx::task<void> task)
|
||||
mutable {
|
||||
try
|
||||
else
|
||||
{
|
||||
task.get();
|
||||
}
|
||||
catch (const pplx::task_canceled&)
|
||||
{
|
||||
cts.cancel();
|
||||
|
||||
logger.log(trace_level::info,
|
||||
std::string("[websocket transport] receive task canceled."));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
cts.cancel();
|
||||
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] error receiving response from websocket: ")
|
||||
.append(e.what()));
|
||||
|
||||
websocket_client->close()
|
||||
.then([](pplx::task<void> task)
|
||||
{
|
||||
try { task.get(); }
|
||||
catch (...) {}
|
||||
});
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->error(e);
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cts.cancel();
|
||||
|
||||
logger.log(
|
||||
trace_level::errors,
|
||||
std::string("[websocket transport] unknown error occurred when receiving response from websocket"));
|
||||
|
||||
websocket_client->close()
|
||||
.then([](pplx::task<void> task)
|
||||
{
|
||||
try { task.get(); }
|
||||
catch (...) {}
|
||||
});
|
||||
|
||||
auto transport = weak_transport.lock();
|
||||
if (transport)
|
||||
{
|
||||
transport->error(signalr_exception("unknown error"));
|
||||
}
|
||||
callback(nullptr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<websocket_client> websocket_transport::safe_get_websocket_client()
|
||||
{
|
||||
{
|
||||
const std::lock_guard<std::mutex> lock(m_websocket_client_lock);
|
||||
auto websocket_client = m_websocket_client;
|
||||
|
||||
return websocket_client;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ namespace signalr
|
|||
{
|
||||
public:
|
||||
static std::shared_ptr<transport> create(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger, const std::function<void(const std::string&)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback);
|
||||
const logger& logger);
|
||||
|
||||
~websocket_transport();
|
||||
|
||||
|
|
@ -25,31 +24,31 @@ namespace signalr
|
|||
|
||||
websocket_transport& operator=(const websocket_transport&) = delete;
|
||||
|
||||
pplx::task<void> connect(const std::string& url) override;
|
||||
|
||||
pplx::task<void> send(const std::string &data) override;
|
||||
|
||||
pplx::task<void> disconnect() override;
|
||||
|
||||
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 on_close(std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
void send(std::string payload, std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
void on_receive(std::function<void(std::string, std::exception_ptr)>) override;
|
||||
|
||||
private:
|
||||
websocket_transport(const std::function<std::shared_ptr<websocket_client>()>& websocket_client_factory,
|
||||
const logger& logger, const std::function<void(const std::string &)>& process_response_callback,
|
||||
std::function<void(const std::exception&)> error_callback);
|
||||
const logger& logger);
|
||||
|
||||
std::function<std::shared_ptr<websocket_client>()> m_websocket_client_factory;
|
||||
std::shared_ptr<websocket_client> m_websocket_client;
|
||||
std::mutex m_websocket_client_lock;
|
||||
std::mutex m_start_stop_lock;
|
||||
std::function<void(std::string, std::exception_ptr)> m_process_response_callback;
|
||||
std::function<void(std::exception_ptr)> m_close_callback;
|
||||
|
||||
pplx::cancellation_token_source m_receive_loop_cts;
|
||||
|
||||
void receive_loop(pplx::cancellation_token_source cts);
|
||||
|
||||
void handle_receive_error(const std::exception &e, pplx::cancellation_token_source cts,
|
||||
logger logger, std::weak_ptr<transport> weak_transport);
|
||||
|
||||
std::shared_ptr<websocket_client> safe_get_websocket_client();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,29 +44,32 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\connection_state.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_connection.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\hub_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\log_writer.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\trace_level.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transport_type.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\web_exception.h" />
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\_exports.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\case_insensitive_comparison_utils.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\connection_impl.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\constants.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\default_http_client.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\default_websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\http_sender.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\hub_connection_impl.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\callback_manager.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\logger.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiate.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiation_response.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\request_sender.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\stdafx.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\trace_log_writer.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\transport.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\transport_factory.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\url_builder.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_client.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_transport.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request.h" />
|
||||
<ClInclude Include="..\..\..\signalrclient\web_request_factory.h" />
|
||||
|
|
@ -76,13 +79,14 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\signalrclient\connection.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\default_http_client.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\default_websocket_client.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\http_sender.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\hub_connection_impl.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\callback_manager.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\logger.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\request_sender.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\negotiate.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\signalr_client_config.cpp" />
|
||||
<ClCompile Include="..\..\..\signalrclient\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
|
|
@ -133,4 +137,4 @@
|
|||
</ItemGroup>
|
||||
<WriteLinesToFile File="..\..\version.h" Lines="@(VersionHeaderContents)" OverWrite="true" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -39,9 +39,6 @@
|
|||
<ClInclude Include="..\..\..\signalrclient\negotiation_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\request_sender.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -60,9 +57,6 @@
|
|||
<ClInclude Include="..\..\..\signalrclient\web_response.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\websocket_transport.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
|
|
@ -105,6 +99,21 @@
|
|||
<ClInclude Include="..\..\resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\default_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\signalrclient\negotiate.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\transfer_format.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\..\include\signalrclient\websocket_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\dllmain.cpp">
|
||||
|
|
@ -128,9 +137,6 @@
|
|||
<ClCompile Include="..\..\..\signalrclient\logger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\request_sender.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\transport.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -164,6 +170,12 @@
|
|||
<ClCompile Include="..\..\..\signalrclient\signalr_client_config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\default_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\signalrclient\negotiate.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="..\..\Resource.rc">
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<ClInclude Include="..\..\memory_log_writer.h" />
|
||||
<ClInclude Include="..\..\stdafx.h" />
|
||||
<ClInclude Include="..\..\targetver.h" />
|
||||
<ClInclude Include="..\..\test_http_client.h" />
|
||||
<ClInclude Include="..\..\test_transport_factory.h" />
|
||||
<ClInclude Include="..\..\test_utils.h" />
|
||||
<ClInclude Include="..\..\test_websocket_client.h" />
|
||||
|
|
@ -60,11 +61,12 @@
|
|||
<ClCompile Include="..\..\hub_exception_tests.cpp" />
|
||||
<ClCompile Include="..\..\logger_tests.cpp" />
|
||||
<ClCompile Include="..\..\memory_log_writer.cpp" />
|
||||
<ClCompile Include="..\..\request_sender_tests.cpp" />
|
||||
<ClCompile Include="..\..\negotiate_tests.cpp" />
|
||||
<ClCompile Include="..\..\signalrclienttests.cpp" />
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\test_http_client.cpp" />
|
||||
<ClCompile Include="..\..\test_transport_factory.cpp" />
|
||||
<ClCompile Include="..\..\test_utils.cpp" />
|
||||
<ClCompile Include="..\..\test_websocket_client.cpp" />
|
||||
|
|
@ -97,4 +99,4 @@
|
|||
<IsTestProject>true</IsTestProject>
|
||||
<TestProjectSkipReason>Flaky, due to https://github.com/aspnet/AspNetCore/issues/8421</TestProjectSkipReason>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
@ -39,6 +39,9 @@
|
|||
<ClInclude Include="..\..\test_transport_factory.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\test_http_client.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\stdafx.cpp">
|
||||
|
|
@ -53,9 +56,6 @@
|
|||
<ClCompile Include="..\..\http_sender_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\request_sender_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\web_request_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
|
@ -98,8 +98,11 @@
|
|||
<ClCompile Include="..\..\case_insensitive_comparison_utils_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<ClCompile Include="..\..\test_http_client.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\negotiate_tests.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
@ -13,13 +13,14 @@
|
|||
#include "cpprest/ws_client.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include "signalrclient/web_exception.h"
|
||||
#include "test_http_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
static std::shared_ptr<connection_impl> create_connection(std::shared_ptr<websocket_client> websocket_client = create_test_websocket_client(),
|
||||
std::shared_ptr<log_writer> log_writer = std::make_shared<trace_log_writer>(), trace_level trace_level = trace_level::all)
|
||||
{
|
||||
return connection_impl::create(create_uri(), trace_level, log_writer, create_test_web_request_factory(),
|
||||
return connection_impl::create(create_uri(), trace_level, log_writer, create_test_http_client(),
|
||||
std::make_unique<test_transport_factory>(websocket_client));
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ TEST(connection_impl_connection_state, initial_connection_state_is_disconnected)
|
|||
TEST(connection_impl_start, cannot_start_non_disconnected_exception)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client);
|
||||
|
||||
connection->start().wait();
|
||||
|
|
@ -55,12 +56,9 @@ TEST(connection_impl_start, connection_state_is_connecting_when_connection_is_be
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string){ return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[](const std::string&)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
});
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")))); });
|
||||
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
||||
|
|
@ -84,7 +82,7 @@ TEST(connection_impl_start, connection_state_is_connecting_when_connection_is_be
|
|||
TEST(connection_impl_start, connection_state_is_connected_when_connection_established_succesfully)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client);
|
||||
connection->start().get();
|
||||
ASSERT_EQ(connection->get_connection_state(), connection_state::connected);
|
||||
|
|
@ -92,14 +90,14 @@ TEST(connection_impl_start, connection_state_is_connected_when_connection_establ
|
|||
|
||||
TEST(connection_impl_start, connection_state_is_disconnected_when_connection_cannot_be_established)
|
||||
{
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string&) -> std::unique_ptr<web_request>
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string&, http_request)
|
||||
{
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)404, "Bad request", ""));
|
||||
return http_response { 404, "" };
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::none, std::make_shared<trace_log_writer>(),
|
||||
std::move(web_request_factory), std::make_unique<transport_factory>());
|
||||
std::move(http_client), std::make_unique<transport_factory>());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -116,9 +114,9 @@ TEST(connection_impl_start, throws_for_invalid_uri)
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
|
||||
auto connection = connection_impl::create(":1\t ä bad_uri&a=b", trace_level::errors, writer, create_test_web_request_factory(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
auto connection = connection_impl::create(":1\t ä bad_uri&a=b", trace_level::errors, writer, create_test_http_client(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -139,15 +137,15 @@ TEST(connection_impl_start, start_sets_id_query_string)
|
|||
std::string query_string;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string&) { return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[&query_string](const std::string& url)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[&query_string](const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
query_string = utility::conversions::to_utf8string(url.substr(url.find('?') + 1));
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception("connecting failed"));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception("connecting failed")));
|
||||
});
|
||||
|
||||
auto connection = connection_impl::create(create_uri(""), trace_level::errors, writer, create_test_web_request_factory(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
auto connection = connection_impl::create(create_uri(""), trace_level::errors, writer, create_test_http_client(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -166,15 +164,15 @@ TEST(connection_impl_start, start_appends_id_query_string)
|
|||
std::string query_string;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string) { return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[&query_string](const std::string& url)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[&query_string](const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
query_string = utility::conversions::to_utf8string(url.substr(url.find('?') + 1));
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, create_test_web_request_factory(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, create_test_http_client(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -191,14 +189,14 @@ TEST(connection_impl_start, start_logs_exceptions)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string&) -> std::unique_ptr<web_request>
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string&, http_request)
|
||||
{
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)404, "Bad request", ""));
|
||||
return http_response{ 404, "" };
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::errors, writer,
|
||||
std::move(web_request_factory), std::make_unique<transport_factory>());
|
||||
std::move(http_client), std::make_unique<transport_factory>());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -211,20 +209,20 @@ TEST(connection_impl_start, start_logs_exceptions)
|
|||
ASSERT_FALSE(log_entries.empty());
|
||||
|
||||
auto entry = remove_date_from_log_entry(log_entries[0]);
|
||||
ASSERT_EQ("[error ] connection could not be started due to: web exception - 404 Bad request\n", entry);
|
||||
ASSERT_EQ("[error ] connection could not be started due to: negotiate failed with status code 404\n", entry);
|
||||
}
|
||||
|
||||
|
||||
TEST(connection_impl_start, start_propagates_exceptions_from_negotiate)
|
||||
{
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string&) -> std::unique_ptr<web_request>
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string&, http_request)
|
||||
{
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)404, "Bad request", ""));
|
||||
return http_response{ 404, "" };
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::none, std::make_shared<trace_log_writer>(),
|
||||
std::move(web_request_factory), std::make_unique<transport_factory>());
|
||||
std::move(http_client), std::make_unique<transport_factory>());
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -233,7 +231,7 @@ TEST(connection_impl_start, start_propagates_exceptions_from_negotiate)
|
|||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ASSERT_EQ(_XPLATSTR("web exception - 404 Bad request"), utility::conversions::to_string_t(e.what()));
|
||||
ASSERT_STREQ("negotiate failed with status code 404", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,11 +240,11 @@ TEST(connection_impl_start, start_fails_if_transport_connect_throws)
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string){ return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[](const std::string&)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback){ callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
|
@ -274,10 +272,10 @@ TEST(connection_impl_send, send_fails_if_transport_fails_when_receiving_messages
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto websocket_client = create_test_websocket_client([]() { return pplx::task_from_result(std::string("")); },
|
||||
/* send function */ [](const std::string &)
|
||||
auto websocket_client = create_test_websocket_client([](std::function<void(std::string, std::exception_ptr)> callback) { callback("", nullptr); },
|
||||
/* send function */ [](const std::string &, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(std::runtime_error("send error"));
|
||||
callback(std::make_exception_ptr(std::runtime_error("send error")));
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
|
@ -307,29 +305,29 @@ TEST(connection_impl_start, start_fails_if_negotiate_request_fails)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string&)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string&, http_request)
|
||||
{
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)400, "Bad Request"));
|
||||
return http_response{ 400, "" };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_receive_function([]()->pplx::task<std::string>
|
||||
websocket_client->set_receive_function([](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_result(std::string("{ }\x1e"));
|
||||
callback("{ }\x1e", nullptr);
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
connection->start().get();
|
||||
ASSERT_TRUE(false); // exception not thrown
|
||||
}
|
||||
catch (const web_exception &e)
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
ASSERT_STREQ("web exception - 400 Bad Request", e.what());
|
||||
ASSERT_STREQ("negotiate failed with status code 400", e.what());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -337,26 +335,31 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_has_error)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
? "{ \"error\": \"bad negotiate\" }"
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_connect_function([tce](const std::string&) mutable
|
||||
websocket_client->set_connect_function([tce](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task<void>(tce);
|
||||
pplx::task<void>(tce)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -367,32 +370,39 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_has_error)
|
|||
{
|
||||
ASSERT_STREQ("bad negotiate", e.what());
|
||||
}
|
||||
|
||||
tce.set();
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_websockets)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
? "{ \"availableTransports\": [ { \"transport\": \"ServerSentEvents\", \"transferFormats\": [ \"Text\" ] } ] }"
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_connect_function([tce](const std::string&) mutable
|
||||
websocket_client->set_connect_function([tce](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task<void>(tce);
|
||||
pplx::task<void>(tce)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -403,32 +413,39 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_webs
|
|||
{
|
||||
ASSERT_STREQ("The server does not support WebSockets which is currently the only transport supported by this client.", e.what());
|
||||
}
|
||||
|
||||
tce.set();
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_transports)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
? "{ \"availableTransports\": [ ] }"
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_connect_function([tce](const std::string&) mutable
|
||||
websocket_client->set_connect_function([tce](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task<void>(tce);
|
||||
pplx::task<void>(tce)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -439,32 +456,39 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_does_not_have_tran
|
|||
{
|
||||
ASSERT_STREQ("The server does not support WebSockets which is currently the only transport supported by this client.", e.what());
|
||||
}
|
||||
|
||||
tce.set();
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, start_fails_if_negotiate_response_is_invalid)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
? "{ \"availableTransports\": [ "
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_connect_function([tce](const std::string&) mutable
|
||||
websocket_client->set_connect_function([tce](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task<void>(tce);
|
||||
pplx::task<void>(tce)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -475,13 +499,15 @@ TEST(connection_impl_start, start_fails_if_negotiate_response_is_invalid)
|
|||
{
|
||||
ASSERT_STREQ("* Line 1, Column 28 Syntax error: Malformed token", e.what());
|
||||
}
|
||||
|
||||
tce.set();
|
||||
}
|
||||
|
||||
TEST(connection_impl_start, negotiate_follows_redirect)
|
||||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -497,21 +523,21 @@ TEST(connection_impl_start, negotiate_follows_redirect)
|
|||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
|
||||
std::string connectUrl;
|
||||
websocket_client->set_connect_function([&connectUrl](const std::string& url)
|
||||
websocket_client->set_connect_function([&connectUrl](const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
connectUrl = url;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
connection->start().get();
|
||||
|
||||
|
|
@ -523,7 +549,7 @@ TEST(connection_impl_start, negotiate_redirect_uses_accessToken)
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
std::string accessToken;
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([&accessToken](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([&accessToken](const std::string& url, http_request request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -539,26 +565,22 @@ TEST(connection_impl_start, negotiate_redirect_uses_accessToken)
|
|||
}
|
||||
}
|
||||
|
||||
auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
request->on_get_response = [&accessToken](web_request_stub& stub)
|
||||
{
|
||||
accessToken = utility::conversions::to_utf8string(stub.m_signalr_client_config.get_http_headers()[_XPLATSTR("Authorization")]);
|
||||
};
|
||||
return std::unique_ptr<web_request>(request);
|
||||
accessToken = request.headers["Authorization"];
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
|
||||
std::string connectUrl;
|
||||
websocket_client->set_connect_function([&connectUrl](const std::string& url)
|
||||
websocket_client->set_connect_function([&connectUrl](const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
connectUrl = url;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
connection->start().get();
|
||||
|
||||
|
|
@ -570,7 +592,7 @@ TEST(connection_impl_start, negotiate_fails_after_too_many_redirects)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -579,14 +601,14 @@ TEST(connection_impl_start, negotiate_fails_after_too_many_redirects)
|
|||
response_body = "{ \"url\": \"http://redirected\" }";
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -602,7 +624,7 @@ TEST(connection_impl_start, negotiate_fails_if_ProtocolVersion_in_response)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -610,14 +632,14 @@ TEST(connection_impl_start, negotiate_fails_if_ProtocolVersion_in_response)
|
|||
response_body = "{\"ProtocolVersion\" : \"\" }";
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -634,7 +656,7 @@ TEST(connection_impl_start, negotiate_redirect_does_not_overwrite_url)
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
int redirectCount = 0;
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([&redirectCount](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([&redirectCount](const std::string& url, http_request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -651,14 +673,14 @@ TEST(connection_impl_start, negotiate_redirect_does_not_overwrite_url)
|
|||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
connection->start().get();
|
||||
ASSERT_EQ(1, redirectCount);
|
||||
|
|
@ -673,15 +695,15 @@ TEST(connection_impl_start, negotiate_redirect_uses_own_query_string)
|
|||
std::string query_string;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string) { return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[&query_string](const std::string& url)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[&query_string](const std::string& url, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
query_string = url.substr(url.find('?') + 1);
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
std::string response_body = "";
|
||||
if (url.find("/negotiate") != std::string::npos)
|
||||
|
|
@ -697,10 +719,10 @@ TEST(connection_impl_start, negotiate_redirect_uses_own_query_string)
|
|||
}
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
auto connection = connection_impl::create(create_uri("a=b&c=d"), trace_level::errors, writer, std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -717,18 +739,23 @@ TEST(connection_impl_start, start_fails_if_connect_request_times_out)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto web_request_factory = create_test_web_request_factory();
|
||||
auto http_client = create_test_http_client();
|
||||
|
||||
pplx::task_completion_event<void> tce;
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_connect_function([tce](const std::string&) mutable
|
||||
websocket_client->set_connect_function([tce](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task<void>(tce);
|
||||
pplx::task<void>(tce)
|
||||
.then([callback](pplx::task<void> prev_task)
|
||||
{
|
||||
prev_task.get();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::messages, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -739,6 +766,8 @@ TEST(connection_impl_start, start_fails_if_connect_request_times_out)
|
|||
{
|
||||
ASSERT_STREQ("transport timed out when trying to connect", e.what());
|
||||
}
|
||||
|
||||
tce.set();
|
||||
}
|
||||
|
||||
TEST(connection_impl_process_response, process_response_logs_messages)
|
||||
|
|
@ -746,10 +775,10 @@ TEST(connection_impl_process_response, process_response_logs_messages)
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
auto wait_receive = std::make_shared<event>();
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [wait_receive]()
|
||||
/* receive function */ [wait_receive](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
wait_receive->set();
|
||||
return pplx::task_from_result(std::string("{ }"));
|
||||
callback("{ }", nullptr);
|
||||
});
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::messages);
|
||||
|
||||
|
|
@ -769,11 +798,11 @@ TEST(connection_impl_send, message_sent)
|
|||
std::string actual_message;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */ [&actual_message](const std::string& message)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */ [&actual_message](const std::string& message, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
actual_message = message;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client);
|
||||
|
|
@ -809,13 +838,13 @@ TEST(connection_impl_send, exceptions_from_send_logged_and_propagated)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []()
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_result(std::string("{}"));
|
||||
callback("{}", nullptr);
|
||||
},
|
||||
/* send function */ [](const std::string&)
|
||||
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(std::runtime_error("error"));
|
||||
callback(std::make_exception_ptr(std::runtime_error("error")));
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
|
@ -846,7 +875,7 @@ TEST(connection_impl_set_message_received, callback_invoked_when_message_receive
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -857,7 +886,7 @@ TEST(connection_impl_set_message_received, callback_invoked_when_message_receive
|
|||
|
||||
call_number = std::min(call_number + 1, 2);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client);
|
||||
|
|
@ -889,7 +918,7 @@ TEST(connection_impl_set_message_received, exception_from_callback_caught_and_lo
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -900,7 +929,7 @@ TEST(connection_impl_set_message_received, exception_from_callback_caught_and_lo
|
|||
|
||||
call_number = std::min(call_number + 1, 2);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
|
@ -935,7 +964,7 @@ TEST(connection_impl_set_message_received, non_std_exception_from_callback_caugh
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -946,7 +975,7 @@ TEST(connection_impl_set_message_received, non_std_exception_from_callback_caugh
|
|||
|
||||
call_number = std::min(call_number + 1, 2);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
|
@ -980,7 +1009,7 @@ TEST(connection_impl_set_message_received, non_std_exception_from_callback_caugh
|
|||
void can_be_set_only_in_disconnected_state(std::function<void(connection_impl *)> callback, const char* expected_exception_message)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client);
|
||||
|
||||
connection->start().get();
|
||||
|
|
@ -1030,14 +1059,15 @@ TEST(connection_impl_stop, stopping_disconnecting_connection_returns_cancelled_t
|
|||
auto writer = std::shared_ptr<log_writer>{std::make_shared<memory_log_writer>()};
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */ [](const std::string){ return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */ [&close_event](const std::string&) { return pplx::task_from_result(); },
|
||||
/* close function */ [&close_event]()
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */ [](const std::string, std::function<void(std::exception_ptr)> callback){ callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */ [&close_event](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(nullptr); },
|
||||
/* close function */ [&close_event](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::create_task([&close_event]()
|
||||
pplx::create_task([&close_event, callback]()
|
||||
{
|
||||
close_event.wait();
|
||||
callback(nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -1072,7 +1102,7 @@ TEST(connection_impl_stop, can_start_and_stop_connection)
|
|||
auto writer = std::shared_ptr<log_writer>{std::make_shared<memory_log_writer>()};
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
connection->start()
|
||||
|
|
@ -1095,7 +1125,7 @@ TEST(connection_impl_stop, can_start_and_stop_connection_multiple_times)
|
|||
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
connection->start()
|
||||
|
|
@ -1137,10 +1167,10 @@ TEST(connection_impl_stop, dtor_stops_the_connection)
|
|||
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []()
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
return pplx::task_from_result(std::string("{ }\x1e"));
|
||||
callback("{ }\x1e", nullptr);
|
||||
});
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
|
|
@ -1170,10 +1200,10 @@ TEST(connection_impl_stop, stop_cancels_ongoing_start_request)
|
|||
auto disconnect_completed_event = std::make_shared<event>();
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [disconnect_completed_event]()
|
||||
/* receive function */ [disconnect_completed_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
disconnect_completed_event->wait();
|
||||
return pplx::task_from_result(std::string("{ }\x1e"));
|
||||
callback("{ }\x1e", nullptr);
|
||||
});
|
||||
|
||||
auto writer = std::shared_ptr<log_writer>{std::make_shared<memory_log_writer>()};
|
||||
|
|
@ -1207,7 +1237,7 @@ TEST(connection_impl_stop, stop_cancels_ongoing_start_request)
|
|||
|
||||
TEST(connection_impl_stop, ongoing_start_request_canceled_if_connection_stopped_before_init_message_received)
|
||||
{
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
|
|
@ -1215,17 +1245,17 @@ TEST(connection_impl_stop, ongoing_start_request_canceled_if_connection_stopped_
|
|||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }"
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = create_test_websocket_client(/*receive function*/ []()
|
||||
auto websocket_client = create_test_websocket_client(/*receive function*/ [](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_result<std::string>("{}");
|
||||
callback("{}", nullptr);
|
||||
});
|
||||
|
||||
auto writer = std::shared_ptr<log_writer>{std::make_shared<memory_log_writer>()};
|
||||
auto connection = connection_impl::create(create_uri(), trace_level::all, writer,
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
auto start_task = connection->start();
|
||||
connection->stop().get();
|
||||
|
|
@ -1253,7 +1283,7 @@ TEST(connection_impl_stop, ongoing_start_request_canceled_if_connection_stopped_
|
|||
TEST(connection_impl_stop, stop_invokes_disconnected_callback)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client);
|
||||
|
||||
auto disconnected_invoked = false;
|
||||
|
|
@ -1274,7 +1304,7 @@ TEST(connection_impl_stop, std_exception_for_disconnected_callback_caught_and_lo
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -1284,7 +1314,7 @@ TEST(connection_impl_stop, std_exception_for_disconnected_callback_caught_and_lo
|
|||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
||||
|
|
@ -1307,7 +1337,7 @@ TEST(connection_impl_stop, exception_for_disconnected_callback_caught_and_logged
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -1317,7 +1347,7 @@ TEST(connection_impl_stop, exception_for_disconnected_callback_caught_and_logged
|
|||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
||||
|
|
@ -1338,7 +1368,7 @@ TEST(connection_impl_config, custom_headers_set_in_requests)
|
|||
{
|
||||
auto writer = std::shared_ptr<log_writer>{std::make_shared<memory_log_writer>()};
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find("/negotiate") != std::string::npos
|
||||
|
|
@ -1346,23 +1376,23 @@ TEST(connection_impl_config, custom_headers_set_in_requests)
|
|||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }"
|
||||
: "";
|
||||
|
||||
auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
/*auto request = new web_request_stub((unsigned short)200, "OK", response_body);
|
||||
request->on_get_response = [](web_request_stub& request)
|
||||
{
|
||||
auto http_headers = request.m_signalr_client_config.get_http_headers();
|
||||
ASSERT_EQ(1U, http_headers.size());
|
||||
ASSERT_EQ(_XPLATSTR("42"), http_headers[_XPLATSTR("Answer")]);
|
||||
};
|
||||
};*/
|
||||
|
||||
return std::unique_ptr<web_request>(request);
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::state_changes,
|
||||
writer, std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
writer, std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
signalr::signalr_client_config signalr_client_config{};
|
||||
auto http_headers = signalr_client_config.get_http_headers();
|
||||
|
|
@ -1393,7 +1423,7 @@ TEST(connection_impl_change_state, change_state_logs)
|
|||
{
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
connection->start().wait();
|
||||
|
|
@ -1410,11 +1440,11 @@ TEST(connection_id, connection_id_is_set_if_start_fails_but_negotiate_request_su
|
|||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](){ return pplx::task_from_exception<std::string>(std::runtime_error("should not be invoked")); },
|
||||
/* send function */ [](const std::string){ return pplx::task_from_exception<void>(std::runtime_error("should not be invoked")); },
|
||||
/* connect function */[](const std::string&)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback){ callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* send function */ [](const std::string, std::function<void(std::exception_ptr)> callback){ callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
|
||||
/* connect function */[](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::errors);
|
||||
|
|
@ -1443,7 +1473,7 @@ TEST(connection_id, can_get_connection_id_when_connection_in_connected_state)
|
|||
auto writer = std::shared_ptr<log_writer>{ std::make_shared<memory_log_writer>() };
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](){ return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback){ callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
std::string connection_id;
|
||||
|
|
@ -1462,7 +1492,7 @@ TEST(connection_id, can_get_connection_id_after_connection_has_stopped)
|
|||
auto writer = std::shared_ptr<log_writer>{ std::make_shared<memory_log_writer>() };
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](){ return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback){ callback("{ }\x1e", nullptr); });
|
||||
auto connection = create_connection(websocket_client, writer, trace_level::state_changes);
|
||||
|
||||
connection->start()
|
||||
|
|
@ -1481,9 +1511,9 @@ TEST(connection_id, connection_id_reset_when_starting_connection)
|
|||
auto writer = std::shared_ptr<log_writer>{ std::make_shared<memory_log_writer>() };
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [](){ return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback){ callback("{ }\x1e", nullptr); });
|
||||
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([&fail_http_requests](const std::string &url) -> std::unique_ptr<web_request>
|
||||
auto http_client = std::make_unique<test_http_client>([&fail_http_requests](const std::string& url, http_request)
|
||||
{
|
||||
if (!fail_http_requests) {
|
||||
auto response_body =
|
||||
|
|
@ -1492,15 +1522,15 @@ TEST(connection_id, connection_id_reset_when_starting_connection)
|
|||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }"
|
||||
: "";
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response{ 200, response_body };
|
||||
}
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)500, "Internal Server Error", ""));
|
||||
return http_response{ 500, "" };
|
||||
});
|
||||
|
||||
auto connection =
|
||||
connection_impl::create(create_uri(), trace_level::none, std::make_shared<trace_log_writer>(),
|
||||
std::move(web_request_factory), std::make_unique<test_transport_factory>(websocket_client));
|
||||
std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
|
||||
|
||||
connection->start()
|
||||
.then([connection]()
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include "stdafx.h"
|
||||
#include "test_utils.h"
|
||||
#include "test_transport_factory.h"
|
||||
#include "test_web_request_factory.h"
|
||||
#include "test_http_client.h"
|
||||
#include "hub_connection_impl.h"
|
||||
#include "trace_log_writer.h"
|
||||
#include "memory_log_writer.h"
|
||||
|
|
@ -17,7 +17,7 @@ std::shared_ptr<hub_connection_impl> create_hub_connection(std::shared_ptr<webso
|
|||
std::shared_ptr<log_writer> log_writer = std::make_shared<trace_log_writer>(), trace_level trace_level = trace_level::all)
|
||||
{
|
||||
return hub_connection_impl::create(create_uri(), trace_level, log_writer,
|
||||
create_test_web_request_factory(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
create_test_http_client(), std::make_unique<test_transport_factory>(websocket_client));
|
||||
}
|
||||
|
||||
TEST(url, negotiate_appended_to_url)
|
||||
|
|
@ -27,14 +27,14 @@ TEST(url, negotiate_appended_to_url)
|
|||
for (const auto& base_url : base_urls)
|
||||
{
|
||||
std::string requested_url;
|
||||
auto web_request_factory = std::make_unique<test_web_request_factory>([&requested_url](const std::string& url)
|
||||
auto http_client = std::make_unique<test_http_client>([&requested_url](const std::string& url, http_request)
|
||||
{
|
||||
requested_url = url;
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)404, "Bad request", ""));
|
||||
return http_response{ 404, "" };
|
||||
});
|
||||
|
||||
auto hub_connection = hub_connection_impl::create(base_url, trace_level::none,
|
||||
std::make_shared<trace_log_writer>(), std::move(web_request_factory),
|
||||
std::make_shared<trace_log_writer>(), std::move(http_client),
|
||||
std::make_unique<test_transport_factory>(create_test_websocket_client()));
|
||||
|
||||
try
|
||||
|
|
@ -50,7 +50,7 @@ TEST(url, negotiate_appended_to_url)
|
|||
TEST(start, start_starts_connection)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* 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();
|
||||
|
|
@ -62,8 +62,8 @@ TEST(start, start_sends_handshake)
|
|||
{
|
||||
auto message = std::make_shared<std::string>();
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */ [message](const std::string& msg) { *message = msg; return pplx::task_from_result(); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* 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();
|
||||
|
|
@ -78,11 +78,11 @@ TEST(start, start_waits_for_handshake_response)
|
|||
pplx::task_completion_event<void> tce;
|
||||
pplx::task_completion_event<void> tceWaitForSend;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [tce, tceWaitForSend]()
|
||||
/* receive function */ [tce, tceWaitForSend](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
tceWaitForSend.set();
|
||||
pplx::task<void>(tce).get();
|
||||
return pplx::task_from_result(std::string("{ }\x1e"));
|
||||
callback("{ }\x1e", nullptr);
|
||||
});
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ TEST(start, start_waits_for_handshake_response)
|
|||
TEST(start, start_fails_for_handshake_response_with_error)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{\"error\":\"bad things\"}\x1e")); });
|
||||
/* 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);
|
||||
|
||||
try
|
||||
|
|
@ -119,11 +119,15 @@ TEST(start, start_fails_if_stop_called_before_handshake_response)
|
|||
pplx::task_completion_event<std::string> tce;
|
||||
pplx::task_completion_event<void> tceWaitForSend;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [tce]() { return pplx::task<std::string>(tce); },
|
||||
/* send function */ [tceWaitForSend](const std::string &)
|
||||
/* receive function */ [tce](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
auto str = pplx::task<std::string>(tce).get();
|
||||
callback(str, nullptr);
|
||||
},
|
||||
/* send function */ [tceWaitForSend](const std::string &, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
tceWaitForSend.set();
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
|
|
@ -147,7 +151,7 @@ TEST(start, start_fails_if_stop_called_before_handshake_response)
|
|||
TEST(stop, stop_stops_connection)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* 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();
|
||||
|
|
@ -159,7 +163,7 @@ TEST(stop, stop_stops_connection)
|
|||
TEST(stop, disconnected_callback_called_when_hub_connection_stops)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
auto disconnected_invoked = false;
|
||||
|
|
@ -177,7 +181,7 @@ TEST(stop, connection_stopped_when_going_out_of_scope)
|
|||
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* 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();
|
||||
|
|
@ -205,7 +209,7 @@ TEST(stop, stop_cancels_pending_callbacks)
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -218,7 +222,7 @@ TEST(stop, stop_cancels_pending_callbacks)
|
|||
call_number++;
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -241,7 +245,7 @@ TEST(stop, pending_callbacks_finished_if_hub_connections_goes_out_of_scope)
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -254,7 +258,7 @@ TEST(stop, pending_callbacks_finished_if_hub_connections_goes_out_of_scope)
|
|||
call_number++;
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
pplx::task<web::json::value> t;
|
||||
|
|
@ -280,7 +284,7 @@ TEST(hub_invocation, hub_connection_invokes_users_code_on_hub_invocations)
|
|||
{
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number]()
|
||||
/* receive function */ [call_number](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -290,7 +294,7 @@ TEST(hub_invocation, hub_connection_invokes_users_code_on_hub_invocations)
|
|||
|
||||
call_number = std::min(call_number + 1, 1);
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -315,16 +319,17 @@ TEST(send, creates_correct_payload)
|
|||
bool handshakeReceived = false;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
payload = m;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -341,7 +346,7 @@ TEST(send, does_not_wait_for_server_response)
|
|||
pplx::task_completion_event<void> waitForSend;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [waitForSend, call_number]() mutable
|
||||
/* receive function */ [waitForSend, call_number](std::function<void(std::string, std::exception_ptr)> callback) mutable
|
||||
{
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -356,7 +361,7 @@ TEST(send, does_not_wait_for_server_response)
|
|||
pplx::task<void>(waitForSend).get();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -373,16 +378,17 @@ TEST(invoke, creates_correct_payload)
|
|||
bool handshakeReceived = false;
|
||||
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m)
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[&payload, &handshakeReceived](const std::string& m, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
payload = m;
|
||||
return pplx::task_from_exception<void>(std::runtime_error("error"));
|
||||
callback(std::make_exception_ptr(std::runtime_error("error")));
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -404,15 +410,16 @@ TEST(invoke, callback_not_called_if_send_throws)
|
|||
{
|
||||
bool handshakeReceived = false;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); },
|
||||
/* send function */[handshakeReceived](const std::string&) mutable
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); },
|
||||
/* send function */[handshakeReceived](const std::string&, std::function<void(std::exception_ptr)> callback) mutable
|
||||
{
|
||||
if (handshakeReceived)
|
||||
{
|
||||
return pplx::task_from_exception<void>(std::runtime_error("error"));
|
||||
callback(std::make_exception_ptr(std::runtime_error("error")));
|
||||
return;
|
||||
}
|
||||
handshakeReceived = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -440,7 +447,7 @@ TEST(invoke, invoke_returns_value_returned_from_the_server)
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event]()
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -455,7 +462,7 @@ TEST(invoke, invoke_returns_value_returned_from_the_server)
|
|||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -476,7 +483,7 @@ TEST(invoke, invoke_propagates_errors_from_server_as_hub_exceptions)
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event]()
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -491,7 +498,7 @@ TEST(invoke, invoke_propagates_errors_from_server_as_hub_exceptions)
|
|||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -519,7 +526,7 @@ TEST(invoke, unblocks_task_when_server_completes_call)
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event]()
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -534,7 +541,7 @@ TEST(invoke, unblocks_task_when_server_completes_call)
|
|||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -557,7 +564,7 @@ TEST(receive, logs_if_callback_for_given_id_not_found)
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, message_received_event, handshake_sent]()
|
||||
/* receive function */ [call_number, message_received_event, handshake_sent](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -575,12 +582,12 @@ TEST(receive, logs_if_callback_for_given_id_not_found)
|
|||
message_received_event->set();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
},
|
||||
[handshake_sent](const std::string&)
|
||||
[handshake_sent](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
handshake_sent->set();
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
|
@ -602,7 +609,7 @@ TEST(invoke_void, invoke_creates_runtime_error)
|
|||
|
||||
int call_number = -1;
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ [call_number, callback_registered_event]()
|
||||
/* receive function */ [call_number, callback_registered_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
mutable {
|
||||
std::string responses[]
|
||||
{
|
||||
|
|
@ -617,7 +624,7 @@ TEST(invoke_void, invoke_creates_runtime_error)
|
|||
callback_registered_event->wait();
|
||||
}
|
||||
|
||||
return pplx::task_from_result(responses[call_number]);
|
||||
callback(responses[call_number], nullptr);
|
||||
});
|
||||
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
|
@ -643,7 +650,7 @@ TEST(invoke_void, invoke_creates_runtime_error)
|
|||
TEST(connection_id, can_get_connection_id)
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("{ }\x1e", nullptr); });
|
||||
auto hub_connection = create_hub_connection(websocket_client);
|
||||
|
||||
ASSERT_EQ("", hub_connection->get_connection_id());
|
||||
|
|
@ -692,7 +699,7 @@ TEST(on, cannot_register_handler_if_connection_not_in_disconnected_state)
|
|||
try
|
||||
{
|
||||
auto websocket_client = create_test_websocket_client(
|
||||
/* receive function */ []() { return pplx::task_from_result(std::string("{ }\x1e")); });
|
||||
/* 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();
|
||||
|
|
|
|||
|
|
@ -2,46 +2,42 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "cpprest/details/basic_types.h"
|
||||
#include "signalrclient/web_exception.h"
|
||||
#include "request_sender.h"
|
||||
#include "web_request_stub.h"
|
||||
#include "test_web_request_factory.h"
|
||||
#include "signalrclient/signalr_exception.h"
|
||||
#include "negotiate.h"
|
||||
#include "test_http_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
TEST(request_sender_negotiate, request_created_with_correct_url)
|
||||
TEST(negotiate, request_created_with_correct_url)
|
||||
{
|
||||
std::string requested_url;
|
||||
auto request_factory = test_web_request_factory([&requested_url](const std::string& url) -> std::unique_ptr<web_request>
|
||||
auto http_client = test_http_client([&requested_url](const std::string& url, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{ \"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
|
||||
"\"availableTransports\" : [] }");
|
||||
|
||||
requested_url = url;
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
request_sender::negotiate(request_factory, "http://fake/signalr").get();
|
||||
negotiate::negotiate(http_client, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("http://fake/signalr/negotiate", requested_url);
|
||||
}
|
||||
|
||||
TEST(request_sender_negotiate, negotiation_request_sent_and_response_serialized)
|
||||
TEST(negotiate, negotiation_request_sent_and_response_serialized)
|
||||
{
|
||||
auto request_factory = test_web_request_factory([](const std::string&) -> std::unique_ptr<web_request>
|
||||
auto request_factory = test_http_client([](const std::string&, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
|
||||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] },"
|
||||
"{ \"transport\": \"ServerSentEvents\", \"transferFormats\": [ \"Text\" ] } ] }");
|
||||
|
||||
return std::unique_ptr<web_request>(new web_request_stub((unsigned short)200, "OK", response_body));
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
auto response = request_sender::negotiate(request_factory, "http://fake/signalr").get();
|
||||
auto response = negotiate::negotiate(request_factory, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", response.connectionId);
|
||||
ASSERT_EQ(2u, response.availableTransports.size());
|
||||
|
|
@ -51,3 +47,20 @@ TEST(request_sender_negotiate, negotiation_request_sent_and_response_serialized)
|
|||
ASSERT_EQ(1u, response.availableTransports[1].transfer_formats.size());
|
||||
ASSERT_EQ("Text", response.availableTransports[1].transfer_formats[0]);
|
||||
}
|
||||
|
||||
TEST(negotiate, negotiation_response_with_redirect)
|
||||
{
|
||||
auto request_factory = test_http_client([](const std::string&, http_request request)
|
||||
{
|
||||
std::string response_body(
|
||||
"{\"url\" : \"http://redirect\", "
|
||||
"\"accessToken\" : \"secret\" }");
|
||||
|
||||
return http_response(200, response_body);
|
||||
});
|
||||
|
||||
auto response = negotiate::negotiate(request_factory, "http://fake/signalr").get();
|
||||
|
||||
ASSERT_EQ("http://redirect", response.url);
|
||||
ASSERT_EQ("secret", response.accessToken);
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "test_http_client.h"
|
||||
|
||||
test_http_client::test_http_client(std::function<http_response(const std::string& url, http_request request)> create_http_response_fn)
|
||||
: m_http_response(create_http_response_fn)
|
||||
{
|
||||
}
|
||||
|
||||
void test_http_client::send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback)
|
||||
{
|
||||
http_response response;
|
||||
std::exception_ptr exception;
|
||||
try
|
||||
{
|
||||
response = m_http_response(url, request);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
exception = std::current_exception();
|
||||
}
|
||||
|
||||
callback(std::move(response), exception);
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "signalrclient/http_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
class test_http_client : public http_client
|
||||
{
|
||||
public:
|
||||
test_http_client(std::function<http_response(const std::string& url, http_request request)> create_http_response_fn);
|
||||
void send(std::string url, http_request request, std::function<void(http_response, std::exception_ptr)> callback) override;
|
||||
private:
|
||||
std::function<http_response(const std::string& url, http_request request)> m_http_response;
|
||||
};
|
||||
|
|
@ -10,12 +10,11 @@ test_transport_factory::test_transport_factory(const std::shared_ptr<websocket_c
|
|||
{ }
|
||||
|
||||
std::shared_ptr<transport> test_transport_factory::create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config&, std::function<void(const std::string&)> process_message_callback,
|
||||
std::function<void(const std::exception&)> error_callback)
|
||||
const signalr_client_config&)
|
||||
{
|
||||
if (transport_type == signalr::transport_type::websockets)
|
||||
{
|
||||
return websocket_transport::create([&](){ return m_websocket_client; }, logger, process_message_callback, error_callback);
|
||||
return websocket_transport::create([&](){ return m_websocket_client; }, logger);
|
||||
}
|
||||
|
||||
throw std::runtime_error("not supported");
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "transport_factory.h"
|
||||
#include "websocket_client.h"
|
||||
#include "signalrclient/websocket_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
|
|
@ -14,9 +14,7 @@ public:
|
|||
test_transport_factory(const std::shared_ptr<websocket_client>& websocket_client);
|
||||
|
||||
std::shared_ptr<transport> create_transport(transport_type transport_type, const logger& logger,
|
||||
const signalr_client_config& signalr_client_config,
|
||||
std::function<void(const std::string&)> process_message_callback,
|
||||
std::function<void(const std::exception&)> error_callback) override;
|
||||
const signalr_client_config& signalr_client_config) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<websocket_client> m_websocket_client;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "test_utils.h"
|
||||
#include "test_websocket_client.h"
|
||||
#include "test_web_request_factory.h"
|
||||
#include "test_http_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
|
|
@ -17,10 +18,10 @@ std::string remove_date_from_log_entry(const std::string &log_entry)
|
|||
return log_entry.substr(date_end_index + 1);
|
||||
}
|
||||
|
||||
std::shared_ptr<websocket_client> create_test_websocket_client(std::function<pplx::task<std::string>()> receive_function,
|
||||
std::function<pplx::task<void>(const std::string& msg)> send_function,
|
||||
std::function<pplx::task<void>(const std::string& url)> connect_function,
|
||||
std::function<pplx::task<void>()> close_function)
|
||||
std::shared_ptr<websocket_client> create_test_websocket_client(std::function<void(std::function<void(std::string, std::exception_ptr)>)> receive_function,
|
||||
std::function<void(const std::string& msg, std::function<void(std::exception_ptr)>)> send_function,
|
||||
std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> connect_function,
|
||||
std::function<void(std::function<void(std::exception_ptr)>)> close_function)
|
||||
{
|
||||
auto websocket_client = std::make_shared<test_websocket_client>();
|
||||
websocket_client->set_receive_function(receive_function);
|
||||
|
|
@ -45,6 +46,20 @@ std::unique_ptr<web_request_factory> create_test_web_request_factory()
|
|||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<http_client> create_test_http_client()
|
||||
{
|
||||
return std::make_unique<test_http_client>([](const std::string & url, http_request request)
|
||||
{
|
||||
auto response_body =
|
||||
url.find_first_of("/negotiate") != 0
|
||||
? "{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
|
||||
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }"
|
||||
: "";
|
||||
|
||||
return http_response{ 200, response_body };
|
||||
});
|
||||
}
|
||||
|
||||
std::string create_uri()
|
||||
{
|
||||
auto unit_test = ::testing::UnitTest::GetInstance();
|
||||
|
|
|
|||
|
|
@ -4,19 +4,97 @@
|
|||
#pragma once
|
||||
|
||||
#include "cpprest/details/basic_types.h"
|
||||
#include "websocket_client.h"
|
||||
#include "signalrclient/websocket_client.h"
|
||||
#include "web_request_factory.h"
|
||||
#include "signalrclient/http_client.h"
|
||||
#include <future>
|
||||
|
||||
std::string remove_date_from_log_entry(const std::string &log_entry);
|
||||
|
||||
std::shared_ptr<signalr::websocket_client> create_test_websocket_client(
|
||||
std::function<pplx::task<std::string>()> receive_function = [](){ return pplx::task_from_result<std::string>(""); },
|
||||
std::function<pplx::task<void>(const std::string& msg)> send_function = [](const std::string&){ return pplx::task_from_result(); },
|
||||
std::function<pplx::task<void>(const std::string& url)> connect_function = [](const std::string&){ return pplx::task_from_result(); },
|
||||
std::function<pplx::task<void>()> close_function = [](){ return pplx::task_from_result(); });
|
||||
std::function<void(std::function<void(std::string, std::exception_ptr)>)> receive_function = [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", nullptr); },
|
||||
std::function<void(const std::string& msg, std::function<void(std::exception_ptr)>)> send_function = [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(nullptr); },
|
||||
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);
|
||||
std::vector<std::string> filter_vector(const std::vector<std::string>& source, const std::string& string);
|
||||
std::string dump_vector(const std::vector<std::string>& source);
|
||||
|
||||
template <typename T>
|
||||
class manual_reset_event
|
||||
{
|
||||
public:
|
||||
void set(T value)
|
||||
{
|
||||
m_promise.set_value(value);
|
||||
}
|
||||
|
||||
void set_exception(std::exception exception)
|
||||
{
|
||||
m_promise.set_exception(std::make_exception_ptr(exception));
|
||||
}
|
||||
|
||||
void set_exception(std::exception_ptr exception)
|
||||
{
|
||||
m_promise.set_exception(exception);
|
||||
}
|
||||
|
||||
T get()
|
||||
{
|
||||
// TODO: timeout
|
||||
try
|
||||
{
|
||||
auto ret = m_promise.get_future().get();
|
||||
m_promise = std::promise<T>();
|
||||
return ret;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_promise = std::promise<T>();
|
||||
std::rethrow_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::promise<T> m_promise;
|
||||
};
|
||||
|
||||
template <>
|
||||
class manual_reset_event<void>
|
||||
{
|
||||
public:
|
||||
void set()
|
||||
{
|
||||
m_promise.set_value();
|
||||
}
|
||||
|
||||
void set_exception(std::exception exception)
|
||||
{
|
||||
m_promise.set_exception(std::make_exception_ptr(exception));
|
||||
}
|
||||
|
||||
void set_exception(std::exception_ptr exception)
|
||||
{
|
||||
m_promise.set_exception(exception);
|
||||
}
|
||||
|
||||
void get()
|
||||
{
|
||||
try
|
||||
{
|
||||
m_promise.get_future().get();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
m_promise = std::promise<void>();
|
||||
std::rethrow_exception(std::current_exception());
|
||||
}
|
||||
|
||||
m_promise = std::promise<void>();
|
||||
}
|
||||
private:
|
||||
std::promise<void> m_promise;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,51 +3,63 @@
|
|||
|
||||
#include "stdafx.h"
|
||||
#include "test_websocket_client.h"
|
||||
#include "pplx/pplxtasks.h"
|
||||
|
||||
test_websocket_client::test_websocket_client()
|
||||
: m_connect_function([](const std::string&){ return pplx::task_from_result(); }),
|
||||
m_send_function ([](const std::string msg){ return pplx::task_from_result(); }),
|
||||
m_receive_function([](){ return pplx::task_from_result<std::string>(""); }),
|
||||
m_close_function([](){ return pplx::task_from_result(); })
|
||||
|
||||
: m_connect_function([](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(nullptr); }),
|
||||
m_send_function([](const std::string msg, std::function<void(std::exception_ptr)> callback) { callback(nullptr); }),
|
||||
m_receive_function([](std::function<void(std::string, std::exception_ptr)> callback) { callback("", nullptr); }),
|
||||
m_close_function([](std::function<void(std::exception_ptr)> callback) { callback(nullptr); })
|
||||
{ }
|
||||
|
||||
pplx::task<void> test_websocket_client::connect(const std::string& url)
|
||||
void test_websocket_client::start(std::string url, transfer_format, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return m_connect_function(url);
|
||||
pplx::create_task([url, callback, this]()
|
||||
{
|
||||
m_connect_function(url, callback);
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<void> test_websocket_client::send(const std::string &msg)
|
||||
void test_websocket_client::stop(std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return m_send_function(msg);
|
||||
pplx::create_task([callback, this]()
|
||||
{
|
||||
m_close_function(callback);
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<std::string> test_websocket_client::receive()
|
||||
void test_websocket_client::send(std::string payload, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::create_task([this]() { return m_receive_function(); });
|
||||
pplx::create_task([payload, callback, this]()
|
||||
{
|
||||
m_send_function(payload, callback);
|
||||
});
|
||||
}
|
||||
|
||||
pplx::task<void> test_websocket_client::close()
|
||||
void test_websocket_client::receive(std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return m_close_function();
|
||||
pplx::create_task([callback, this]()
|
||||
{
|
||||
m_receive_function(callback);
|
||||
});
|
||||
}
|
||||
|
||||
void test_websocket_client::set_connect_function(std::function<pplx::task<void>(const std::string& url)> connect_function)
|
||||
void test_websocket_client::set_connect_function(std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> connect_function)
|
||||
{
|
||||
m_connect_function = connect_function;
|
||||
}
|
||||
|
||||
void test_websocket_client::set_send_function(std::function<pplx::task<void>(const std::string& msg)> send_function)
|
||||
void test_websocket_client::set_send_function(std::function<void(const std::string& msg, std::function<void(std::exception_ptr)>)> send_function)
|
||||
{
|
||||
m_send_function = send_function;
|
||||
}
|
||||
|
||||
void test_websocket_client::set_receive_function(std::function<pplx::task<std::string>()> receive_function)
|
||||
void test_websocket_client::set_receive_function(std::function<void(std::function<void(std::string, std::exception_ptr)>)> receive_function)
|
||||
{
|
||||
m_receive_function = receive_function;
|
||||
}
|
||||
|
||||
void test_websocket_client::set_close_function(std::function<pplx::task<void>()> close_function)
|
||||
void test_websocket_client::set_close_function(std::function<void(std::function<void(std::exception_ptr)>)> close_function)
|
||||
{
|
||||
m_close_function = close_function;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include "websocket_client.h"
|
||||
#include "signalrclient/websocket_client.h"
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
|
|
@ -13,28 +13,28 @@ class test_websocket_client : public websocket_client
|
|||
public:
|
||||
test_websocket_client();
|
||||
|
||||
pplx::task<void> connect(const std::string& url) override;
|
||||
void start(std::string url, transfer_format format, std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
pplx::task<void> send(const std::string& msg) override;
|
||||
void stop(std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
pplx::task<std::string> receive() override;
|
||||
void send(std::string payload, std::function<void(std::exception_ptr)> callback) override;
|
||||
|
||||
pplx::task<void> close() override;
|
||||
void receive(std::function<void(std::string, std::exception_ptr)> callback) override;
|
||||
|
||||
void set_connect_function(std::function<pplx::task<void>(const std::string& url)> connect_function);
|
||||
void set_connect_function(std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> connect_function);
|
||||
|
||||
void set_send_function(std::function<pplx::task<void>(const std::string& msg)> send_function);
|
||||
void set_send_function(std::function<void(const std::string& msg, std::function<void(std::exception_ptr)>)> send_function);
|
||||
|
||||
void set_receive_function(std::function<pplx::task<std::string>()> receive_function);
|
||||
void set_receive_function(std::function<void(std::function<void(std::string, std::exception_ptr)>)> receive_function);
|
||||
|
||||
void set_close_function(std::function<pplx::task<void>()> close_function);
|
||||
void set_close_function(std::function<void(std::function<void(std::exception_ptr)>)> close_function);
|
||||
|
||||
private:
|
||||
std::function<pplx::task<void>(const std::string& url)> m_connect_function;
|
||||
std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> m_connect_function;
|
||||
|
||||
std::function<pplx::task<void>(const std::string&)> m_send_function;
|
||||
std::function<void(const std::string&, std::function<void(std::exception_ptr)>)> m_send_function;
|
||||
|
||||
std::function<pplx::task<std::string>()> m_receive_function;
|
||||
std::function<void(std::function<void(std::string, std::exception_ptr)>)> m_receive_function;
|
||||
|
||||
std::function<pplx::task<void>()> m_close_function;
|
||||
std::function<void(std::function<void(std::exception_ptr)>)> m_close_function;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "test_websocket_client.h"
|
||||
#include "websocket_transport.h"
|
||||
#include "memory_log_writer.h"
|
||||
#include <future>
|
||||
|
||||
using namespace signalr;
|
||||
|
||||
|
|
@ -16,24 +17,29 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
|
|||
auto receive_called = std::make_shared<event>();
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
client->set_connect_function([&connect_called](const std::string&) -> pplx::task<void>
|
||||
client->set_connect_function([&connect_called](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
connect_called = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
client->set_receive_function([receive_called]()->pplx::task<std::string>
|
||||
client->set_receive_function([receive_called](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
receive_called->set();
|
||||
return pplx::task_from_result(std::string(""));
|
||||
callback("", nullptr);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::info),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::info));
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org/connect?param=42").get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org/connect?param=42", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(connect_called);
|
||||
ASSERT_FALSE(receive_called->wait(5000));
|
||||
|
|
@ -48,18 +54,22 @@ TEST(websocket_transport_connect, connect_connects_and_starts_receive_loop)
|
|||
TEST(websocket_transport_connect, connect_propagates_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_connect_function([](const std::string&)->pplx::task<void>
|
||||
client->set_connect_function([](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
try
|
||||
{
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
ASSERT_TRUE(false); // exception not thrown
|
||||
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.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
@ -70,21 +80,24 @@ TEST(websocket_transport_connect, connect_propagates_exceptions)
|
|||
TEST(websocket_transport_connect, connect_logs_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_connect_function([](const std::string&) -> pplx::task<void>
|
||||
client->set_connect_function([](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connecting failed"))));
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::errors),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, 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)
|
||||
{
|
||||
mre.set_exception(exception);
|
||||
});
|
||||
try
|
||||
{
|
||||
ws_transport->connect("ws://fakeuri.org").wait();
|
||||
mre.get();
|
||||
}
|
||||
catch (...)
|
||||
{ }
|
||||
catch (...) {}
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
|
|
@ -100,15 +113,23 @@ TEST(websocket_transport_connect, connect_logs_exceptions)
|
|||
TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_transport)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").wait();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
try
|
||||
{
|
||||
ws_transport->connect("ws://fakeuri.org").wait();
|
||||
ASSERT_TRUE(false); // exception not thrown
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr exception)
|
||||
{
|
||||
mre.set_exception(exception);
|
||||
});
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
|
|
@ -119,13 +140,26 @@ TEST(websocket_transport_connect, cannot_call_connect_on_already_connected_trans
|
|||
TEST(websocket_transport_connect, can_connect_after_disconnecting)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
ws_transport->disconnect().get();
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
// shouldn't throw or crash
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
}
|
||||
|
||||
TEST(websocket_transport_send, send_creates_and_sends_websocket_messages)
|
||||
|
|
@ -134,18 +168,26 @@ TEST(websocket_transport_send, send_creates_and_sends_websocket_messages)
|
|||
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
client->set_send_function([&send_called](const std::string&) -> pplx::task<void>
|
||||
client->set_send_function([&send_called](const std::string&, std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
send_called = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://url")
|
||||
.then([ws_transport](){ return ws_transport->send("ABC"); })
|
||||
.wait();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->send("ABC", [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(send_called);
|
||||
}
|
||||
|
|
@ -156,43 +198,67 @@ TEST(websocket_transport_disconnect, disconnect_closes_websocket)
|
|||
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
client->set_close_function([&close_called]() -> pplx::task<void>
|
||||
client->set_close_function([&close_called](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
close_called = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://url")
|
||||
.then([ws_transport]()
|
||||
{
|
||||
return ws_transport->disconnect();
|
||||
}).get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ASSERT_TRUE(close_called);
|
||||
}
|
||||
|
||||
TEST(websocket_transport_disconnect, disconnect_does_not_throw)
|
||||
TEST(websocket_transport_stop, propogates_exception_from_close)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
bool close_called = false;
|
||||
client->set_close_function([&close_called]() -> pplx::task<void>
|
||||
client->set_close_function([&close_called](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
close_called = true;
|
||||
return pplx::task_from_exception<void>(std::exception());
|
||||
callback(std::make_exception_ptr(std::exception()));
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://url")
|
||||
.then([ws_transport]()
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
return ws_transport->disconnect();
|
||||
}).get();
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr exception)
|
||||
{
|
||||
if (exception)
|
||||
{
|
||||
mre.set_exception(exception);
|
||||
}
|
||||
else
|
||||
{
|
||||
mre.set();
|
||||
}
|
||||
});
|
||||
try
|
||||
{
|
||||
mre.get();
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
catch (...) { }
|
||||
|
||||
ASSERT_TRUE(close_called);
|
||||
}
|
||||
|
|
@ -200,21 +266,27 @@ TEST(websocket_transport_disconnect, disconnect_does_not_throw)
|
|||
TEST(websocket_transport_disconnect, disconnect_logs_exceptions)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_close_function([]()->pplx::task<void>
|
||||
client->set_close_function([](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<void>(web::websockets::client::websocket_exception(_XPLATSTR("connection closing failed")));
|
||||
callback(std::make_exception_ptr(web::websockets::client::websocket_exception(_XPLATSTR("connection closing failed"))));
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::errors),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level::errors));
|
||||
|
||||
ws_transport->connect("ws://url")
|
||||
.then([ws_transport]()
|
||||
{
|
||||
return ws_transport->disconnect();
|
||||
}).get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
auto log_entries = std::dynamic_pointer_cast<memory_log_writer>(writer)->get_log_entries();
|
||||
|
||||
|
|
@ -241,34 +313,59 @@ TEST(websocket_transport_disconnect, receive_not_called_after_disconnect)
|
|||
// receive_task_tce is captured by reference since we assign it a new value after the first disconnect. This is
|
||||
// safe here because we are blocking on disconnect and therefore we won't get into a state where we would be using
|
||||
// an invalid reference because the tce went out of scope and was destroyed.
|
||||
client->set_close_function([&receive_task_tce]()
|
||||
client->set_close_function([&receive_task_tce](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
// unblock receive
|
||||
receive_task_tce.set(std::string(""));
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
int num_called = 0;
|
||||
|
||||
client->set_receive_function([&receive_task_tce, &receive_task_started_tce, &num_called]() -> pplx::task<std::string>
|
||||
client->set_receive_function([&receive_task_tce, &receive_task_started_tce, &num_called](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
num_called++;
|
||||
receive_task_started_tce.set();
|
||||
return pplx::create_task(receive_task_tce);
|
||||
pplx::create_task(receive_task_tce)
|
||||
.then([callback](pplx::task<std::string> prev)
|
||||
{
|
||||
prev.get();
|
||||
callback("", nullptr);
|
||||
});
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
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)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
pplx::create_task(receive_task_started_tce).get();
|
||||
ws_transport->disconnect().get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
receive_task_tce = pplx::task_completion_event<std::string>();
|
||||
receive_task_started_tce = pplx::task_completion_event<void>();
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
pplx::create_task(receive_task_started_tce).get();
|
||||
ws_transport->disconnect().get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ASSERT_EQ(2, num_called);
|
||||
}
|
||||
|
|
@ -279,16 +376,20 @@ TEST(websocket_transport_disconnect, disconnect_is_no_op_if_transport_not_starte
|
|||
|
||||
auto close_called = false;
|
||||
|
||||
client->set_close_function([&close_called]()
|
||||
client->set_close_function([&close_called](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
close_called = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->disconnect().get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ASSERT_FALSE(close_called);
|
||||
}
|
||||
|
|
@ -298,20 +399,29 @@ TEST(websocket_transport_disconnect, exceptions_from_outstanding_receive_task_ob
|
|||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
auto receive_event = std::make_shared<event>();
|
||||
client->set_receive_function([receive_event]()
|
||||
client->set_receive_function([receive_event](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::create_task([receive_event]()
|
||||
pplx::create_task([receive_event, callback]()
|
||||
{
|
||||
receive_event->wait();
|
||||
return pplx::task_from_exception<std::string>(std::runtime_error("exception from receive"));
|
||||
callback("", std::make_exception_ptr(std::runtime_error("exception from receive")));
|
||||
});
|
||||
});
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
ws_transport->disconnect().get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
ws_transport->stop([&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
// at this point the cancellation token that closes the receive loop is set to cancelled so
|
||||
// we can unblock the the receive task which throws an exception that should be observed otwherwise the test will crash
|
||||
|
|
@ -333,8 +443,8 @@ TEST(websocket_transport_receive_loop, receive_loop_logs_if_receive_task_cancele
|
|||
{
|
||||
receive_loop_logs_exception_runner(
|
||||
pplx::task_canceled("canceled"),
|
||||
"[info ] [websocket transport] receive task canceled.\n",
|
||||
trace_level::info);
|
||||
"[error ] [websocket transport] error receiving response from websocket: canceled\n",
|
||||
trace_level::errors);
|
||||
}
|
||||
|
||||
TEST(websocket_transport_receive_loop, receive_loop_logs_std_exception)
|
||||
|
|
@ -351,22 +461,24 @@ void receive_loop_logs_exception_runner(const T& e, const std::string& expected_
|
|||
event receive_event;
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
|
||||
client->set_receive_function([&receive_event, &e]()->pplx::task<std::string>
|
||||
client->set_receive_function([&receive_event, &e](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
callback("", std::make_exception_ptr(e));
|
||||
receive_event.set();
|
||||
return pplx::task_from_exception<std::string>(e);
|
||||
});
|
||||
|
||||
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(writer, trace_level));
|
||||
|
||||
ws_transport->connect("ws://url")
|
||||
.then([&receive_event]()
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://url", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
receive_event.wait();
|
||||
}).get();
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
receive_event.wait();
|
||||
|
||||
// this is race'y but there is nothing we can block on
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
|
@ -375,30 +487,36 @@ void receive_loop_logs_exception_runner(const T& e, const std::string& expected_
|
|||
|
||||
ASSERT_NE(std::find_if(log_entries.begin(), log_entries.end(),
|
||||
[&expected_message](std::string entry) { return remove_date_from_log_entry(entry) == expected_message; }),
|
||||
log_entries.end());
|
||||
log_entries.end()) << dump_vector(log_entries);
|
||||
}
|
||||
|
||||
TEST(websocket_transport_receive_loop, process_response_callback_called_when_message_received)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_receive_function([]() -> pplx::task<std::string>
|
||||
client->set_receive_function([](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_result(std::string("msg"));
|
||||
callback("msg", nullptr);
|
||||
});
|
||||
|
||||
auto process_response_event = std::make_shared<event>();
|
||||
auto msg = std::make_shared<std::string>();
|
||||
|
||||
auto process_response = [msg, process_response_event](const std::string& message)
|
||||
auto process_response = [msg, process_response_event](const std::string& message, std::exception_ptr exception)
|
||||
{
|
||||
ASSERT_FALSE(exception);
|
||||
*msg = message;
|
||||
process_response_event->set();
|
||||
};
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
process_response, [](const std::exception&){});
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
ws_transport->on_receive(process_response);
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
process_response_event->wait(1000);
|
||||
|
||||
|
|
@ -408,31 +526,43 @@ TEST(websocket_transport_receive_loop, process_response_callback_called_when_mes
|
|||
TEST(websocket_transport_receive_loop, error_callback_called_when_exception_thrown)
|
||||
{
|
||||
auto client = std::make_shared<test_websocket_client>();
|
||||
client->set_receive_function([]()
|
||||
client->set_receive_function([](std::function<void(std::string, std::exception_ptr)> callback)
|
||||
{
|
||||
return pplx::task_from_exception<std::string>(std::runtime_error("error"));
|
||||
callback("", std::make_exception_ptr(std::runtime_error("error")));
|
||||
});
|
||||
|
||||
auto close_invoked = std::make_shared<bool>(false);
|
||||
client->set_close_function([close_invoked]()
|
||||
client->set_close_function([close_invoked](std::function<void(std::exception_ptr)> callback)
|
||||
{
|
||||
*close_invoked = true;
|
||||
return pplx::task_from_result();
|
||||
callback(nullptr);
|
||||
});
|
||||
|
||||
auto error_event = std::make_shared<event>();
|
||||
auto exception_msg = std::make_shared<std::string>();
|
||||
|
||||
auto error_callback = [exception_msg, error_event](const std::exception& e)
|
||||
auto error_callback = [exception_msg, error_event](std::exception_ptr exception)
|
||||
{
|
||||
*exception_msg = e.what();
|
||||
try
|
||||
{
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
*exception_msg = e.what();
|
||||
}
|
||||
error_event->set();
|
||||
};
|
||||
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, error_callback);
|
||||
auto ws_transport = websocket_transport::create([&](){ return client; }, logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
ws_transport->on_close(error_callback);
|
||||
|
||||
ws_transport->connect("ws://fakeuri.org").get();
|
||||
auto mre = manual_reset_event<void>();
|
||||
ws_transport->start("ws://fakeuri.org", transfer_format::text, [&mre](std::exception_ptr)
|
||||
{
|
||||
mre.set();
|
||||
});
|
||||
mre.get();
|
||||
|
||||
error_event->wait(1000);
|
||||
|
||||
|
|
@ -444,8 +574,7 @@ TEST(websocket_transport_get_transport_type, get_transport_type_returns_websocke
|
|||
{
|
||||
auto ws_transport = websocket_transport::create(
|
||||
[](){ return std::make_shared<default_websocket_client>(); },
|
||||
logger(std::make_shared<trace_log_writer>(), trace_level::none),
|
||||
[](const std::string&){}, [](const std::exception&){});
|
||||
logger(std::make_shared<trace_log_writer>(), trace_level::none));
|
||||
|
||||
ASSERT_EQ(transport_type::websockets, ws_transport->get_transport_type());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue