2#include <ossia/detail/variant.hpp>
3#include <ossia/network/context.hpp>
4#include <ossia/network/sockets/configuration.hpp>
5#include <ossia/network/sockets/encoding.hpp>
6#include <ossia/network/sockets/cobs_framing.hpp>
7#include <ossia/network/sockets/fixed_length_framing.hpp>
8#include <ossia/network/sockets/line_framing.hpp>
9#include <ossia/network/sockets/no_framing.hpp>
10#include <ossia/network/sockets/size_prefix_framing.hpp>
11#include <ossia/network/sockets/slip_framing.hpp>
12#include <ossia/network/sockets/stx_etx_framing.hpp>
13#include <ossia/network/sockets/tcp_socket.hpp>
14#include <ossia/network/sockets/var_size_prefix_framing.hpp>
16#include <ossia-qt/protocols/utils.hpp>
22#include <nano_observer.hpp>
28class qml_tcp_connection
30 ,
public Nano::Observer
32 W_OBJECT(qml_tcp_connection)
34 using socket_t = boost::asio::ip::tcp::socket;
35 using decoder_type = ossia::slow_variant<
36 ossia::net::no_framing::decoder<socket_t>,
37 ossia::net::slip_decoder<socket_t>,
38 ossia::net::size_prefix_decoder<socket_t>,
39 ossia::net::line_framing_decoder<socket_t>,
40 ossia::net::cobs_decoder<socket_t>,
41 ossia::net::stx_etx_framing::decoder<socket_t>,
42 ossia::net::size_prefix_1byte_framing::decoder<socket_t>,
43 ossia::net::size_prefix_2byte_be_framing::decoder<socket_t>,
44 ossia::net::size_prefix_2byte_le_framing::decoder<socket_t>,
45 ossia::net::size_prefix_4byte_le_framing::decoder<socket_t>,
46 ossia::net::fixed_length_decoder<socket_t>>;
50 boost::asio::io_context& context;
51 ossia::net::tcp_listener listener;
52 std::atomic_bool alive{
true};
53 ossia::net::framing framing{ossia::net::framing::none};
54 ossia::net::encoding enc{ossia::net::encoding::none};
55 char line_delimiter[8] = {};
59 ossia::net::tcp_listener l, boost::asio::io_context& ctx,
60 ossia::net::framing f = ossia::net::framing::none,
61 const std::string& delim = {},
62 ossia::net::encoding e = ossia::net::encoding::none)
64 , listener{std::move(l)}
65 , decoder{
ossia::in_place_index<0>, listener.m_socket}
71 auto sz = std::min(delim.size(), (
size_t)7);
72 std::copy_n(delim.begin(), sz, line_delimiter);
78 case ossia::net::framing::none:
80 case ossia::net::framing::slip:
81 decoder.template emplace<1>(listener.m_socket);
83 case ossia::net::framing::size_prefix:
84 decoder.template emplace<2>(listener.m_socket);
86 case ossia::net::framing::line_delimiter:
87 decoder.template emplace<3>(listener.m_socket);
89 auto& dec = ossia::get<3>(decoder);
90 std::copy_n(line_delimiter, 8, dec.delimiter);
93 case ossia::net::framing::cobs:
94 decoder.template emplace<4>(listener.m_socket);
96 case ossia::net::framing::stx_etx:
97 decoder.template emplace<5>(listener.m_socket);
99 case ossia::net::framing::size_prefix_1byte:
100 decoder.template emplace<6>(listener.m_socket);
102 case ossia::net::framing::size_prefix_2byte_be:
103 decoder.template emplace<7>(listener.m_socket);
105 case ossia::net::framing::size_prefix_2byte_le:
106 decoder.template emplace<8>(listener.m_socket);
108 case ossia::net::framing::size_prefix_4byte_le:
109 decoder.template emplace<9>(listener.m_socket);
111 case ossia::net::framing::fixed_length:
112 decoder.template emplace<10>(listener.m_socket);
114 ossia::get<10>(decoder).frame_size = std::stoul(delim);
119 void write_encoded(
const char* data, std::size_t sz)
124 case ossia::net::framing::none:
125 listener.write(boost::asio::const_buffer(data, sz));
127 case ossia::net::framing::slip:
128 ossia::net::slip_encoder<socket_t>{listener.m_socket}.write(data, sz);
130 case ossia::net::framing::size_prefix:
131 ossia::net::size_prefix_encoder<socket_t>{listener.m_socket}.write(data, sz);
133 case ossia::net::framing::line_delimiter: {
134 ossia::net::line_framing_encoder<socket_t> enc{listener.m_socket};
135 std::copy_n(line_delimiter, 8, enc.delimiter);
139 case ossia::net::framing::cobs:
140 ossia::net::cobs_encoder<socket_t>{listener.m_socket}.write(data, sz);
142 case ossia::net::framing::stx_etx:
143 ossia::net::stx_etx_framing::encoder<socket_t>{listener.m_socket}.write(
146 case ossia::net::framing::size_prefix_1byte:
147 ossia::net::size_prefix_1byte_framing::encoder<socket_t>{listener.m_socket}
150 case ossia::net::framing::size_prefix_2byte_be:
151 ossia::net::size_prefix_2byte_be_framing::encoder<socket_t>{listener.m_socket}
154 case ossia::net::framing::size_prefix_2byte_le:
155 ossia::net::size_prefix_2byte_le_framing::encoder<socket_t>{listener.m_socket}
158 case ossia::net::framing::size_prefix_4byte_le:
159 ossia::net::size_prefix_4byte_le_framing::encoder<socket_t>{listener.m_socket}
162 case ossia::net::framing::fixed_length:
163 ossia::net::fixed_length_encoder<socket_t>{listener.m_socket}.write(data, sz);
169 struct receive_callback
171 std::shared_ptr<state> st;
172 QPointer<qml_tcp_connection> self;
174 void operator()(
const unsigned char* data, std::size_t sz)
const
178 auto buf = apply_decoding(st->enc, data, sz);
179 ossia::qt::run_async(
184 if(self->onBytes.isCallable())
186 auto engine = qjsEngine(self.get());
188 self->onBytes.call({engine->toScriptValue(buf)});
194 bool validate_stream(boost::system::error_code ec)
const
196 if(ec == boost::asio::error::operation_aborted)
198 if(ec == boost::asio::error::eof)
200 ossia::qt::run_async(
205 if(self->onClose.isCallable())
206 self->onClose.call();
219 explicit qml_tcp_connection(
220 ossia::net::tcp_listener listener, boost::asio::io_context& ctx,
221 ossia::net::framing f = ossia::net::framing::none,
222 const std::string& delim = {},
223 ossia::net::encoding e = ossia::net::encoding::none)
224 : m_state{std::make_shared<state>(std::move(listener), ctx, f, delim, e)}
228 ~qml_tcp_connection()
232 m_state->alive =
false;
237 bool isOpen() const noexcept {
return m_state && m_state->alive; }
239 inline boost::asio::io_context& context() noexcept {
return m_state->context; }
241 void write(QByteArray buffer)
246 if(st->enc != ossia::net::encoding::none)
247 buffer = apply_encoding(st->enc, buffer);
248 boost::asio::dispatch(st->context, [st, buffer = std::move(buffer)] {
250 st->write_encoded(buffer.data(), buffer.size());
255 void close(QByteArray)
260 boost::asio::dispatch(st->context, [st] { st->listener.close(); });
264 void receive(QJSValue v)
268 auto self = QPointer{
this};
270 [cb = receive_callback{st, self}](
auto& decoder)
mutable {
271 decoder.receive(std::move(cb));
279 W_PROPERTY(QJSValue, onClose W_MEMBER onClose);
282 std::shared_ptr<state> m_state;
285class qml_tcp_inbound_socket
287 ,
public Nano::Observer
289 W_OBJECT(qml_tcp_inbound_socket)
293 ossia::net::tcp_server server;
294 std::atomic_bool alive{
true};
295 std::atomic_bool open{
false};
296 ossia::net::framing framing{ossia::net::framing::none};
297 std::string framing_delimiter;
298 ossia::net::encoding enc{ossia::net::encoding::none};
301 const ossia::net::inbound_socket_configuration& conf,
302 boost::asio::io_context& ctx,
303 ossia::net::framing f = ossia::net::framing::none,
304 std::string delim = {},
305 ossia::net::encoding e = ossia::net::encoding::none)
308 , framing_delimiter{std::move(delim)}
314 qml_tcp_inbound_socket() { }
316 ~qml_tcp_inbound_socket()
320 m_state->alive =
false;
325 bool isOpen() const noexcept {
return m_state && m_state->open; }
328 const ossia::net::inbound_socket_configuration& conf,
329 boost::asio::io_context& ctx,
330 ossia::net::framing f = ossia::net::framing::none,
331 const std::string& delim = {},
332 ossia::net::encoding e = ossia::net::encoding::none)
334 m_state = std::make_shared<state>(conf, ctx, f, delim, e);
335 m_state->open =
true;
336 accept_impl(m_state, QPointer{
this});
337 if(onOpen.isCallable())
338 onOpen.call({qjsEngine(
this)->newQObject(
this)});
345 m_state->open =
false;
346 m_state->server.m_acceptor.close();
347 if(onClose.isCallable())
356 m_state->open =
false;
357 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
363 QJSValue onConnection;
366 static void accept_impl(
367 std::shared_ptr<state> st, QPointer<qml_tcp_inbound_socket> self)
369 st->server.m_acceptor.async_accept(
371 boost::system::error_code ec, ossia::net::tcp_server::proto::socket socket) {
372 if(!st->alive || !st->open)
376 ossia::qt::run_async(
378 [self, st, socket = std::move(socket)]()
mutable {
381 auto conn = new qml_tcp_connection{
382 ossia::net::tcp_listener{std::move(socket)}, st->server.m_context,
383 st->framing, st->framing_delimiter, st->enc};
387 conn->setParent(self.get());
389 if(self->onConnection.isCallable())
391 self->onConnection.call({qjsEngine(self.get())->newQObject(conn)});
395 accept_impl(st, self);
400 std::shared_ptr<state> m_state;
Definition qml_device.cpp:43