@@ -21,22 +21,21 @@ namespace signalr
21
21
static void log (const logger& logger, trace_level level, const utility::string_t & entry);
22
22
}
23
23
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)
26
25
{
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>());
28
27
}
29
28
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)
32
31
{
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,
34
33
log_writer ? log_writer : std::make_shared<trace_log_writer>(), std::move (web_request_factory), std::move (transport_factory)));
35
34
}
36
35
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,
38
37
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),
40
39
m_transport (nullptr ), m_web_request_factory(std::move(web_request_factory)), m_transport_factory(std::move(transport_factory)),
41
40
m_message_received([](const utility::string_t &) noexcept {}), m_disconnected([]() noexcept {})
42
41
{ }
@@ -84,94 +83,138 @@ namespace signalr
84
83
m_message_id = m_groups_token = m_connection_id = _XPLATSTR (" " );
85
84
}
86
85
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
+
87
96
pplx::task_completion_event<void > start_tce;
88
97
89
98
auto weak_connection = weak_from_this ();
90
99
91
100
pplx::task_from_result ()
92
- .then ([weak_connection]()
101
+ .then ([weak_connection, url]()
102
+ {
103
+ auto connection = weak_connection.lock ();
104
+ if (!connection)
93
105
{
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 ())
96
126
{
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);
98
130
}
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 )
103
140
{
104
- auto connection = weak_connection.lock ();
105
- if (!connection)
141
+ if (availableTransport.transport == _XPLATSTR (" WebSockets" ))
106
142
{
107
- return pplx::task_from_exception<void >(_XPLATSTR (" connection no longer exists" ));
143
+ foundWebsockets = true ;
144
+ break ;
108
145
}
109
- connection-> m_connection_id = std::move (negotiation_response. connection_id );
146
+ }
110
147
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
+ }
112
152
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)
126
157
{
127
158
auto connection = weak_connection.lock ();
128
159
if (!connection)
129
160
{
130
161
return pplx::task_from_exception<void >(_XPLATSTR (" connection no longer exists" ));
131
162
}
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;
143
164
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))
148
166
{
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 ())));
161
170
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 );
166
172
}
167
173
168
174
return pplx::task_from_result ();
169
175
});
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
+ });
170
213
171
214
return pplx::create_task (start_tce);
172
215
}
173
216
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 )
175
218
{
176
219
auto connection = shared_from_this ();
177
220
@@ -247,18 +290,15 @@ namespace signalr
247
290
}
248
291
});
249
292
250
- return connection->send_connect_request (transport, connect_request_tce)
293
+ return connection->send_connect_request (transport, url, connect_request_tce)
251
294
.then ([transport](){ return pplx::task_from_result (transport); });
252
295
}
253
296
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)
255
298
{
256
299
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);
262
302
263
303
transport->connect (connect_url)
264
304
.then ([transport, connect_request_tce, logger](pplx::task<void > connect_task)
0 commit comments