Skip to content

Commit ea4ad5b

Browse files
authored
Skip negotiation (#33)
1 parent 4450cf1 commit ea4ad5b

File tree

9 files changed

+132
-73
lines changed

9 files changed

+132
-73
lines changed

include/signalrclient/hub_connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ namespace signalr
5656

5757
explicit hub_connection(const std::string& url, trace_level trace_level = trace_level::all,
5858
std::shared_ptr<log_writer> log_writer = nullptr, std::shared_ptr<http_client> http_client = nullptr,
59-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory = nullptr);
59+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory = nullptr, bool skip_negotiation = false);
6060

6161
std::shared_ptr<hub_connection_impl> m_pImpl;
6262
};

include/signalrclient/hub_connection_builder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ namespace signalr
3333

3434
SIGNALRCLIENT_API hub_connection_builder& with_http_client(std::shared_ptr<http_client> http_client);
3535

36+
SIGNALRCLIENT_API hub_connection_builder& skip_negotiation(bool skip = true);
37+
3638
SIGNALRCLIENT_API hub_connection build();
3739
private:
3840
hub_connection_builder(const std::string& url);
@@ -42,5 +44,6 @@ namespace signalr
4244
trace_level m_log_level;
4345
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> m_websocket_factory;
4446
std::shared_ptr<http_client> m_http_client;
47+
bool m_skip_negotiation = false;
4548
};
4649
}

src/signalrclient/connection_impl.cpp

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,20 @@ namespace signalr
2222
{
2323
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
2424
{
25-
return connection_impl::create(url, trace_level, log_writer, nullptr, nullptr);
25+
return connection_impl::create(url, trace_level, log_writer, nullptr, nullptr, false);
2626
}
2727

2828
std::shared_ptr<connection_impl> connection_impl::create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
29-
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory)
29+
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
3030
{
3131
return std::shared_ptr<connection_impl>(new connection_impl(url, trace_level,
32-
log_writer ? log_writer : std::make_shared<trace_log_writer>(), http_client, websocket_factory));
32+
log_writer ? log_writer : std::make_shared<trace_log_writer>(), http_client, websocket_factory, skip_negotiation));
3333
}
3434

3535
connection_impl::connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
36-
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory)
36+
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory, const bool skip_negotiation)
3737
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr),
38-
m_transport_factory(std::move(transport_factory)), m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
38+
m_transport_factory(std::move(transport_factory)), m_skip_negotiation(skip_negotiation), m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
3939
{
4040
if (http_client != nullptr)
4141
{
@@ -50,8 +50,8 @@ namespace signalr
5050
}
5151

5252
connection_impl::connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
53-
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory)
54-
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr),
53+
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
54+
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level), m_transport(nullptr), m_skip_negotiation(skip_negotiation),
5555
m_message_received([](const std::string&) noexcept {}), m_disconnected([]() noexcept {})
5656
{
5757
if (http_client != nullptr)
@@ -156,8 +156,68 @@ namespace signalr
156156
std::weak_ptr<connection_impl> weak_connection = shared_from_this();
157157
const auto& token = m_disconnect_cts;
158158

159+
const auto transport_started = [weak_connection, callback, token](std::shared_ptr<transport> transport, std::exception_ptr exception)
160+
{
161+
auto connection = weak_connection.lock();
162+
if (!connection)
163+
{
164+
callback(std::make_exception_ptr(signalr_exception("connection no longer exists")));
165+
return;
166+
}
167+
168+
try
169+
{
170+
if (exception != nullptr)
171+
{
172+
std::rethrow_exception(exception);
173+
}
174+
token->throw_if_cancellation_requested();
175+
}
176+
catch (const std::exception& e)
177+
{
178+
if (token->is_canceled())
179+
{
180+
connection->m_logger.log(trace_level::info,
181+
"starting the connection has been canceled.");
182+
}
183+
else
184+
{
185+
connection->m_logger.log(trace_level::errors,
186+
std::string("connection could not be started due to: ")
187+
.append(e.what()));
188+
}
189+
190+
connection->m_transport = nullptr;
191+
connection->change_state(connection_state::disconnected);
192+
connection->m_start_completed_event.cancel();
193+
callback(std::current_exception());
194+
return;
195+
}
196+
197+
connection->m_transport = transport;
198+
199+
if (!connection->change_state(connection_state::connecting, connection_state::connected))
200+
{
201+
connection->m_logger.log(trace_level::errors,
202+
std::string("internal error - transition from an unexpected state. expected state: connecting, actual state: ")
203+
.append(translate_connection_state(connection->get_connection_state())));
204+
205+
assert(false);
206+
}
207+
208+
connection->m_start_completed_event.cancel();
209+
callback(nullptr);
210+
};
211+
212+
if (m_skip_negotiation)
213+
{
214+
// TODO: check that the websockets transport is explicitly selected
215+
216+
return start_transport(url, transport_started);
217+
}
218+
159219
negotiate::negotiate(*m_http_client, url, m_signalr_client_config,
160-
[callback, weak_connection, redirect_count, token, url](negotiation_response&& response, std::exception_ptr exception)
220+
[callback, weak_connection, redirect_count, token, url, transport_started](negotiation_response&& response, std::exception_ptr exception)
161221
{
162222
auto connection = weak_connection.lock();
163223
if (!connection)
@@ -236,58 +296,7 @@ namespace signalr
236296
return;
237297
}
238298

239-
connection->start_transport(url, [weak_connection, callback, token](std::shared_ptr<transport> transport, std::exception_ptr exception)
240-
{
241-
auto connection = weak_connection.lock();
242-
if (!connection)
243-
{
244-
callback(std::make_exception_ptr(signalr_exception("connection no longer exists")));
245-
return;
246-
}
247-
248-
try
249-
{
250-
if (exception != nullptr)
251-
{
252-
std::rethrow_exception(exception);
253-
}
254-
token->throw_if_cancellation_requested();
255-
}
256-
catch (const std::exception& e)
257-
{
258-
if (token->is_canceled())
259-
{
260-
connection->m_logger.log(trace_level::info,
261-
"starting the connection has been canceled.");
262-
}
263-
else
264-
{
265-
connection->m_logger.log(trace_level::errors,
266-
std::string("connection could not be started due to: ")
267-
.append(e.what()));
268-
}
269-
270-
connection->m_transport = nullptr;
271-
connection->change_state(connection_state::disconnected);
272-
connection->m_start_completed_event.cancel();
273-
callback(std::current_exception());
274-
return;
275-
}
276-
277-
connection->m_transport = transport;
278-
279-
if (!connection->change_state(connection_state::connecting, connection_state::connected))
280-
{
281-
connection->m_logger.log(trace_level::errors,
282-
std::string("internal error - transition from an unexpected state. expected state: connecting, actual state: ")
283-
.append(translate_connection_state(connection->get_connection_state())));
284-
285-
assert(false);
286-
}
287-
288-
connection->m_start_completed_event.cancel();
289-
callback(nullptr);
290-
});
299+
connection->start_transport(url, transport_started);
291300
});
292301
}
293302

src/signalrclient/connection_impl.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace signalr
3030
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer);
3131

3232
static std::shared_ptr<connection_impl> create(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
33-
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory);
33+
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation = false);
3434

3535
connection_impl(const connection_impl&) = delete;
3636

@@ -55,6 +55,7 @@ namespace signalr
5555
logger m_logger;
5656
std::shared_ptr<transport> m_transport;
5757
std::unique_ptr<transport_factory> m_transport_factory;
58+
bool m_skip_negotiation;
5859

5960
std::function<void(std::string&&)> m_message_received;
6061
std::function<void()> m_disconnected;
@@ -68,10 +69,10 @@ namespace signalr
6869
std::shared_ptr<http_client> m_http_client;
6970

7071
connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
71-
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory);
72+
std::unique_ptr<http_client> http_client, std::unique_ptr<transport_factory> transport_factory, bool skip_negotiation);
7273

7374
connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
74-
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory);
75+
std::shared_ptr<http_client> http_client, std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation);
7576

7677
void start_transport(const std::string& url, std::function<void(std::shared_ptr<transport>, std::exception_ptr)> callback);
7778
void send_connect_request(const std::shared_ptr<transport>& transport,

src/signalrclient/hub_connection.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ namespace signalr
1313
{
1414
hub_connection::hub_connection(const std::string& url,
1515
trace_level trace_level, std::shared_ptr<log_writer> log_writer, std::shared_ptr<http_client> http_client,
16-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory)
17-
: m_pImpl(hub_connection_impl::create(url, trace_level, log_writer, http_client, websocket_factory))
16+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
17+
: m_pImpl(hub_connection_impl::create(url, trace_level, log_writer, http_client, websocket_factory, skip_negotiation))
1818
{}
1919

2020
hub_connection::hub_connection(hub_connection&& rhs) noexcept

src/signalrclient/hub_connection_builder.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ namespace signalr
7171
return *this;
7272
}
7373

74+
hub_connection_builder& hub_connection_builder::skip_negotiation(const bool skip)
75+
{
76+
m_skip_negotiation = skip;
77+
78+
return *this;
79+
}
80+
7481
hub_connection hub_connection_builder::build()
7582
{
7683
#ifndef USE_CPPRESTSDK
@@ -85,6 +92,6 @@ namespace signalr
8592
}
8693
#endif
8794

88-
return hub_connection(m_url, m_log_level, m_logger, m_http_client, m_websocket_factory);
95+
return hub_connection(m_url, m_log_level, m_logger, m_http_client, m_websocket_factory, m_skip_negotiation);
8996
}
9097
}

src/signalrclient/hub_connection_impl.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ namespace signalr
2424

2525
std::shared_ptr<hub_connection_impl> hub_connection_impl::create(const std::string& url, trace_level trace_level,
2626
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
27-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory)
27+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
2828
{
29-
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, trace_level, log_writer, http_client, websocket_factory));
29+
auto connection = std::shared_ptr<hub_connection_impl>(new hub_connection_impl(url, trace_level, log_writer, http_client, websocket_factory, skip_negotiation));
3030

3131
connection->initialize();
3232

@@ -35,9 +35,9 @@ namespace signalr
3535

3636
hub_connection_impl::hub_connection_impl(const std::string& url, trace_level trace_level,
3737
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
38-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory)
38+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, const bool skip_negotiation)
3939
: m_connection(connection_impl::create(url, trace_level, log_writer,
40-
http_client, websocket_factory)), m_logger(log_writer, trace_level),
40+
http_client, websocket_factory, skip_negotiation)), m_logger(log_writer, trace_level),
4141
m_callback_manager(signalr::value(std::map<std::string, signalr::value> { { std::string("error"), std::string("connection went out of scope before invocation result was received") } })),
4242
m_handshakeReceived(false), m_disconnected([]() noexcept {}), m_protocol(std::make_shared<json_hub_protocol>())
4343
{}

src/signalrclient/hub_connection_impl.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace signalr
2626
public:
2727
static std::shared_ptr<hub_connection_impl> create(const std::string& url, trace_level trace_level,
2828
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
29-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory);
29+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory, bool skip_negotiation = false);
3030

3131
hub_connection_impl(const hub_connection_impl&) = delete;
3232
hub_connection_impl& operator=(const hub_connection_impl&) = delete;
@@ -48,7 +48,8 @@ namespace signalr
4848
private:
4949
hub_connection_impl(const std::string& url, trace_level trace_level,
5050
const std::shared_ptr<log_writer>& log_writer, std::shared_ptr<http_client> http_client,
51-
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory);
51+
std::function<std::shared_ptr<websocket_client>(const signalr_client_config&)> websocket_factory,
52+
bool skip_negotiation);
5253

5354
std::shared_ptr<connection_impl> m_connection;
5455
logger m_logger;

test/signalrclienttests/connection_impl_tests.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -961,6 +961,44 @@ TEST(connection_impl_start, start_fails_if_connect_request_times_out)
961961
wait_connect_mre.get();
962962
}
963963

964+
TEST(connection_impl_start, negotiate_can_be_skipped)
965+
{
966+
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
967+
968+
bool negotiate_called = false;
969+
970+
auto http_client = std::unique_ptr<test_http_client>(new test_http_client([&negotiate_called](const std::string& url, http_request request)
971+
{
972+
negotiate_called = true;
973+
974+
auto response_body =
975+
url.find_first_of("/negotiate") != 0
976+
? "{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
977+
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }"
978+
: "";
979+
980+
return http_response{ 200, response_body };
981+
}));
982+
983+
auto websocket_client = std::make_shared<test_websocket_client>();
984+
985+
auto connection =
986+
connection_impl::create(create_uri(), trace_level::messages, writer,
987+
std::move(http_client), [websocket_client](const signalr_client_config&) { return websocket_client; }, true);
988+
989+
auto mre = manual_reset_event<void>();
990+
connection->start([&mre](std::exception_ptr exception)
991+
{
992+
mre.set(exception);
993+
});
994+
995+
mre.get();
996+
997+
ASSERT_TRUE(connection->get_connection_state() == connection_state::connected);
998+
ASSERT_TRUE(connection->get_connection_id().empty());
999+
ASSERT_FALSE(negotiate_called);
1000+
}
1001+
9641002
TEST(connection_impl_process_response, process_response_logs_messages)
9651003
{
9661004
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());

0 commit comments

Comments
 (0)