Skip to content

Commit 06e9700

Browse files
committed
Improved error reporting for websocketpp based implementation. Added tests for invalid server certificates with websocket client.
1 parent 1930a3e commit 06e9700

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

Release/include/cpprest/ws_client.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,14 @@ class websocket_exception : public std::exception
277277
}
278278

279279
/// <summary>
280-
/// Destroys the <c>websocket_exception</c> object.
280+
/// Creates a <c>websocket_exception</c> from a error code and string message to use as the what() argument.
281+
/// <param name="code">Error code.</param>
282+
/// <param name="whatArg">Message to use in what() string.</param>
281283
/// </summary>
282-
~websocket_exception() _noexcept {}
284+
websocket_exception(std::error_code code, std::string whatArg) :
285+
m_errorCode(std::move(code)),
286+
m_msg(std::move(whatArg))
287+
{}
283288

284289
/// <summary>
285290
/// Gets a string identifying the cause of the exception.

Release/src/websockets/client/ws_client.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#undef ntohll
5050
#undef htonll
5151
#endif
52+
#define _WEBSOCKETPP_CPP11_SYSTEM_ERROR_
5253
#include <websocketpp/config/asio_client.hpp>
5354
#include <websocketpp/config/asio_no_tls_client.hpp>
5455
#include <websocketpp/client.hpp>
@@ -90,6 +91,17 @@ namespace client
9091
namespace details
9192
{
9293

94+
// Utility function to build up error string based on error code and location.
95+
static std::string build_error_msg(const std::error_code &ec, const std::string &location)
96+
{
97+
std::string msg(location);
98+
msg.append(": ");
99+
msg.append(std::to_string(ec.value()));
100+
msg.append(": ");
101+
msg.append(ec.message());
102+
return msg;
103+
}
104+
93105
static utility::string_t g_subProtocolHeader(_XPLATSTR("Sec-WebSocket-Protocol"));
94106

95107
class wspp_client : public _websocket_client_impl, public std::enable_shared_from_this<wspp_client>
@@ -151,6 +163,7 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
151163
catch (...) {}
152164
break;
153165
}
166+
154167
// We have released the lock on all paths here
155168
m_service.stop();
156169
if (m_thread.joinable())
@@ -225,13 +238,17 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
225238
m_connect_tce.set();
226239
});
227240

228-
client.set_fail_handler([this](websocketpp::connection_hdl)
241+
client.set_fail_handler([this](websocketpp::connection_hdl con_hdl)
229242
{
230243
_ASSERTE(m_state == CONNECTING);
244+
auto &client = m_client->client<WebsocketConfigType>();
245+
const auto &ec = client.get_con_from_hdl(con_hdl)->get_ec();
246+
websocket_exception exc(ec, build_error_msg(ec, "set_fail_handler"));
247+
231248
std::lock_guard<std::mutex> lock(m_receive_queue_lock);
232-
close_pending_tasks_with_error();
249+
close_pending_tasks_with_error(exc);
233250
m_state = CLOSED;
234-
m_connect_tce.set_exception(websocket_exception("Connection attempt failed."));
251+
m_connect_tce.set_exception(exc);
235252
});
236253

237254
client.set_message_handler([this](websocketpp::connection_hdl, const websocketpp::config::asio_client::message_type::ptr &msg)
@@ -278,11 +295,15 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
278295
}
279296
});
280297

281-
client.set_close_handler([this](websocketpp::connection_hdl)
298+
client.set_close_handler([this](websocketpp::connection_hdl con_hdl)
282299
{
283-
std::unique_lock<std::mutex> lock(m_receive_queue_lock);
284300
_ASSERTE(m_state != CLOSED);
285-
close_pending_tasks_with_error();
301+
auto &client = m_client->client<WebsocketConfigType>();
302+
const auto &ec = client.get_con_from_hdl(con_hdl)->get_ec();
303+
websocket_exception exc(ec, build_error_msg(ec, "set_close_handler"));
304+
305+
std::lock_guard<std::mutex> lock(m_receive_queue_lock);
306+
close_pending_tasks_with_error(exc);
286307
m_close_tce.set();
287308
m_state = CLOSED;
288309
});
@@ -294,7 +315,7 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
294315
m_con = con;
295316
if (ec.value() != 0)
296317
{
297-
return pplx::task_from_exception<void>(websocket_exception(ec.message()));
318+
return pplx::task_from_exception<void>(websocket_exception(ec, build_error_msg(ec, "get_connection")));
298319
}
299320

300321
// Add any request headers specified by the user.
@@ -316,7 +337,7 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
316337
con->add_subprotocol(utility::conversions::to_utf8string(value), ec);
317338
if (ec.value())
318339
{
319-
return pplx::task_from_exception<void>(websocket_exception(ec.message()));
340+
return pplx::task_from_exception<void>(websocket_exception(ec, build_error_msg(ec, "add_subprotocol")));
320341
}
321342
}
322343
}
@@ -328,12 +349,6 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
328349
{
329350
m_service.run();
330351
_ASSERTE(m_state == CLOSED);
331-
{
332-
std::unique_lock<std::mutex> lock(m_receive_queue_lock);
333-
close_pending_tasks_with_error();
334-
}
335-
_ASSERTE(m_state == CLOSED);
336-
return 0;
337352
});
338353

339354
return pplx::create_task(m_connect_tce);
@@ -534,10 +549,10 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
534549
try
535550
{
536551
// Catch exceptions from previous tasks, if any and convert it to websocket exception.
537-
auto ec = previousTask.get();
552+
const auto &ec = previousTask.get();
538553
if (ec.value() != 0)
539554
{
540-
eptr = std::make_exception_ptr(websocket_exception(ec.message()));
555+
eptr = std::make_exception_ptr(websocket_exception(ec, build_error_msg(ec, "sending message")));
541556
}
542557
}
543558
catch (...)
@@ -612,14 +627,14 @@ class wspp_client : public _websocket_client_impl, public std::enable_shared_fro
612627
}
613628

614629
// Note: must be called while m_receive_queue_lock is locked.
615-
void close_pending_tasks_with_error()
630+
void close_pending_tasks_with_error(const websocket_exception &exc)
616631
{
617632
while (!m_receive_task_queue.empty())
618633
{
619634
// There are tasks waiting to receive a message, signal them
620635
auto tce = m_receive_task_queue.front();
621636
m_receive_task_queue.pop();
622-
tce.set_exception(std::make_exception_ptr(websocket_exception("Websocket connection has been closed.")));
637+
tce.set_exception(std::make_exception_ptr(exc));
623638
}
624639
}
625640

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ TEST_FIXTURE(uri_address, auth_with_credentials, "Ignore", "245")
8686
}
8787
#endif
8888

89-
TEST_FIXTURE(uri_address, ssl_test)
89+
TEST(ssl_test)
9090
{
9191
websocket_client client;
9292
client.connect(U("wss://echo.websocket.org/")).wait();
@@ -109,6 +109,40 @@ TEST_FIXTURE(uri_address, ssl_test)
109109
client.close().wait();
110110
}
111111

112+
// These tests are specific to our websocketpp based implementation.
113+
#if !defined(__cplusplus_winrt)
114+
115+
void handshake_error_test_impl(const ::utility::string_t &host)
116+
{
117+
websocket_client client;
118+
try
119+
{
120+
client.connect(host).wait();
121+
VERIFY_IS_TRUE(false);
122+
}
123+
catch (const websocket_exception &e)
124+
{
125+
VERIFY_ARE_EQUAL("TLS handshake failed", e.error_code().message());
126+
}
127+
}
128+
129+
TEST(self_signed_cert)
130+
{
131+
handshake_error_test_impl(U("wss://www.pcwebshop.co.uk/"));
132+
}
133+
134+
TEST(hostname_mismatch)
135+
{
136+
handshake_error_test_impl(U("wss://swordsoftruth.com/"));
137+
}
138+
139+
TEST(cert_expired)
140+
{
141+
handshake_error_test_impl(U("wss://tv.eurosport.com/"));
142+
}
143+
144+
#endif
145+
112146
} // SUITE(authentication_tests)
113147

114148
}}}}

0 commit comments

Comments
 (0)