OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
websocket_client.hpp
1#pragma once
2#include <ossia/detail/config.hpp>
3
4#include <ossia/detail/json.hpp>
6
7#include <websocketpp/client.hpp>
8#include <websocketpp/common/thread.hpp>
9#include <websocketpp/config/asio_no_tls_client.hpp>
10
11#include <nano_signal_slot.hpp>
12
13namespace ossia::net
14{
15
18{
19public:
20 using connection_handler = websocketpp::connection_hdl;
21 Nano::Signal<void()> on_open;
22 Nano::Signal<void()> on_close;
23 Nano::Signal<void()> on_fail;
24
26 : m_open{false}
27 {
28 init_client();
29 }
30
31 void init_client()
32 {
33 m_client = std::make_shared<client_t>();
34 assert(m_client);
35 std::weak_ptr<client_t> weak_client = m_client;
36 m_client->clear_access_channels(websocketpp::log::alevel::all);
37 m_client->clear_error_channels(websocketpp::log::elevel::all);
38
39 m_client->set_open_handler([this, weak_client](connection_handler hdl) {
40 if(!weak_client.lock())
41 return;
42 scoped_lock guard(m_lock);
43 m_open = true;
44
45 on_open();
46 });
47
48 m_client->set_close_handler([this, weak_client](connection_handler hdl) {
49 if(!weak_client.lock())
50 return;
51 {
52 scoped_lock guard(m_lock);
53 m_open = false;
54 }
55 on_close();
56 });
57
58 m_client->set_fail_handler([this, weak_client](connection_handler hdl) {
59 if(!weak_client.lock())
60 return;
61 {
62 scoped_lock guard(m_lock);
63 m_open = false;
64 }
65 on_fail();
66 });
67 assert(m_client);
68 }
69
72 template <typename MessageHandler>
73 websocket_client(MessageHandler&& onMessage)
75 {
76 assert(m_client);
77 m_client->init_asio();
78
79 std::weak_ptr<client_t> weak_client = m_client;
80 m_client->set_message_handler(
81 [handler = std::move(onMessage),
82 weak_client](connection_handler hdl, client_t::message_ptr msg) {
83 if(!weak_client.lock())
84 return;
85 handler(hdl, msg->get_opcode(), msg->get_raw_payload());
86 });
87 assert(m_client);
88 }
89
90 template <typename MessageHandler>
91 websocket_client(boost::asio::io_context& ctx, MessageHandler&& onMessage)
93 {
94 assert(m_client);
95 m_client->init_asio(&ctx);
96 m_ctx = &ctx;
97
98 std::weak_ptr<client_t> weak_client = m_client;
99 m_client->set_message_handler(
100 [handler = std::move(onMessage),
101 weak_client](connection_handler hdl, client_t::message_ptr msg) {
102 if(!weak_client.lock())
103 return;
104 handler(hdl, msg->get_opcode(), msg->get_raw_payload());
105 });
106 assert(m_client);
107 }
108
109 ~websocket_client()
110 {
111 if(m_open)
112 stop();
113 }
114
115 bool connected() const { return m_open; }
116
117 void stop()
118 {
119 if(!m_open)
120 {
121 if(m_client)
122 m_client->stop();
123 m_connected = false;
124 return;
125 }
126
127 scoped_lock guard(m_lock);
128 m_client->close(m_hdl, websocketpp::close::status::normal, "");
129 m_client->stop();
130 m_open = false;
131 }
132
133 auto& client() { return m_client; }
134 auto& handle() { return m_hdl; }
135 bool after_connect() { return m_connected; }
136
137 void connect(const std::string& uri)
138 {
139 websocketpp::lib::error_code ec;
140 if(!m_client)
141 {
142 init_client();
143 assert(m_client);
144 if(m_ctx)
145 m_client->init_asio(m_ctx);
146 else
147 m_client->init_asio();
148 }
149
150 auto con = m_client->get_connection(uri, ec);
151 if(ec)
152 {
153 m_client->get_alog().write(
154 websocketpp::log::alevel::app, "Get Connection Error: " + ec.message());
155 return;
156 }
157
158 m_hdl = con->get_handle();
159 m_client->connect(con);
160 m_connected = true;
161 }
162
163 void finish_connection()
164 {
165 m_connected = false;
166 m_client.reset(); // In order to be able to reconnect afterwards.
167 }
168
169 // This function returns if the connection is stopped / fails.
170 void connect_and_run(const std::string& uri)
171 {
172 connect(uri);
173
174 m_client->run();
175
176 finish_connection();
177 }
178
179 void send_message(const std::string& request)
180 {
181 if(!m_open || !m_client)
182 return;
183
184 websocketpp::lib::error_code ec;
185
186 m_client->send(m_hdl, request, websocketpp::frame::opcode::text, ec);
187
188 if(ec)
189 {
190 m_client->get_alog().write(
191 websocketpp::log::alevel::app, "Send Error: " + ec.message());
192 }
193 }
194
195 void send_message(const rapidjson::StringBuffer& request)
196 {
197 if(!m_open || !m_client)
198 return;
199
200 websocketpp::lib::error_code ec;
201
202 m_client->send(
203 m_hdl, request.GetString(), request.GetSize(), websocketpp::frame::opcode::text,
204 ec);
205
206 if(ec)
207 {
208 m_client->get_alog().write(
209 websocketpp::log::alevel::app, "Send Error: " + ec.message());
210 }
211 }
212
213 void send_binary_message(std::string_view request)
214 {
215 if(!m_open || !m_client)
216 return;
217
218 websocketpp::lib::error_code ec;
219
220 m_client->send(
221 m_hdl, request.data(), request.size(), websocketpp::frame::opcode::binary, ec);
222
223 if(ec)
224 {
225 m_client->get_alog().write(
226 websocketpp::log::alevel::app, "Send Error: " + ec.message());
227 }
228 }
229
230protected:
231 using client_t = websocketpp::client<websocketpp::config::asio_client>;
232 using scoped_lock = websocketpp::lib::lock_guard<websocketpp::lib::mutex>;
233
234 boost::asio::io_context* m_ctx{};
235 std::shared_ptr<client_t> m_client;
236 connection_handler m_hdl;
237 websocketpp::lib::mutex m_lock;
238 std::atomic_bool m_open{false};
239 std::atomic_bool m_connected{false};
240};
241}
Low-level Websocket client.
Definition websocket_client.hpp:18
websocket_client(MessageHandler &&onMessage)
Definition websocket_client.hpp:73