-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathclient_ws_awaitable.cpp
More file actions
127 lines (113 loc) · 5.18 KB
/
client_ws_awaitable.cpp
File metadata and controls
127 lines (113 loc) · 5.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <chrono>
#include <boost/asio/cancel_after.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/context.hpp>
#include <boost/asio/ssl/stream.hpp>
#include <boost/asio/strand.hpp>
#include <boost/asio/version.hpp>
#include <http_async.h>
#include "extra/CLI11.hpp"
//----------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------
// Typedefs
//----------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------
using boost::asio::detached;
using boost::asio::ip::tcp;
using boost::asio::make_strand;
using tcp_socket = boost::asio::basic_stream_socket<tcp, boost::asio::strand<boost::asio::io_context::executor_type>>;
using tls_socket = boost::asio::ssl::stream<tcp_socket>;
using http_socket = http::stream<tcp_socket>;
using https_socket = http::stream<tls_socket>;
using awaitable_strand = boost::asio::awaitable<void, boost::asio::strand<boost::asio::io_context::executor_type>>;
using namespace std::chrono_literals;
//----------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------
// WS session
//----------------------------------------------------------------------------------------------------------------
//----------------------------------------------------------------------------------------------------------------
awaitable_strand read_write_one(auto& sock, std::string_view host, std::string& msg)
{
constexpr bool is_text{false};
co_await http::async_ws_handshake(sock, host, "/ws");
co_await http::async_ws_write(sock, boost::asio::buffer(msg), is_text);
co_await http::async_ws_read(sock, msg);
co_await http::async_ws_close(sock, http::ws_going_away);
}
awaitable_strand ws_session(std::string_view host, uint16_t port, std::string_view msg)
{
try
{
// Objects
http_socket sock(tcp_socket(co_await boost::asio::this_coro::executor), false);
tcp::resolver resolver(sock.get_executor());
std::string buf(msg);
// Async IO
co_await boost::asio::async_connect(sock.lowest_layer(), co_await resolver.async_resolve(host, std::to_string(port)), boost::asio::cancel_after(5s));
co_await read_write_one(sock, host, buf);
// Print echo
printf("Server echoed back\n\"%.*s\"\n", (int)buf.size(), buf.data());
}
catch(const boost::system::system_error& e)
{
if (e.code() != boost::asio::error::eof)
fprintf(stderr, "[HTTP session] %s\n", e.what());
}
}
awaitable_strand ws_ssl_session(std::string_view host, uint16_t port, std::string_view msg)
{
try
{
// Objects
boost::asio::ssl::context ssl(boost::asio::ssl::context::tlsv12_client);
ssl.set_verify_callback([](bool preverified, boost::asio::ssl::verify_context& ctx) {return true;});
ssl.set_verify_mode(boost::asio::ssl::verify_peer);
https_socket sock(tls_socket(co_await boost::asio::this_coro::executor, ssl), false);
tcp::resolver resolver(sock.get_executor());
std::string buf(msg);
// Async IO
co_await boost::asio::async_connect(sock.lowest_layer(), co_await resolver.async_resolve(host, std::to_string(port)), boost::asio::cancel_after(5s));
co_await sock.next_layer().async_handshake(boost::asio::ssl::stream_base::client);
co_await read_write_one(sock, host, buf);
co_await sock.next_layer().async_shutdown();
// Print echo
printf("TLS server echoed back\n\"%.*s\"\n", (int)buf.size(), buf.data());
}
catch(const boost::system::system_error& e)
{
if (e.code() != boost::asio::error::eof)
fprintf(stderr, "[HTTP session] %s\n", e.what());
}
}
int main(int argc, char* argv[])
{
std::string host;
uint16_t port;
std::string msg;
bool use_tls{};
CLI::App app{"WebSocket echo client"};
try{
app.add_option("--host", host, "Host or IP address of WebSocket server")->required();
app.add_option("--port", port, "Port of WebSocket server")->required();
app.add_option("--msg", msg, "Message to be echoed back by server")->required();
app.add_flag("--tls", use_tls, "Use transport over TLS");
app.parse(argc, argv);
} catch (const CLI::ParseError& e) {return app.exit(e);}
boost::asio::io_context ioc{1};
try
{
if (use_tls)
co_spawn(make_strand(ioc), ws_ssl_session(host, port, msg), detached);
else
co_spawn(make_strand(ioc), ws_session(host, port, msg), detached);
ioc.run();
}
catch (const std::exception& e)
{
printf("Exception: %s\n", e.what());
}
}