Skip to content

Commit e455c2c

Browse files
Add negotiation response support (#7675)
1 parent 6fea3a6 commit e455c2c

25 files changed

+697
-186
lines changed

src/SignalR/clients/cpp/include/signalrclient/connection.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ namespace signalr
2121
public:
2222
typedef std::function<void __cdecl(const utility::string_t&)> message_received_handler;
2323

24-
SIGNALRCLIENT_API explicit connection(const utility::string_t& url, const utility::string_t& query_string = _XPLATSTR(""),
25-
trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = nullptr);
24+
SIGNALRCLIENT_API explicit connection(const utility::string_t& url, trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = nullptr);
2625

2726
SIGNALRCLIENT_API ~connection();
2827

src/SignalR/clients/cpp/include/signalrclient/hub_connection.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ namespace signalr
2222
public:
2323
typedef std::function<void __cdecl (const web::json::value&)> method_invoked_handler;
2424

25-
SIGNALRCLIENT_API explicit hub_connection(const utility::string_t& url, const utility::string_t& query_string = _XPLATSTR(""),
26-
trace_level trace_level = trace_level::all, std::shared_ptr<log_writer> log_writer = nullptr);
25+
SIGNALRCLIENT_API explicit hub_connection(const utility::string_t& url, trace_level trace_level = trace_level::all,
26+
std::shared_ptr<log_writer> log_writer = nullptr);
2727

2828
SIGNALRCLIENT_API ~hub_connection();
2929

src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ void send_message(signalr::hub_connection& connection, const utility::string_t&
4141

4242
void chat(const utility::string_t& name)
4343
{
44-
signalr::hub_connection connection(U("http://localhost:5000/default"), U(""), signalr::trace_level::all, std::make_shared<logger>());
44+
signalr::hub_connection connection(U("http://localhost:5000/default"), signalr::trace_level::all, std::make_shared<logger>());
4545
connection.on(U("Send"), [](const web::json::value& m)
4646
{
4747
ucout << std::endl << m.at(0).as_string() << /*U(" wrote:") << m.at(1).as_string() <<*/ std::endl << U("Enter your message: ");

src/SignalR/clients/cpp/samples/HubConnectionSample/HubConnectionSample.vcxproj

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="..\..\Build\SignalRClient.Build.Settings" />
44
<ItemGroup Label="ProjectConfigurations">
@@ -36,7 +36,6 @@
3636
</PropertyGroup>
3737
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
3838
<ImportGroup Label="ExtensionSettings">
39-
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
4039
<Import Project="..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets" Condition="Exists('..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" />
4140
</ImportGroup>
4241
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
@@ -110,10 +109,10 @@
110109
<PropertyGroup>
111110
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
112111
</PropertyGroup>
113-
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
114112
<Error Condition="!Exists('..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.2.9.1\build\native\cpprestsdk.v140.windesktop.msvcstl.dyn.rt-dyn.targets'))" />
115113
</Target>
116114
<Target Name="AfterBuild">
117-
<Exec Command="copy /y &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).dll&quot; &quot;$(SolutionDir)$(Configuration)\$(SignalrClientTargetName).dll&quot;" />
115+
<Exec Command="copy /y &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).dll&quot; &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).dll&quot;" />
116+
<Exec Command="copy /y &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\dll\$(SignalrClientTargetName).pdb&quot; &quot;$(SolutionDir)bin\Desktop\$(Platform)\$(Configuration)\$(SignalrClientTargetName).pdb&quot;" />
118117
</Target>
119-
</Project>
118+
</Project>

src/SignalR/clients/cpp/src/signalrclient/connection.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88

99
namespace signalr
1010
{
11-
connection::connection(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
12-
: m_pImpl(connection_impl::create(url, query_string, trace_level, std::move(log_writer)))
11+
connection::connection(const utility::string_t& url, trace_level trace_level, std::shared_ptr<log_writer> log_writer)
12+
: m_pImpl(connection_impl::create(url, trace_level, std::move(log_writer)))
1313
{}
1414

1515
// Do NOT remove this destructor. Letting the compiler generate and inline the default dtor may lead to

src/SignalR/clients/cpp/src/signalrclient/connection_impl.cpp

Lines changed: 113 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,21 @@ namespace signalr
2121
static void log(const logger& logger, trace_level level, const utility::string_t& entry);
2222
}
2323

24-
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& query_string,
25-
trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
24+
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer)
2625
{
27-
return connection_impl::create(url, query_string, trace_level, log_writer, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
26+
return connection_impl::create(url, trace_level, log_writer, std::make_unique<web_request_factory>(), std::make_unique<transport_factory>());
2827
}
2928

30-
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level,
31-
const std::shared_ptr<log_writer>& log_writer, std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
29+
std::shared_ptr<connection_impl> connection_impl::create(const utility::string_t& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
30+
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
3231
{
33-
return std::shared_ptr<connection_impl>(new connection_impl(url, query_string, trace_level,
32+
return std::shared_ptr<connection_impl>(new connection_impl(url, trace_level,
3433
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move(web_request_factory), std::move(transport_factory)));
3534
}
3635

37-
connection_impl::connection_impl(const utility::string_t& url, const utility::string_t& query_string, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
36+
connection_impl::connection_impl(const utility::string_t& url, trace_level trace_level, const std::shared_ptr<log_writer>& log_writer,
3837
std::unique_ptr<web_request_factory> web_request_factory, std::unique_ptr<transport_factory> transport_factory)
39-
: m_base_url(url), m_query_string(query_string), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level),
38+
: m_base_url(url), m_connection_state(connection_state::disconnected), m_logger(log_writer, trace_level),
4039
m_transport(nullptr), m_web_request_factory(std::move(web_request_factory)), m_transport_factory(std::move(transport_factory)),
4140
m_message_received([](const utility::string_t&) noexcept {}), m_disconnected([]() noexcept {})
4241
{ }
@@ -84,94 +83,138 @@ namespace signalr
8483
m_message_id = m_groups_token = m_connection_id = _XPLATSTR("");
8584
}
8685

86+
return start_negotiate(m_base_url, 0);
87+
}
88+
89+
pplx::task<void> connection_impl::start_negotiate(const web::uri& url, int redirect_count)
90+
{
91+
if (redirect_count >= MAX_NEGOTIATE_REDIRECTS)
92+
{
93+
return pplx::task_from_exception<void>(signalr_exception(_XPLATSTR("Negotiate redirection limit exceeded.")));
94+
}
95+
8796
pplx::task_completion_event<void> start_tce;
8897

8998
auto weak_connection = weak_from_this();
9099

91100
pplx::task_from_result()
92-
.then([weak_connection]()
101+
.then([weak_connection, url]()
102+
{
103+
auto connection = weak_connection.lock();
104+
if (!connection)
93105
{
94-
auto connection = weak_connection.lock();
95-
if (!connection)
106+
return pplx::task_from_exception<negotiation_response>(_XPLATSTR("connection no longer exists"));
107+
}
108+
return request_sender::negotiate(*connection->m_web_request_factory, url, connection->m_signalr_client_config);
109+
}, m_disconnect_cts.get_token())
110+
.then([weak_connection, start_tce, redirect_count, url](negotiation_response negotiation_response)
111+
{
112+
auto connection = weak_connection.lock();
113+
if (!connection)
114+
{
115+
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
116+
}
117+
118+
if (!negotiation_response.error.empty())
119+
{
120+
return pplx::task_from_exception<void>(signalr_exception(negotiation_response.error));
121+
}
122+
123+
if (!negotiation_response.url.empty())
124+
{
125+
if (!negotiation_response.accessToken.empty())
96126
{
97-
return pplx::task_from_exception<negotiation_response>(_XPLATSTR("connection no longer exists"));
127+
auto headers = connection->m_signalr_client_config.get_http_headers();
128+
headers[_XPLATSTR("Authorization")] = _XPLATSTR("Bearer ") + negotiation_response.accessToken;
129+
connection->m_signalr_client_config.set_http_headers(headers);
98130
}
99-
return request_sender::negotiate(*connection->m_web_request_factory, connection->m_base_url,
100-
connection->m_query_string, connection->m_signalr_client_config);
101-
}, m_disconnect_cts.get_token())
102-
.then([weak_connection](negotiation_response negotiation_response)
131+
return connection->start_negotiate(negotiation_response.url, redirect_count + 1);
132+
}
133+
134+
connection->m_connection_id = std::move(negotiation_response.connectionId);
135+
136+
// TODO: fallback logic
137+
138+
bool foundWebsockets = false;
139+
for (auto availableTransport : negotiation_response.availableTransports)
103140
{
104-
auto connection = weak_connection.lock();
105-
if (!connection)
141+
if (availableTransport.transport == _XPLATSTR("WebSockets"))
106142
{
107-
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
143+
foundWebsockets = true;
144+
break;
108145
}
109-
connection->m_connection_id = std::move(negotiation_response.connection_id);
146+
}
110147

111-
// TODO: check available transports
148+
if (!foundWebsockets)
149+
{
150+
return pplx::task_from_exception<void>(signalr_exception(_XPLATSTR("The server does not support WebSockets which is currently the only transport supported by this client.")));
151+
}
112152

113-
return connection->start_transport()
114-
.then([weak_connection](std::shared_ptr<transport> transport)
115-
{
116-
auto connection = weak_connection.lock();
117-
if (!connection)
118-
{
119-
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
120-
}
121-
connection->m_transport = transport;
122-
return pplx::task_from_result();
123-
});
124-
}, m_disconnect_cts.get_token())
125-
.then([start_tce, weak_connection](pplx::task<void> previous_task)
153+
// TODO: use transfer format
154+
155+
return connection->start_transport(url)
156+
.then([weak_connection, start_tce](std::shared_ptr<transport> transport)
126157
{
127158
auto connection = weak_connection.lock();
128159
if (!connection)
129160
{
130161
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
131162
}
132-
try
133-
{
134-
previous_task.get();
135-
if (!connection->change_state(connection_state::connecting, connection_state::connected))
136-
{
137-
connection->m_logger.log(trace_level::errors,
138-
utility::string_t(_XPLATSTR("internal error - transition from an unexpected state. expected state: connecting, actual state: "))
139-
.append(translate_connection_state(connection->get_connection_state())));
140-
141-
_ASSERTE(false);
142-
}
163+
connection->m_transport = transport;
143164

144-
connection->m_start_completed_event.set();
145-
start_tce.set();
146-
}
147-
catch (const std::exception &e)
165+
if (!connection->change_state(connection_state::connecting, connection_state::connected))
148166
{
149-
auto task_canceled_exception = dynamic_cast<const pplx::task_canceled *>(&e);
150-
if (task_canceled_exception)
151-
{
152-
connection->m_logger.log(trace_level::info,
153-
_XPLATSTR("starting the connection has been canceled."));
154-
}
155-
else
156-
{
157-
connection->m_logger.log(trace_level::errors,
158-
utility::string_t(_XPLATSTR("connection could not be started due to: "))
159-
.append(utility::conversions::to_string_t(e.what())));
160-
}
167+
connection->m_logger.log(trace_level::errors,
168+
utility::string_t(_XPLATSTR("internal error - transition from an unexpected state. expected state: connecting, actual state: "))
169+
.append(translate_connection_state(connection->get_connection_state())));
161170

162-
connection->m_transport = nullptr;
163-
connection->change_state(connection_state::disconnected);
164-
connection->m_start_completed_event.set();
165-
start_tce.set_exception(std::current_exception());
171+
_ASSERTE(false);
166172
}
167173

168174
return pplx::task_from_result();
169175
});
176+
}, m_disconnect_cts.get_token())
177+
.then([start_tce, weak_connection](pplx::task<void> previous_task)
178+
{
179+
auto connection = weak_connection.lock();
180+
if (!connection)
181+
{
182+
return pplx::task_from_exception<void>(_XPLATSTR("connection no longer exists"));
183+
}
184+
try
185+
{
186+
previous_task.get();
187+
connection->m_start_completed_event.set();
188+
start_tce.set();
189+
}
190+
catch (const std::exception & e)
191+
{
192+
auto task_canceled_exception = dynamic_cast<const pplx::task_canceled*>(&e);
193+
if (task_canceled_exception)
194+
{
195+
connection->m_logger.log(trace_level::info,
196+
_XPLATSTR("starting the connection has been canceled."));
197+
}
198+
else
199+
{
200+
connection->m_logger.log(trace_level::errors,
201+
utility::string_t(_XPLATSTR("connection could not be started due to: "))
202+
.append(utility::conversions::to_string_t(e.what())));
203+
}
204+
205+
connection->m_transport = nullptr;
206+
connection->change_state(connection_state::disconnected);
207+
connection->m_start_completed_event.set();
208+
start_tce.set_exception(std::current_exception());
209+
}
210+
211+
return pplx::task_from_result();
212+
});
170213

171214
return pplx::create_task(start_tce);
172215
}
173216

174-
pplx::task<std::shared_ptr<transport>> connection_impl::start_transport()
217+
pplx::task<std::shared_ptr<transport>> connection_impl::start_transport(const web::uri& url)
175218
{
176219
auto connection = shared_from_this();
177220

@@ -247,18 +290,15 @@ namespace signalr
247290
}
248291
});
249292

250-
return connection->send_connect_request(transport, connect_request_tce)
293+
return connection->send_connect_request(transport, url, connect_request_tce)
251294
.then([transport](){ return pplx::task_from_result(transport); });
252295
}
253296

254-
pplx::task<void> connection_impl::send_connect_request(const std::shared_ptr<transport>& transport, const pplx::task_completion_event<void>& connect_request_tce)
297+
pplx::task<void> connection_impl::send_connect_request(const std::shared_ptr<transport>& transport, const web::uri& url, const pplx::task_completion_event<void>& connect_request_tce)
255298
{
256299
auto logger = m_logger;
257-
auto query_string = m_query_string;
258-
if (!query_string.empty())
259-
query_string.append(_XPLATSTR("&"));
260-
query_string.append(_XPLATSTR("id=")).append(m_connection_id);
261-
auto connect_url = url_builder::build_connect(m_base_url, transport->get_transport_type(), query_string);
300+
auto query_string = _XPLATSTR("id=" + m_connection_id);
301+
auto connect_url = url_builder::build_connect(url, transport->get_transport_type(), query_string);
262302

263303
transport->connect(connect_url)
264304
.then([transport, connect_request_tce, logger](pplx::task<void> connect_task)

0 commit comments

Comments
 (0)