2#include <ossia/detail/variant.hpp>
3#include <ossia/network/context.hpp>
4#include <ossia/network/sockets/configuration.hpp>
5#include <ossia/network/sockets/line_framing.hpp>
6#include <ossia/network/sockets/no_framing.hpp>
7#include <ossia/network/sockets/size_prefix_framing.hpp>
8#include <ossia/network/sockets/slip_framing.hpp>
9#include <ossia/network/sockets/tcp_socket.hpp>
11#include <ossia-qt/protocols/utils.hpp>
17#include <nano_observer.hpp>
23class qml_tcp_connection
25 ,
public Nano::Observer
27 W_OBJECT(qml_tcp_connection)
29 using socket_t = boost::asio::ip::tcp::socket;
30 using decoder_type = ossia::slow_variant<
31 ossia::net::no_framing::decoder<socket_t>,
32 ossia::net::slip_decoder<socket_t>,
33 ossia::net::size_prefix_decoder<socket_t>,
34 ossia::net::line_framing_decoder<socket_t>>;
38 boost::asio::io_context& context;
39 ossia::net::tcp_listener listener;
40 std::atomic_bool alive{
true};
41 ossia::net::framing framing{ossia::net::framing::none};
42 char line_delimiter[8] = {};
46 ossia::net::tcp_listener l, boost::asio::io_context& ctx,
47 ossia::net::framing f = ossia::net::framing::none,
48 const std::string& delim = {})
50 , listener{std::move(l)}
51 , decoder{
ossia::in_place_index<0>, listener.m_socket}
56 auto sz = std::min(delim.size(), (
size_t)7);
57 std::copy_n(delim.begin(), sz, line_delimiter);
63 case ossia::net::framing::none:
65 case ossia::net::framing::slip:
66 decoder.template emplace<1>(listener.m_socket);
68 case ossia::net::framing::size_prefix:
69 decoder.template emplace<2>(listener.m_socket);
71 case ossia::net::framing::line_delimiter:
72 decoder.template emplace<3>(listener.m_socket);
74 auto& dec = ossia::get<3>(decoder);
75 std::copy_n(line_delimiter, 8, dec.delimiter);
81 void write_encoded(
const char* data, std::size_t sz)
86 case ossia::net::framing::none:
87 listener.write(boost::asio::const_buffer(data, sz));
89 case ossia::net::framing::slip: {
90 ossia::net::slip_encoder<socket_t> enc{listener.m_socket};
94 case ossia::net::framing::size_prefix: {
95 ossia::net::size_prefix_encoder<socket_t> enc{listener.m_socket};
99 case ossia::net::framing::line_delimiter: {
100 ossia::net::line_framing_encoder<socket_t> enc{listener.m_socket};
101 std::copy_n(line_delimiter, 8, enc.delimiter);
109 struct receive_callback
111 std::shared_ptr<state> st;
112 QPointer<qml_tcp_connection> self;
114 void operator()(
const unsigned char* data, std::size_t sz)
const
118 auto buf = QByteArray((
const char*)data, sz);
119 ossia::qt::run_async(
124 if(self->onBytes.isCallable())
126 auto engine = qjsEngine(self.get());
128 self->onBytes.call({engine->toScriptValue(buf)});
134 bool validate_stream(boost::system::error_code ec)
const
136 if(ec == boost::asio::error::operation_aborted)
138 if(ec == boost::asio::error::eof)
140 ossia::qt::run_async(
143 if(self && self->onClose.isCallable())
144 self->onClose.call();
153 explicit qml_tcp_connection(
154 ossia::net::tcp_listener listener, boost::asio::io_context& ctx,
155 ossia::net::framing f = ossia::net::framing::none,
156 const std::string& delim = {})
157 : m_state{std::make_shared<state>(std::move(listener), ctx, f, delim)}
161 ~qml_tcp_connection()
165 m_state->alive =
false;
170 bool isOpen() const noexcept {
return m_state && m_state->alive; }
172 inline boost::asio::io_context& context() noexcept {
return m_state->context; }
174 void write(QByteArray buffer)
179 boost::asio::dispatch(st->context, [st, buffer] {
181 st->write_encoded(buffer.data(), buffer.size());
186 void close(QByteArray)
191 boost::asio::dispatch(st->context, [st] { st->listener.close(); });
195 void receive(QJSValue v)
199 auto self = QPointer{
this};
201 [cb = receive_callback{st, self}](
auto& decoder)
mutable {
202 decoder.receive(std::move(cb));
210 W_PROPERTY(QJSValue, onClose W_MEMBER onClose);
213 std::shared_ptr<state> m_state;
216class qml_tcp_inbound_socket
218 ,
public Nano::Observer
220 W_OBJECT(qml_tcp_inbound_socket)
224 ossia::net::tcp_server server;
225 std::atomic_bool alive{
true};
226 std::atomic_bool open{
false};
227 ossia::net::framing framing{ossia::net::framing::none};
228 std::string framing_delimiter;
231 const ossia::net::inbound_socket_configuration& conf,
232 boost::asio::io_context& ctx,
233 ossia::net::framing f = ossia::net::framing::none,
234 std::string delim = {})
237 , framing_delimiter{std::move(delim)}
242 qml_tcp_inbound_socket() { }
244 ~qml_tcp_inbound_socket()
248 m_state->alive =
false;
253 bool isOpen() const noexcept {
return m_state && m_state->open; }
256 const ossia::net::inbound_socket_configuration& conf,
257 boost::asio::io_context& ctx,
258 ossia::net::framing f = ossia::net::framing::none,
259 const std::string& delim = {})
261 m_state = std::make_shared<state>(conf, ctx, f, delim);
262 m_state->open =
true;
263 accept_impl(m_state, QPointer{
this});
264 if(onOpen.isCallable())
265 onOpen.call({qjsEngine(
this)->newQObject(
this)});
272 m_state->open =
false;
273 m_state->server.m_acceptor.close();
274 if(onClose.isCallable())
283 m_state->open =
false;
284 ossia::qt::run_async(
this, [=,
this] { onClose.call(); }, Qt::AutoConnection);
290 QJSValue onConnection;
293 static void accept_impl(
294 std::shared_ptr<state> st, QPointer<qml_tcp_inbound_socket> self)
296 st->server.m_acceptor.async_accept(
298 boost::system::error_code ec, ossia::net::tcp_server::proto::socket socket) {
299 if(!st->alive || !st->open)
303 ossia::qt::run_async(
305 [self, st, socket = std::move(socket)]()
mutable {
308 auto conn = new qml_tcp_connection{
309 ossia::net::tcp_listener{std::move(socket)}, st->server.m_context,
310 st->framing, st->framing_delimiter};
312 if(self->onConnection.isCallable())
314 self->onConnection.call({qjsEngine(self.get())->newQObject(conn)});
318 accept_impl(st, self);
323 std::shared_ptr<state> m_state;
Definition qml_device.cpp:43