-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathex_hello_wss_builtin.cpp
130 lines (111 loc) · 4.23 KB
/
ex_hello_wss_builtin.cpp
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
128
129
130
// NOTE: Must run Python 3 script `run_wss_server.py` before running this example.
// This example demonstrates how to create a WebSocket client with TLS encryption (WSS).
// It uses a OpenSSL wrapped TCP socket to connect to a WebSocket server running on localhost:9443.
#include <iostream>
#include <string>
#include <format>
#include <chrono>
#include <algorithm>
#include <iomanip>
#include "ws_client/ws_client.hpp"
#include "ws_client/transport/builtin/TcpSocket.hpp"
#include "ws_client/transport/builtin/OpenSslSocket.hpp"
#include "ws_client/PermessageDeflate.hpp"
using namespace ws_client;
using namespace std::chrono_literals;
expected<void, WSError> run()
{
WS_TRY(url, URL::parse("wss://localhost:9443"));
// websocketclient logger
ConsoleLogger logger{LogLevel::D};
logger.set_level(LogTopic::DNS, LogLevel::D);
logger.set_level(LogTopic::TCP, LogLevel::D);
logger.set_level(LogTopic::Handshake, LogLevel::D);
logger.set_level(LogTopic::RecvFrame, LogLevel::D);
logger.set_level(LogTopic::RecvFramePayload, LogLevel::D);
logger.set_level(LogTopic::SendFrame, LogLevel::D);
logger.set_level(LogTopic::SendFramePayload, LogLevel::D);
// resolve hostname
DnsResolver dns(&logger);
WS_TRY(dns_res, dns.resolve(url->host(), url->port_str(), AddrType::ipv4));
AddressInfo& addr = (*dns_res)[0];
// create TCP socket
auto tcp = TcpSocket(&logger, std::move(addr));
WS_TRYV(tcp.init());
// SSL socket wrapper
OpenSslContext ctx(&logger);
WS_TRYV(ctx.init());
WS_TRYV(ctx.set_default_verify_paths());
WS_TRYV(ctx.load_verify_file("../cert.pem"));
WS_TRYV(ctx.set_session_cache_mode_client());
auto ssl = OpenSslSocket(&logger, std::move(tcp), &ctx, url->host(), true);
WS_TRYV(ssl.init());
WS_TRYV(ssl.connect(2s)); // 2 sec connect timeout
// create websocket client
auto client = WebSocketClient(&logger, std::move(ssl));
// handshake handler
auto handshake = Handshake(&logger, *url);
// enable compression (permessage-deflate extension)
handshake.set_permessage_deflate(
{.logger = &logger,
.server_max_window_bits = 15,
.client_max_window_bits = 15,
.server_no_context_takeover = true,
.client_no_context_takeover = true}
);
// perform handshake
WS_TRYV(client.handshake(handshake, 5s)); // 5 sec timeout
// allocate message buffer with 4 KiB initial size and 1 MiB max size
WS_TRY(buffer, Buffer::create(4096, 1 * 1024 * 1024));
while (true)
{
// read message from server into buffer
variant<Message, PingFrame, PongFrame, CloseFrame, WSError> var = //
client.read_message(*buffer, 30s); // 30 sec timeout
if (auto msg = std::get_if<Message>(&var))
{
WS_TRYV(client.send_message(*msg, {.timeout = 5s})); // 5 sec timeout
}
else if (auto ping_frame = std::get_if<PingFrame>(&var))
{
logger.log<LogLevel::D, LogTopic::User>("Ping frame received");
WS_TRYV(client.send_pong_frame(ping_frame->payload_bytes()));
}
else if (std::get_if<PongFrame>(&var))
{
logger.log<LogLevel::D, LogTopic::User>("Pong frame received");
}
else if (auto close_frame = std::get_if<CloseFrame>(&var))
{
// server initiated close
if (close_frame->has_reason())
{
logger.log<LogLevel::I, LogTopic::User>(
std::format("Close frame received: {}", close_frame->get_reason())
);
}
else
logger.log<LogLevel::I, LogTopic::User>("Close frame received");
break;
}
else if (auto err = std::get_if<WSError>(&var))
{
// error occurred - must close connection
logger.log<LogLevel::E, LogTopic::User>(err->to_string());
WS_TRYV(client.close(err->close_with_code));
return {};
}
}
WS_TRYV(client.close(close_code::normal_closure));
return {};
};
int main()
{
auto res = run();
if (!res.has_value())
{
std::cerr << "Error: " << res.error() << std::endl;
return 2;
}
return 0;
};