-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathex_hello_ws_builtin.cpp
122 lines (104 loc) · 4.02 KB
/
ex_hello_ws_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
// NOTE: Must run Python 3 script `run_ws_server.py` before running this example.
// This example demonstrates how to create a WebSocket client using the ws_client library.
// It uses a bare, unencrypted TCP socket to connect to a WebSocket server running on localhost:8080.
// Read and write timeouts are set to 1000 ms directly on the POSIX socket (see set_recv_timeout, set_send_timeout).
#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:8080"));
// 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());
WS_TRYV(tcp.connect(2s)); // 2 sec connect timeout
// create websocket client
auto client = WebSocketClient(&logger, std::move(tcp));
// 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;
};