Skip to content

Commit d0f42f3

Browse files
Negotiate version support (#16)
1 parent 0a217b4 commit d0f42f3

File tree

11 files changed

+205
-13
lines changed

11 files changed

+205
-13
lines changed

src/signalrclient/connection_impl.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ namespace signalr
136136
const auto& token = m_disconnect_cts;
137137

138138
negotiate::negotiate(*m_http_client, url, m_signalr_client_config,
139-
[callback, weak_connection, redirect_count, token, url](const negotiation_response& response, std::exception_ptr exception)
139+
[callback, weak_connection, redirect_count, token, url](negotiation_response&& response, std::exception_ptr exception)
140140
{
141141
auto connection = weak_connection.lock();
142142
if (!connection)
@@ -182,6 +182,7 @@ namespace signalr
182182
}
183183

184184
connection->m_connection_id = std::move(response.connectionId);
185+
connection->m_connection_token = std::move(response.connectionToken);
185186

186187
// TODO: fallback logic
187188

@@ -407,7 +408,7 @@ namespace signalr
407408
void connection_impl::send_connect_request(const std::shared_ptr<transport>& transport, const std::string& url, std::function<void(std::exception_ptr)> callback)
408409
{
409410
auto logger = m_logger;
410-
auto query_string = "id=" + m_connection_id;
411+
auto query_string = "id=" + m_connection_token;
411412
auto connect_url = url_builder::build_connect(url, transport->get_transport_type(), query_string);
412413

413414
transport->start(connect_url, transfer_format::text, [callback, logger](std::exception_ptr exception)

src/signalrclient/connection_impl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ namespace signalr
6161
std::mutex m_stop_lock;
6262
cancellation_token m_start_completed_event;
6363
std::string m_connection_id;
64+
std::string m_connection_token;
6465
std::unique_ptr<http_client> m_http_client;
6566

6667
connection_impl(const std::string& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,

src/signalrclient/negotiate.cpp

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ namespace signalr
1010
{
1111
namespace negotiate
1212
{
13+
const int negotiate_version = 1;
14+
1315
void negotiate(http_client& client, const std::string& base_url,
1416
const signalr_client_config& config,
15-
std::function<void(const negotiation_response&, std::exception_ptr)> callback) noexcept
17+
std::function<void(negotiation_response&&, std::exception_ptr)> callback) noexcept
1618
{
1719
std::string negotiate_url;
1820
try
1921
{
2022
negotiate_url = url_builder::build_negotiate(base_url);
23+
negotiate_url = url_builder::add_query_string(negotiate_url, "negotiateVersion=" + std::to_string(negotiate_version));
2124
}
2225
catch (...)
2326
{
@@ -58,15 +61,31 @@ namespace signalr
5861
if (negotiation_response_json.has_field(_XPLATSTR("error")))
5962
{
6063
response.error = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("error")].as_string());
61-
callback(response, nullptr);
64+
callback(std::move(response), nullptr);
6265
return;
6366
}
6467

68+
int server_negotiate_version = 0;
69+
if (negotiation_response_json.has_field(_XPLATSTR("negotiateVersion")))
70+
{
71+
server_negotiate_version = negotiation_response_json[_XPLATSTR("negotiateVersion")].as_integer();
72+
}
73+
6574
if (negotiation_response_json.has_field(_XPLATSTR("connectionId")))
6675
{
6776
response.connectionId = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("connectionId")].as_string());
6877
}
6978

79+
if (negotiation_response_json.has_field(_XPLATSTR("connectionToken")))
80+
{
81+
response.connectionToken = utility::conversions::to_utf8string(negotiation_response_json[_XPLATSTR("connectionToken")].as_string());
82+
}
83+
84+
if (server_negotiate_version <= 0)
85+
{
86+
response.connectionToken = response.connectionId;
87+
}
88+
7089
if (negotiation_response_json.has_field(_XPLATSTR("availableTransports")))
7190
{
7291
for (auto transportData : negotiation_response_json[_XPLATSTR("availableTransports")].as_array())
@@ -100,7 +119,7 @@ namespace signalr
100119
return;
101120
}
102121

103-
callback(response, nullptr);
122+
callback(std::move(response), nullptr);
104123
}
105124
catch (...)
106125
{

src/signalrclient/negotiate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ namespace signalr
1313
{
1414
void negotiate(http_client& client, const std::string& base_url,
1515
const signalr_client_config& signalr_client_config,
16-
std::function<void(const negotiation_response&, std::exception_ptr)> callback) noexcept;
16+
std::function<void(negotiation_response&&, std::exception_ptr)> callback) noexcept;
1717
}
1818
}

src/signalrclient/negotiation_response.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace signalr
1414
struct negotiation_response
1515
{
1616
std::string connectionId;
17+
std::string connectionToken;
1718
std::vector<available_transport> availableTransports;
1819
std::string url;
1920
std::string accessToken;

src/signalrclient/url_builder.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,11 @@ namespace signalr
4949
auto builder = build_uri(base_url, "", query_string);
5050
return convert_to_websocket_url(builder, transport).to_string();
5151
}
52+
53+
std::string add_query_string(const std::string& base_url, const std::string& query_string)
54+
{
55+
uri_builder builder(base_url);
56+
return builder.append_query(query_string).to_string();
57+
}
5258
}
5359
}

src/signalrclient/url_builder.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ namespace signalr
1111
{
1212
std::string build_negotiate(const std::string& base_url);
1313
std::string build_connect(const std::string& base_url, transport_type transport, const std::string& query_string);
14+
std::string add_query_string(const std::string& base_url, const std::string& query_string);
1415
}
1516
}

test/signalrclienttests/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11

22

3-
set (SOURCES
3+
set (SOURCES
44
callback_manager_tests.cpp
55
case_insensitive_comparison_utils_tests.cpp
66
connection_impl_tests.cpp
77
hub_connection_impl_tests.cpp
88
hub_exception_tests.cpp
99
logger_tests.cpp
1010
memory_log_writer.cpp
11+
negotiate_tests.cpp
1112
signalrclienttests.cpp
1213
stdafx.cpp
1314
test_http_client.cpp
@@ -20,7 +21,7 @@ set (SOURCES
2021

2122
include_directories(
2223
../../src/signalrclient)
23-
24+
2425
add_executable (signalrclienttests ${SOURCES})
2526

2627
target_link_libraries(signalrclienttests gtest signalrclient ${CPPREST_LIB})

test/signalrclienttests/connection_impl_tests.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -891,6 +891,92 @@ TEST(connection_impl_start, negotiate_redirect_uses_own_query_string)
891891
ASSERT_EQ("customQuery=1&id=f7707523-307d-4cba-9abf-3eef701241e8", query_string);
892892
}
893893

894+
TEST(connection_impl_start, negotiate_with_negotiateVersion_uses_connectionToken)
895+
{
896+
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
897+
std::string query_string;
898+
899+
auto websocket_client = create_test_websocket_client(
900+
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
901+
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
902+
/* connect function */[&query_string](const std::string& url, std::function<void(std::exception_ptr)> callback)
903+
{
904+
query_string = url.substr(url.find('?') + 1);
905+
callback(std::make_exception_ptr(std::runtime_error("connecting failed")));
906+
});
907+
908+
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
909+
{
910+
std::string response_body = "";
911+
if (url.find("/negotiate") != std::string::npos)
912+
{
913+
response_body = "{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
914+
"\"connectionToken\":\"1122334455\", \"negotiateVersion\": 1, "
915+
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }";
916+
}
917+
918+
return http_response{ 200, response_body };
919+
});
920+
921+
auto connection = connection_impl::create(create_uri(), trace_level::errors, writer, std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
922+
923+
auto mre = manual_reset_event<void>();
924+
connection->start([&mre](std::exception_ptr exception)
925+
{
926+
mre.set(exception);
927+
});
928+
929+
try
930+
{
931+
mre.get();
932+
ASSERT_TRUE(false);
933+
}
934+
catch (const std::runtime_error & ex)
935+
{
936+
ASSERT_STREQ("connecting failed", ex.what());
937+
}
938+
939+
ASSERT_EQ("id=1122334455", query_string);
940+
}
941+
942+
TEST(connection_impl_start, correct_connection_id_returned_with_negotiateVersion_one)
943+
{
944+
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());
945+
946+
auto websocket_client = create_test_websocket_client(
947+
/* receive function */ [](std::function<void(std::string, std::exception_ptr)> callback) { callback("", std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
948+
/* send function */ [](const std::string&, std::function<void(std::exception_ptr)> callback) { callback(std::make_exception_ptr(std::runtime_error("should not be invoked"))); },
949+
/* connect function */[](const std::string& url, std::function<void(std::exception_ptr)> callback)
950+
{
951+
callback(nullptr);
952+
});
953+
954+
auto http_client = std::make_unique<test_http_client>([](const std::string& url, http_request)
955+
{
956+
std::string response_body = "";
957+
if (url.find("/negotiate") != std::string::npos)
958+
{
959+
response_body = "{\"connectionId\" : \"f7707523-307d-4cba-9abf-3eef701241e8\", "
960+
"\"connectionToken\":\"1122334455\", \"negotiateVersion\": 1, "
961+
"\"availableTransports\" : [ { \"transport\": \"WebSockets\", \"transferFormats\": [ \"Text\", \"Binary\" ] } ] }";
962+
}
963+
964+
return http_response{ 200, response_body };
965+
});
966+
967+
auto connection = connection_impl::create(create_uri(), trace_level::errors, writer, std::move(http_client), std::make_unique<test_transport_factory>(websocket_client));
968+
969+
auto mre = manual_reset_event<void>();
970+
connection->start([&mre](std::exception_ptr exception)
971+
{
972+
mre.set(exception);
973+
});
974+
975+
mre.get();
976+
977+
ASSERT_EQ("f7707523-307d-4cba-9abf-3eef701241e8", connection->get_connection_id());
978+
}
979+
894980
TEST(connection_impl_start, start_fails_if_connect_request_times_out)
895981
{
896982
std::shared_ptr<log_writer> writer(std::make_shared<memory_log_writer>());

test/signalrclienttests/hub_connection_impl_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ TEST(url, negotiate_appended_to_url)
4949
}
5050
catch (const std::exception&) {}
5151

52-
ASSERT_EQ("http://fakeuri/negotiate", requested_url);
52+
ASSERT_EQ("http://fakeuri/negotiate?negotiateVersion=1", requested_url);
5353
}
5454
}
5555

0 commit comments

Comments
 (0)