1111#include " message_type.h"
1212#include " handshake_protocol.h"
1313#include " signalrclient/websocket_client.h"
14- #include " messagepack_hub_protocol .h"
14+ #include " signalr_default_scheduler .h"
1515
1616namespace signalr
1717{
@@ -65,6 +65,7 @@ namespace signalr
6565 {
6666 // start may be waiting on the handshake response so we complete it here, this no-ops if already set
6767 connection->m_handshakeTask ->set (std::make_exception_ptr (signalr_exception (" connection closed while handshake was in progress." )));
68+ connection->m_disconnect_cts ->cancel ();
6869
6970 connection->m_callback_manager .clear (" connection was stopped before invocation result was received" );
7071
@@ -107,6 +108,7 @@ namespace signalr
107108
108109 m_connection->set_client_config (m_signalr_client_config);
109110 m_handshakeTask = std::make_shared<completion_event>();
111+ m_disconnect_cts = std::make_shared<cancellation_token>();
110112 m_handshakeReceived = false ;
111113 std::weak_ptr<hub_connection_impl> weak_connection = shared_from_this ();
112114 m_connection->start ([weak_connection, callback](std::exception_ptr start_exception)
@@ -139,10 +141,13 @@ namespace signalr
139141 return ;
140142 }
141143
142- auto handshake_request = handshake::write_handshake (connection->m_protocol );
144+ std::shared_ptr<std::mutex> handshake_request_lock = std::make_shared<std::mutex>();
145+ std::shared_ptr<bool > handshake_request_done = std::make_shared<bool >();
143146
144- connection-> m_connection -> send (handshake_request, connection-> m_protocol -> transfer_format (), [weak_connection, callback](std::exception_ptr exception)
147+ auto handle_handshake = [weak_connection, handshake_request_done, handshake_request_lock, callback](std::exception_ptr exception, bool fromSend )
145148 {
149+ assert (fromSend ? *handshake_request_done : true );
150+
146151 auto connection = weak_connection.lock ();
147152 if (!connection)
148153 {
@@ -151,25 +156,103 @@ namespace signalr
151156 return ;
152157 }
153158
154- if (exception)
155159 {
156- callback (exception);
157- return ;
160+ std::lock_guard<std::mutex> lock (*handshake_request_lock);
161+ // connection.send will be waiting on the handshake task which has been set by the caller already
162+ if (!fromSend && *handshake_request_done == true )
163+ {
164+ return ;
165+ }
166+ *handshake_request_done = true ;
158167 }
159168
160169 try
161170 {
162- connection->m_handshakeTask ->get ();
163- callback (nullptr );
171+ if (exception == nullptr )
172+ {
173+ connection->m_handshakeTask ->get ();
174+ callback (nullptr );
175+ }
164176 }
165177 catch (...)
166178 {
167- auto handshake_exception = std::current_exception ();
168- connection->m_connection ->stop ([callback, handshake_exception](std::exception_ptr)
179+ exception = std::current_exception ();
180+ }
181+
182+ if (exception != nullptr )
183+ {
184+ connection->m_connection ->stop ([callback, exception](std::exception_ptr)
185+ {
186+ callback (exception);
187+ }, exception);
188+ }
189+ };
190+
191+ auto handshake_request = handshake::write_handshake (connection->m_protocol );
192+ auto handshake_task = connection->m_handshakeTask ;
193+ auto handshake_timeout = connection->m_signalr_client_config .get_handshake_timeout ();
194+
195+ connection->m_disconnect_cts ->register_callback ([handle_handshake, handshake_request_lock, handshake_request_done]()
196+ {
197+ {
198+ std::lock_guard<std::mutex> lock (*handshake_request_lock);
199+ // no op after connection.send returned, m_handshakeTask should be set before m_disconnect_cts is canceled
200+ if (*handshake_request_done == true )
201+ {
202+ return ;
203+ }
204+ }
205+
206+ // if the request isn't completed then no one is waiting on the handshake task
207+ // so we need to run the callback here instead of relying on connection.send completing
208+ // handshake_request_done is set in handle_handshake, don't set it here
209+ handle_handshake (nullptr , false );
210+ });
211+
212+ timer (connection->m_signalr_client_config .get_scheduler (),
213+ [handle_handshake, handshake_task, handshake_timeout, handshake_request_lock](std::chrono::milliseconds duration)
214+ {
215+ {
216+ std::lock_guard<std::mutex> lock (*handshake_request_lock);
217+
218+ // if the task is set then connection.send is either already waiting on the handshake or has completed,
219+ // or stop has been called and will be handling the callback
220+ if (handshake_task->is_set ())
221+ {
222+ return true ;
223+ }
224+
225+ if (duration < handshake_timeout)
226+ {
227+ return false ;
228+ }
229+ }
230+
231+ auto exception = std::make_exception_ptr (signalr_exception (" timed out waiting for the server to respond to the handshake message." ));
232+ // unblocks connection.send if it's waiting on the task
233+ handshake_task->set (exception);
234+
235+ handle_handshake (exception, false );
236+ return true ;
237+ });
238+
239+ connection->m_connection ->send (handshake_request, connection->m_protocol ->transfer_format (),
240+ [handle_handshake, handshake_request_done, handshake_request_lock](std::exception_ptr exception)
241+ {
242+ {
243+ std::lock_guard<std::mutex> lock (*handshake_request_lock);
244+ if (*handshake_request_done == true )
169245 {
170- callback (handshake_exception);
171- }, nullptr );
246+ // callback ran from timer or cancellation token, nothing to do here
247+ return ;
248+ }
249+
250+ // indicates that the handshake timer doesn't need to call the callback, it just needs to set the timeout exception
251+ // handle_handshake will be waiting on the handshake completion (error or success) to call the callback
252+ *handshake_request_done = true ;
172253 }
254+
255+ handle_handshake (exception, true );
173256 });
174257 });
175258 }
@@ -205,6 +288,8 @@ namespace signalr
205288 return ;
206289 }
207290
291+ assert (connection->get_connection_state () == connection_state::disconnected);
292+
208293 std::vector<std::function<void (std::exception_ptr)>> callbacks;
209294
210295 {
@@ -251,6 +336,7 @@ namespace signalr
251336 if (found != obj.end ())
252337 {
253338 m_handshakeTask->set (std::make_exception_ptr (signalr_exception (std::string (" Received unexpected message while waiting for the handshake response." ))));
339+ return ;
254340 }
255341
256342 m_handshakeReceived = true ;
0 commit comments