Skip to content

Commit ba8e9b2

Browse files
committed
Fixing a potential AV issue with websocket winrt client if the task returned from close wasn't waited on before the destructor executes.
1 parent c752c64 commit ba8e9b2

File tree

2 files changed

+17
-21
lines changed

2 files changed

+17
-21
lines changed

Release/src/websockets/client/ws_client_winrt.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
8585
public:
8686
winrt_callback_client(websocket_client_config config) :
8787
websocket_client_callback_impl(std::move(config)),
88+
m_connected(false),
8889
m_num_sends(0)
8990
{
9091
m_msg_websocket = ref new MessageWebSocket();
@@ -144,23 +145,26 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
144145

145146
~winrt_callback_client()
146147
{
147-
// Locally copy the task completion event since there is a PPL bug
148-
// that the set method accesses internal state in the event and the websocket
149-
// client could be destroyed.
150-
auto local_close_tce = m_close_tce;
151-
local_close_tce.set();
148+
// Only call close if successfully connected.
149+
if (m_connected)
150+
{
151+
// Users should have already called close and wait on the returned task
152+
// before destroying the client. In case they didn't we call close and wait for
153+
// it to complete. It is safe to call MessageWebSocket::Close multiple times and
154+
// concurrently, it has safe guards in place to only execute once.
155+
close().wait();
156+
}
152157
}
153158

154159
pplx::task<void> connect()
155160
{
161+
_ASSERTE(!m_connected);
156162
const auto &proxy = m_config.proxy();
157163
if(!proxy.is_default())
158164
{
159165
return pplx::task_from_exception<void>(websocket_exception("Only a default proxy server is supported."));
160166
}
161167

162-
163-
164168
const auto &proxy_cred = proxy.credentials();
165169
if(proxy_cred.is_set())
166170
{
@@ -187,6 +191,7 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
187191
websocket_exception exc(e->HResult, build_error_msg(e, "ConnectAsync"));
188192
return pplx::task_from_exception<void>(exc);
189193
}
194+
m_connected = true;
190195
return pplx::task_from_result();
191196
});
192197
}
@@ -410,6 +415,9 @@ class winrt_callback_client : public websocket_client_callback_impl, public std:
410415

411416
pplx::task_completion_event<void> m_close_tce;
412417

418+
// Tracks whether or not the websocket client successfully connected to the server.
419+
std::atomic<bool> m_connected;
420+
413421
// External callback for handling received and close event
414422
std::function<void(websocket_incoming_message)> m_external_message_handler;
415423
std::function<void(websocket_close_status, const utility::string_t&, const std::error_code&)> m_external_close_handler;

Release/tests/functional/websockets/client/error_tests.cpp

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -143,24 +143,12 @@ TEST_FIXTURE(uri_address, try_receive_after_server_initiated_close)
143143
}
144144

145145
// Destroy the client without closing it explicitly
146-
// CodePlex 319 fails on VS2013.
147-
#if !defined(_MSC_VER) || _MSC_VER >= 1900
148146
TEST_FIXTURE(uri_address, destroy_without_close)
149147
{
150148
test_websocket_server server;
151-
pplx::task<websocket_incoming_message> t;
152-
153-
{
154-
websocket_client client;
155-
156-
client.connect(m_uri).wait();
157-
158-
t = client.receive();
159-
}
160-
161-
VERIFY_THROWS(t.wait(), websocket_exception);
149+
websocket_client client;
150+
client.connect(m_uri).wait();
162151
}
163-
#endif
164152

165153
// Destroy the callback client without closing it explicitly
166154
TEST_FIXTURE(uri_address, destroy_without_close_callback_client)

0 commit comments

Comments
 (0)