Skip to content

Commit 4d28ca4

Browse files
author
pfeatherstone
committed
TLS client WS
1 parent 6d42af3 commit 4d28ca4

File tree

4 files changed

+99
-15
lines changed

4 files changed

+99
-15
lines changed

examples/client_ws_awaitable.cpp

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
#include <boost/asio/connect.hpp>
55
#include <boost/asio/detached.hpp>
66
#include <boost/asio/io_context.hpp>
7-
#include <boost/asio/strand.hpp>
87
#include <boost/asio/ip/tcp.hpp>
8+
#include <boost/asio/ssl/context.hpp>
9+
#include <boost/asio/ssl/stream.hpp>
10+
#include <boost/asio/strand.hpp>
911
#include <boost/asio/version.hpp>
1012
#include <http_async.h>
1113
#include "extra/CLI11.hpp"
@@ -20,6 +22,7 @@ using boost::asio::detached;
2022
using boost::asio::ip::tcp;
2123
using boost::asio::make_strand;
2224
using tcp_socket = boost::asio::basic_stream_socket<tcp, boost::asio::strand<boost::asio::io_context::executor_type>>;
25+
using tls_socket = boost::asio::ssl::stream<tcp_socket>;
2326
using awaitable_strand = boost::asio::awaitable<void, boost::asio::strand<boost::asio::io_context::executor_type>>;
2427
using namespace std::chrono_literals;
2528

@@ -56,24 +59,63 @@ awaitable_strand ws_session(std::string host, uint16_t port, std::string msg)
5659
}
5760
}
5861

62+
awaitable_strand ws_ssl_session(std::string host, uint16_t port, std::string msg)
63+
{
64+
try
65+
{
66+
// SSL
67+
boost::asio::ssl::context ssl(boost::asio::ssl::context::tlsv12_client);
68+
ssl.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx) {return true;});
69+
ssl.set_verify_mode(boost::asio::ssl::verify_peer);
70+
71+
// Connect
72+
tls_socket sock(tcp_socket(co_await boost::asio::this_coro::executor), ssl);
73+
tcp::resolver resolver(sock.get_executor());
74+
std::vector<char> buf(begin(msg), end(msg));
75+
size_t ret{};
76+
77+
// Async IO
78+
co_await boost::asio::async_connect(sock.next_layer(), co_await resolver.async_resolve(host, std::to_string(port)), boost::asio::cancel_after(5s));
79+
co_await sock.async_handshake(boost::asio::ssl::stream_base::client);
80+
co_await http::async_ws_handshake(sock, host, "/ws");
81+
ret = co_await http::async_ws_write(sock, buf, true, false);
82+
ret = co_await http::async_ws_read(sock, buf, false);
83+
co_await http::async_ws_close(sock, http::ws_going_away, false);
84+
co_await sock.async_shutdown();
85+
86+
// Print echo
87+
printf("Server echoed back\n\"%.*s\"\n", (int)buf.size(), buf.data());
88+
}
89+
catch(const boost::system::system_error& e)
90+
{
91+
if (e.code() != boost::asio::error::eof)
92+
fprintf(stderr, "[HTTP session] %s\n", e.what());
93+
}
94+
}
95+
5996
int main(int argc, char* argv[])
6097
{
6198
std::string host;
6299
uint16_t port;
63100
std::string msg;
101+
bool use_tls;
64102
CLI::App app{"WebSocket echo client"};
65103
try{
66104
app.add_option("--host", host, "Host or IP address of WebSocket server")->required();
67105
app.add_option("--port", port, "Port of WebSocket server")->required();
68106
app.add_option("--msg", msg, "Message to be echoed back by server")->required();
107+
app.add_flag("--tls", use_tls, "Use transport over TLS");
69108
app.parse(argc, argv);
70109
} catch (const CLI::ParseError& e) {return app.exit(e);}
71110

72111
boost::asio::io_context ioc{1};
73112

74113
try
75114
{
76-
co_spawn(make_strand(ioc), ws_session(host, port, msg), detached);
115+
if (use_tls)
116+
co_spawn(make_strand(ioc), ws_ssl_session(host, port, msg), detached);
117+
else
118+
co_spawn(make_strand(ioc), ws_session(host, port, msg), detached);
77119
ioc.run();
78120
}
79121
catch (const std::exception& e)

examples/client_ws_coro.cpp

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include <boost/asio/detached.hpp>
66
#include <boost/asio/io_context.hpp>
77
#include <boost/asio/spawn.hpp>
8+
#include <boost/asio/ssl/context.hpp>
9+
#include <boost/asio/ssl/stream.hpp>
810
#include <boost/asio/strand.hpp>
911
#include <boost/asio/ip/tcp.hpp>
1012
#include <boost/asio/version.hpp>
@@ -22,6 +24,7 @@ using boost::asio::detached;
2224
using boost::asio::ip::tcp;
2325
using boost::asio::make_strand;
2426
using tcp_socket = boost::asio::basic_stream_socket<tcp, boost::asio::strand<boost::asio::io_context::executor_type>>;
27+
using tls_socket = boost::asio::ssl::stream<tcp_socket>;
2528
using yield_context_strand = boost::asio::basic_yield_context<boost::asio::strand<boost::asio::io_context::executor_type>>;
2629
using namespace std::chrono_literals;
2730

@@ -58,24 +61,63 @@ void ws_session(std::string host, uint16_t port, std::string msg, yield_context_
5861
}
5962
}
6063

64+
void ws_ssl_session(std::string host, uint16_t port, std::string msg, yield_context_strand yield)
65+
{
66+
try
67+
{
68+
// SSL
69+
boost::asio::ssl::context ssl(boost::asio::ssl::context::tlsv12_client);
70+
ssl.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx) {return true;});
71+
ssl.set_verify_mode(boost::asio::ssl::verify_peer);
72+
73+
// Connect
74+
tls_socket sock(tcp_socket(yield.get_executor()), ssl);
75+
tcp::resolver resolver(sock.get_executor());
76+
std::vector<char> buf(begin(msg), end(msg));
77+
size_t ret{};
78+
79+
// Async IO
80+
boost::asio::async_connect(sock.next_layer(), resolver.async_resolve(host, std::to_string(port), yield), boost::asio::cancel_after(5s, yield));
81+
sock.async_handshake(boost::asio::ssl::stream_base::client, yield);
82+
http::async_ws_handshake(sock, host, "/ws", yield);
83+
ret = http::async_ws_write(sock, buf, true, false, yield);
84+
ret = http::async_ws_read(sock, buf, false, yield);
85+
http::async_ws_close(sock, http::ws_going_away, false, yield);
86+
sock.async_shutdown(yield);
87+
88+
// Print echo
89+
printf("Server echoed back\n\"%.*s\"\n", (int)buf.size(), buf.data());
90+
}
91+
catch(const boost::system::system_error& e)
92+
{
93+
if (e.code() != boost::asio::error::eof)
94+
fprintf(stderr, "[HTTP session] %s\n", e.what());
95+
}
96+
}
97+
6198
int main(int argc, char* argv[])
6299
{
63100
std::string host;
64101
uint16_t port;
65102
std::string msg;
103+
bool use_tls;
66104
CLI::App app{"WebSocket echo client"};
67105
try{
68106
app.add_option("--host", host, "Host or IP address of WebSocket server")->required();
69107
app.add_option("--port", port, "Port of WebSocket server")->required();
70108
app.add_option("--msg", msg, "Message to be echoed back by server")->required();
109+
app.add_flag("--tls", use_tls, "Use transport over TLS");
71110
app.parse(argc, argv);
72111
} catch (const CLI::ParseError& e) {return app.exit(e);}
73112

74113
boost::asio::io_context ioc{1};
75114

76115
try
77116
{
78-
boost::asio::spawn(make_strand(ioc), bind_front(ws_session, host, port, msg), detached);
117+
if (use_tls)
118+
boost::asio::spawn(make_strand(ioc), bind_front(ws_ssl_session, host, port, msg), detached);
119+
else
120+
boost::asio::spawn(make_strand(ioc), bind_front(ws_session, host, port, msg), detached);
79121
ioc.run();
80122
}
81123
catch (const std::exception& e)

examples/server.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@ awaitable listen (
402402
ssl = std::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv13_server);
403403
ssl->set_options(
404404
boost::asio::ssl::context::default_workarounds |
405-
boost::asio::ssl::context::no_sslv2 |
405+
boost::asio::ssl::context::no_sslv2 |
406406
boost::asio::ssl::context::single_dh_use |
407407
boost::asio::ssl::context::verify_peer
408408
);

src/http_async.h

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,14 @@ namespace http
8080
//----------------------------------------------------------------------------------------------------------------
8181

8282
template <
83-
class AsyncReadWriteStream,
84-
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(boost::system::error_code)) CompletionToken = boost::asio::default_completion_token_t<typename AsyncReadWriteStream::executor_type>
83+
class AsyncStream,
84+
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(boost::system::error_code)) CompletionToken = boost::asio::default_completion_token_t<typename AsyncStream::executor_type>
8585
>
8686
auto async_ws_handshake (
87-
AsyncReadWriteStream& sock,
88-
std::string host,
89-
std::string uri,
90-
CompletionToken&& token = boost::asio::default_completion_token_t<typename AsyncReadWriteStream::executor_type>()
87+
AsyncStream& sock,
88+
std::string host,
89+
std::string uri,
90+
CompletionToken&& token = boost::asio::default_completion_token_t<typename AsyncStream::executor_type>()
9191
);
9292

9393
//----------------------------------------------------------------------------------------------------------------
@@ -559,14 +559,14 @@ namespace http
559559
}
560560

561561
template <
562-
class AsyncReadWriteStream,
562+
class AsyncStream,
563563
BOOST_ASIO_COMPLETION_TOKEN_FOR(void(boost::system::error_code)) CompletionToken
564564
>
565565
inline auto async_ws_handshake (
566-
AsyncReadWriteStream& sock,
567-
std::string host,
568-
std::string uri,
569-
CompletionToken&& token
566+
AsyncStream& sock,
567+
std::string host,
568+
std::string uri,
569+
CompletionToken&& token
570570
)
571571
{
572572
return boost::asio::async_compose<CompletionToken, void(boost::system::error_code)> (

0 commit comments

Comments
 (0)