2#include <ossia/network/context.hpp>
3#include <ossia/network/sockets/configuration.hpp>
4#include <ossia/network/sockets/websocket_server.hpp>
6#include <ossia-qt/protocols/utils.hpp>
12#include <nano_observer.hpp>
26 boost::asio::io_context&
context;
27 std::shared_ptr<ossia::net::websocket_server> server;
28 std::atomic_bool alive{
true};
32 , server{std::make_shared<ossia::net::websocket_server>(ctx)}
37class qml_websocket_connection
39 ,
public Nano::Observer
41 W_OBJECT(qml_websocket_connection)
43 using handler = ossia::net::websocket_server::connection_handler;
45 qml_websocket_connection(std::shared_ptr<websocket_server_state> st, handler hdl)
46 : m_state{std::move(st)}
47 , m_hdl{std::move(hdl)}
51 bool isOpen() const noexcept {
return m_state && !m_hdl.expired(); }
53 const handler& handle() const noexcept {
return m_hdl; }
55 void write(QString message)
60 boost::asio::dispatch(st->context, [st, hdl = m_hdl, msg = message.toStdString()] {
65 st->server->send_message(hdl, msg);
74 void writeBinary(QByteArray buffer)
79 boost::asio::dispatch(
81 [st, hdl = m_hdl, msg = std::string(buffer.data(), buffer.size())] {
86 st->server->send_binary_message(hdl, msg);
100 boost::asio::dispatch(st->context, [st, hdl = m_hdl] {
105 st->server->close(hdl);
116 websocketpp::frame::opcode::value opcode,
const std::string& payload)
const
118 if(opcode == websocketpp::frame::opcode::text && onTextMessage.isCallable())
119 onTextMessage.call({QString::fromUtf8(payload.data(), payload.size())});
120 else if(opcode == websocketpp::frame::opcode::binary && onBinaryMessage.isCallable())
122 if(
auto engine = qjsEngine(
this))
123 onBinaryMessage.call(
124 {engine->toScriptValue(QByteArray(payload.data(), payload.size()))});
127 if(onBytes.isCallable())
129 if(
auto engine = qjsEngine(
this))
130 onBytes.call({engine->toScriptValue(QByteArray(payload.data(), payload.size()))});
135 W_PROPERTY(QJSValue, onBytes W_MEMBER onBytes);
136 QJSValue onTextMessage;
137 W_PROPERTY(QJSValue, onTextMessage W_MEMBER onTextMessage);
138 QJSValue onBinaryMessage;
139 W_PROPERTY(QJSValue, onBinaryMessage W_MEMBER onBinaryMessage);
141 W_PROPERTY(QJSValue, onClose W_MEMBER onClose);
144 std::shared_ptr<websocket_server_state> m_state;
148class qml_websocket_inbound_socket
150 ,
public Nano::Observer
152 W_OBJECT(qml_websocket_inbound_socket)
154 using state = websocket_server_state;
155 using handler = ossia::net::websocket_server::connection_handler;
157 qml_websocket_inbound_socket() { }
159 ~qml_websocket_inbound_socket() { shutdown(); }
161 bool isOpen() const noexcept {
return m_state && m_state->alive; }
164 const ossia::net::inbound_socket_configuration& conf,
165 const ossia::net::network_context_ptr& ctx)
167 auto st = std::make_shared<state>(ctx);
168 auto self = QPointer{
this};
170 st->server->set_open_handler([st, self](handler hdl) {
173 if(
auto* ptr = self.get())
174 ossia::qt::run_async(
177 if(
auto* ptr = self.get())
178 ptr->on_connection(st, hdl);
183 st->server->set_close_handler([st, self](handler hdl) {
186 if(
auto* ptr = self.get())
187 ossia::qt::run_async(
190 if(
auto* ptr = self.get())
191 ptr->on_disconnection(hdl);
196 st->server->set_message_handler(
198 handler hdl, websocketpp::frame::opcode::value opcode,
199 const std::string& payload) -> ossia::net::server_reply {
202 if(
auto* ptr = self.get())
203 ossia::qt::run_async(
205 [self, hdl, opcode, msg = payload] {
206 if(
auto* ptr = self.get())
208 if(
auto* conn = ptr->find_connection(hdl))
209 conn->on_message(opcode, msg);
216 m_state = std::move(st);
217 m_state->server->listen(conf.port);
219 if(onOpen.isCallable())
220 onOpen.call({qjsEngine(
this)->newQObject(
this)});
228 if(onClose.isCallable())
236 QJSValue onConnection;
245 auto st = std::move(m_state);
248 std::vector<handler> handles;
249 handles.reserve(m_connections.size());
250 for(
auto* conn : m_connections)
252 handles.push_back(conn->handle());
258 m_connections.clear();
263 boost::asio::dispatch(st->context, [st, handles = std::move(handles)] {
264 for(const auto& hdl : handles)
268 st->server->close(hdl);
285 void on_connection(
const std::shared_ptr<state>& st,
const handler& hdl)
287 if(!m_state || m_state != st)
290 auto conn =
new qml_websocket_connection{st, hdl};
292 conn->setParent(
this);
293 m_connections.push_back(conn);
295 if(onConnection.isCallable())
296 onConnection.call({qjsEngine(
this)->newQObject(conn)});
299 void on_disconnection(
const handler& hdl)
301 for(
auto it = m_connections.begin(); it != m_connections.end(); ++it)
304 if(!same(conn->handle(), hdl))
307 m_connections.erase(it);
308 if(conn->onClose.isCallable())
309 conn->onClose.call();
315 qml_websocket_connection* find_connection(
const handler& hdl)
const noexcept
317 for(
auto* conn : m_connections)
318 if(same(conn->handle(), hdl))
323 static bool same(
const handler& lhs,
const handler& rhs)
noexcept
325 return !lhs.owner_before(rhs) && !rhs.owner_before(lhs);
328 std::shared_ptr<state> m_state;
329 std::vector<qml_websocket_connection*> m_connections;
Definition qml_device.cpp:43
If using the library, you should create this class at some point.
Definition context.hpp:27
context()
Most common case.
Definition context.cpp:89
Definition qml_ws_inbound_socket.hpp:25