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);
182 if(buf.isEmpty() && st->enc != ossia::net::encoding::none)
184 ossia::qt::run_async(
189 if(self->onBytes.isCallable())
191 auto engine = qjsEngine(self.get());
193 self->onBytes.call({engine->toScriptValue(buf)});
199 bool validate_stream(boost::system::error_code ec)
const
201 if(ec == boost::asio::error::operation_aborted)
203 if(ec == boost::asio::error::eof)
205 ossia::qt::run_async(
210 if(self->onClose.isCallable())
211 self->onClose.call();
224 explicit qml_tcp_connection(
225 ossia::net::tcp_listener listener, boost::asio::io_context& ctx,
226 ossia::net::framing f = ossia::net::framing::none,
227 const std::string& delim = {},
228 ossia::net::encoding e = ossia::net::encoding::none)
229 : m_state{std::make_shared<state>(std::move(listener), ctx, f, delim, e)}
233 ~qml_tcp_connection()
237 m_state->alive =
false;
242 bool isOpen() const noexcept {
return m_state && m_state->alive; }
244 inline boost::asio::io_context& context() noexcept {
return m_state->context; }
246 void write(QByteArray buffer)
251 if(st->enc != ossia::net::encoding::none)
252 buffer = apply_encoding(st->enc, buffer);
253 boost::asio::dispatch(st->context, [st, buffer = std::move(buffer)] {
255 st->write_encoded(buffer.data(), buffer.size());
260 void close(QByteArray)
265 boost::asio::dispatch(st->context, [st] { st->listener.close(); });
269 void receive(QJSValue v)
273 auto self = QPointer{
this};
275 [cb = receive_callback{st, self}](
auto& decoder)
mutable {
276 decoder.receive(std::move(cb));
284 W_PROPERTY(QJSValue, onClose W_MEMBER onClose);
287 std::shared_ptr<state> m_state;
290class qml_tcp_inbound_socket
292 ,
public Nano::Observer
294 W_OBJECT(qml_tcp_inbound_socket)
298 ossia::net::tcp_server server;
299 std::atomic_bool alive{
true};
300 std::atomic_bool open{
false};
301 ossia::net::framing framing{ossia::net::framing::none};
302 std::string framing_delimiter;
303 ossia::net::encoding enc{ossia::net::encoding::none};
306 const ossia::net::inbound_socket_configuration& conf,
307 boost::asio::io_context& ctx,
308 ossia::net::framing f = ossia::net::framing::none,
309 std::string delim = {},
310 ossia::net::encoding e = ossia::net::encoding::none)
313 , framing_delimiter{std::move(delim)}
319 qml_tcp_inbound_socket() { }
321 ~qml_tcp_inbound_socket()
325 m_state->alive =
false;
330 bool isOpen() const noexcept {
return m_state && m_state->open; }
333 const ossia::net::inbound_socket_configuration& conf,
334 boost::asio::io_context& ctx,
335 ossia::net::framing f = ossia::net::framing::none,
336 const std::string& delim = {},
337 ossia::net::encoding e = ossia::net::encoding::none)
339 m_state = std::make_shared<state>(conf, ctx, f, delim, e);
340 m_state->open =
true;
341 accept_impl(m_state, QPointer{
this});
342 if(onOpen.isCallable())
343 onOpen.call({qjsEngine(
this)->newQObject(
this)});
350 m_state->open =
false;
351 m_state->server.m_acceptor.close();
352 if(onClose.isCallable())
361 m_state->open =
false;
362 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
368 QJSValue onConnection;
371 static void accept_impl(
372 std::shared_ptr<state> st, QPointer<qml_tcp_inbound_socket> self)
374 st->server.m_acceptor.async_accept(
376 boost::system::error_code ec, ossia::net::tcp_server::proto::socket socket) {
377 if(!st->alive || !st->open)
381 ossia::qt::run_async(
383 [self, st, socket = std::move(socket)]()
mutable {
386 auto conn = new qml_tcp_connection{
387 ossia::net::tcp_listener{std::move(socket)}, st->server.m_context,
388 st->framing, st->framing_delimiter, st->enc};
392 conn->setParent(self.get());
394 if(self->onConnection.isCallable())
396 self->onConnection.call({qjsEngine(self.get())->newQObject(conn)});
400 accept_impl(st, self);
405 std::shared_ptr<state> m_state;
Definition qml_device.cpp:43