OSSIA
Open Scenario System for Interactive Application
Loading...
Searching...
No Matches
socketio_session.hpp
1#pragma once
2
3#include <ossia/detail/config.hpp>
5
6#include <boost/asio/awaitable.hpp>
7#include <boost/asio/buffer.hpp>
8#include <boost/asio/co_spawn.hpp>
9#include <boost/asio/experimental/awaitable_operators.hpp>
10#include <boost/asio/experimental/channel.hpp>
11#include <boost/asio/io_context.hpp>
12#include <boost/asio/strand.hpp>
13#include <boost/beast/core.hpp>
14#include <boost/beast/http.hpp>
15#include <boost/beast/version.hpp>
16#include <boost/beast/websocket.hpp>
17#include <boost/json.hpp>
18
19#include <charconv>
20#include <cstdlib>
21#include <functional>
22#include <memory>
23#include <string>
24
25namespace ossia::net
26{
27
29enum engine_io_packet_type : char
30{
31 EIO_OPEN = '0',
32 EIO_CLOSE = '1',
33 EIO_PING = '2',
34 EIO_PONG = '3',
35 EIO_MESSAGE = '4',
36 EIO_UPGRADE = '5',
37 EIO_NOOP = '6',
38};
39
41enum socket_io_packet_type : char
42{
43 SIO_CONNECT = '0',
44 SIO_DISCONNECT = '1',
45 SIO_EVENT = '2',
46 SIO_ACK = '3',
47 SIO_CONNECT_ERROR = '4',
48 SIO_BINARY_EVENT = '5',
49 SIO_BINARY_ACK = '6',
50};
51
54{
55 std::string sid;
56 std::string socketio_sid;
57 std::chrono::milliseconds ping_interval{25000};
58 std::chrono::milliseconds ping_timeout{20000};
59 int64_t max_payload{1000000};
60 bool can_websocket{false};
61};
62
64inline bool parse_engineio_open(std::string_view str, engineio_config& config)
65{
66 if(str.size() < 2 || !str.starts_with("0{"))
67 return false;
68
69 str = str.substr(1);
70 auto json = boost::json::parse(str);
71 if(auto obj = json.try_as_object())
72 {
73 auto& o = *obj;
74 if(auto k = o.find("sid"))
75 if(auto sid = k->value().try_as_string())
76 config.sid = *sid;
77
78 if(auto k = o.find("upgrades"))
79 if(auto v = k->value().try_as_array())
80 if(v->size() > 0 && v->front() == "websocket")
81 config.can_websocket = true;
82
83 if(auto k = o.find("pingInterval"))
84 if(auto v = k->value().try_as_int64())
85 config.ping_interval = std::chrono::milliseconds(*v);
86
87 if(auto k = o.find("pingTimeout"))
88 if(auto v = k->value().try_as_int64())
89 config.ping_timeout = std::chrono::milliseconds(*v);
90
91 if(auto k = o.find("maxPayload"))
92 if(auto v = k->value().try_as_int64())
93 config.max_payload = *v;
94 }
95 return !config.sid.empty();
96}
97
99inline bool parse_socketio_connect(std::string_view str, engineio_config& config)
100{
101 if(str.size() < 4 || !str.starts_with("40{"))
102 return false;
103 if(auto end = str.find('\x1e'); end != std::string_view::npos)
104 str = str.substr(0, end);
105
106 str = str.substr(2);
107 auto json = boost::json::parse(str);
108 if(auto obj = json.try_as_object())
109 {
110 if(auto k = obj->find("sid"))
111 if(auto sid = k->value().try_as_string())
112 {
113 config.socketio_sid = *sid;
114 return true;
115 }
116 }
117 return false;
118}
119
121inline std::string make_engineio_open(const engineio_config& config)
122{
123 boost::json::object obj;
124 obj["sid"] = config.sid;
125 obj["upgrades"] = boost::json::array{"websocket"};
126 obj["pingInterval"] = config.ping_interval.count();
127 obj["pingTimeout"] = config.ping_timeout.count();
128 obj["maxPayload"] = config.max_payload;
129 return std::string(1, EIO_OPEN) + boost::json::serialize(obj);
130}
131
133inline std::string make_socketio_connect(const std::string& sid)
134{
135 boost::json::object obj;
136 obj["sid"] = sid;
137 return std::string("40") + boost::json::serialize(obj);
138}
139
141inline std::optional<int> consume_int(std::string_view& input)
142{
143 int out;
144 const std::from_chars_result result
145 = std::from_chars(input.data(), input.data() + input.size(), out);
146 if(result.ec == std::errc::invalid_argument
147 || result.ec == std::errc::result_out_of_range)
148 return std::nullopt;
149
150 int n = result.ptr - input.data();
151 input = input.substr(n);
152 return out;
153}
154
155}
Engine.IO configuration received from the server handshake.
Definition socketio_session.hpp:54