Orbit Framework
An ultra-modern, asynchronous, cross-platform C++ Web Framework.
Loading...
Searching...
No Matches
EventRouter.hpp
Go to the documentation of this file.
1#pragma once
4#include <orbit/http/json.hpp>
5#include <string>
6#include <unordered_map>
7#include <unordered_set>
8#include <functional>
9#include <mutex>
10#include <memory>
11#include <iostream>
12
13namespace websocket {
14
15struct EmptySession {};
16
17template <typename SessionType = EmptySession>
18class EventRouter;
19
23template <typename SessionType>
25public:
27 : raw_(raw), router_(router), id_(std::move(id)) {}
28
32 template <typename T>
33 void emit(const std::string& event, const T& data) {
34 nlohmann::json payload = {{"event", event}, {"data", data}};
35 raw_.send(payload.dump());
36 }
37
41 void join(const std::string& room);
42
46 void leave(const std::string& room);
47
51 auto to(const std::string& room);
52
56 SessionType& session() { return session_; }
57
58 const std::string& id() const { return id_; }
59
60 // Internal access
62
63private:
66 std::string id_;
67 SessionType session_;
68 std::unordered_set<std::string> rooms_;
69
70 friend class EventRouter<SessionType>;
71};
72
76template <typename SessionType>
78public:
79 using SocketPtr = std::shared_ptr<EventSocket<SessionType>>;
80
87 template <typename PayloadType>
88 void on(const std::string& event, std::function<void(EventSocket<SessionType>&, const PayloadType&)> handler) {
89 handlers_[event] = [handler, event](EventSocket<SessionType>& ws, const nlohmann::json& raw_data) {
90 try {
91 PayloadType data = raw_data.get<PayloadType>();
92 handler(ws, data);
93 } catch (const std::exception& e) {
94 // Ignore or log bad payload
95 std::cerr << "[EventRouter] Invalid payload for event '" << event << "': " << e.what() << "\n";
96 }
97 };
98 }
99
103 void on(const std::string& event, std::function<void(EventSocket<SessionType>&)> handler) {
104 handlers_[event] = [handler](EventSocket<SessionType>& ws, const nlohmann::json&) {
105 handler(ws);
106 };
107 }
108
112 void on_connect(std::function<void(EventSocket<SessionType>&)> handler) {
113 on_connect_ = std::move(handler);
114 }
115
119 void on_disconnect(std::function<void(EventSocket<SessionType>&)> handler) {
120 on_disconnect_ = std::move(handler);
121 }
122
126 void attach(server::App& app, const std::string& path) {
128
129 // Simple UUID generation using counter and pointer for uniqueness
130 static std::atomic<uint64_t> counter = 0;
131 std::string id = "ws_" + std::to_string(++counter) + "_" + std::to_string(reinterpret_cast<uint64_t>(&raw_ws));
132
133 auto es = std::make_shared<EventSocket<SessionType>>(raw_ws, *this, id);
134
135 {
136 std::lock_guard<std::mutex> lock(mutex_);
137 sockets_[id] = es;
138 }
139
140 if (on_connect_) on_connect_(*es);
141
142 raw_ws.on_message([this, id](const std::string& msg) {
143 try {
145 if (!j.contains("event")) return;
146
147 std::string event_name = j["event"];
148 nlohmann::json data = j.contains("data") ? j["data"] : nlohmann::json(nullptr);
149
150 auto socket = get_socket(id);
151 if (socket) {
152 auto it = handlers_.find(event_name);
153 if (it != handlers_.end()) {
154 it->second(*socket, data);
155 }
156 }
157 } catch (...) {
158 // Ignore malformed JSON
159 }
160 });
161
162 raw_ws.on_close([this, id]() {
163 auto socket = get_socket(id);
164 if (socket && on_disconnect_) on_disconnect_(*socket);
165
166 std::lock_guard<std::mutex> lock(mutex_);
167 if (socket) {
168 for (const auto& room : socket->rooms_) {
169 rooms_[room].erase(id);
170 }
171 }
172 sockets_.erase(id);
173 });
174 });
175 }
176
177 // Broadcasting helper class
180 std::string room;
181
182 template <typename T>
183 void emit(const std::string& event, const T& data) {
184 nlohmann::json payload = {{"event", event}, {"data", data}};
185 std::string msg = payload.dump();
187 }
188 };
189
190 RoomBroadcaster to(const std::string& room) {
191 return RoomBroadcaster{*this, room};
192 }
193
194 // Internal API for EventSocket
195 void join_room(const std::string& id, const std::string& room) {
196 std::lock_guard<std::mutex> lock(mutex_);
197 if (sockets_.count(id)) {
198 sockets_[id]->rooms_.insert(room);
199 rooms_[room].insert(id);
200 }
201 }
202
203 void leave_room(const std::string& id, const std::string& room) {
204 std::lock_guard<std::mutex> lock(mutex_);
205 if (sockets_.count(id)) {
206 sockets_[id]->rooms_.erase(room);
207 rooms_[room].erase(id);
208 }
209 }
210
211 void broadcast_to_room(const std::string& room, const std::string& msg) {
212 std::lock_guard<std::mutex> lock(mutex_);
213 if (rooms_.count(room)) {
214 for (const std::string& id : rooms_[room]) {
215 if (sockets_.count(id)) {
216 sockets_[id]->raw().send(msg);
217 }
218 }
219 }
220 }
221
222private:
223 SocketPtr get_socket(const std::string& id) {
224 std::lock_guard<std::mutex> lock(mutex_);
225 auto it = sockets_.find(id);
226 if (it != sockets_.end()) return it->second;
227 return nullptr;
228 }
229
230 std::mutex mutex_;
231 std::unordered_map<std::string, SocketPtr> sockets_;
232 std::unordered_map<std::string, std::unordered_set<std::string>> rooms_;
233 std::unordered_map<std::string, std::function<void(EventSocket<SessionType>&, const nlohmann::json&)>> handlers_;
234
235 std::function<void(EventSocket<SessionType>&)> on_connect_;
236 std::function<void(EventSocket<SessionType>&)> on_disconnect_;
237};
238
239template <typename SessionType>
240void EventSocket<SessionType>::join(const std::string& room) {
241 router_.join_room(id_, room);
242}
243
244template <typename SessionType>
245void EventSocket<SessionType>::leave(const std::string& room) {
246 router_.leave_room(id_, room);
247}
248
249template <typename SessionType>
250auto EventSocket<SessionType>::to(const std::string& room) {
251 return router_.to(room);
252}
253
254} // namespace websocket
Represents an active WebSocket connection.
Definition WebSocketConnection.hpp:56
void send(const std::string &message)
Sends a text message over the WebSocket connection.
Definition WebSocketConnection.cpp:113
a class to store JSON values
Definition json.hpp:19401
static basic_json parse(InputType &&i, const parser_callback_t cb=nullptr, const bool allow_exceptions=true, const bool ignore_comments=false)
deserialize from a compatible input
Definition json.hpp:23317
The main application class for the Orbit Framework.
Definition App.hpp:26
A Socket.IO style event router for WebSockets with Room and Session support.
Definition EventRouter.hpp:77
void join_room(const std::string &id, const std::string &room)
Definition EventRouter.hpp:195
std::shared_ptr< EventSocket< SessionType > > SocketPtr
Definition EventRouter.hpp:79
void on(const std::string &event, std::function< void(EventSocket< SessionType > &)> handler)
Registers an untyped event handler (no data expected).
Definition EventRouter.hpp:103
void on(const std::string &event, std::function< void(EventSocket< SessionType > &, const PayloadType &)> handler)
Definition EventRouter.hpp:88
void leave_room(const std::string &id, const std::string &room)
Definition EventRouter.hpp:203
void on_connect(std::function< void(EventSocket< SessionType > &)> handler)
Registers a connection handler.
Definition EventRouter.hpp:112
void on_disconnect(std::function< void(EventSocket< SessionType > &)> handler)
Registers a disconnection handler.
Definition EventRouter.hpp:119
RoomBroadcaster to(const std::string &room)
Definition EventRouter.hpp:190
void attach(server::App &app, const std::string &path)
Attaches this EventRouter to the Orbit App at a specific path.
Definition EventRouter.hpp:126
void broadcast_to_room(const std::string &room, const std::string &msg)
Definition EventRouter.hpp:211
Represents a connected client in the EventRouter.
Definition EventRouter.hpp:24
void join(const std::string &room)
Joins a specific room/channel.
Definition EventRouter.hpp:240
void leave(const std::string &room)
Leaves a specific room/channel.
Definition EventRouter.hpp:245
void emit(const std::string &event, const T &data)
Emits an event with strongly-typed data back to this specific client.
Definition EventRouter.hpp:33
EventSocket(http::websocket::WebSocketConnection &raw, EventRouter< SessionType > &router, std::string id)
Definition EventRouter.hpp:26
auto to(const std::string &room)
Returns a broadcaster to emit messages to everyone in the room.
Definition EventRouter.hpp:250
const std::string & id() const
Definition EventRouter.hpp:58
SessionType & session()
The strongly-typed session state attached to this connection.
Definition EventRouter.hpp:56
http::websocket::WebSocketConnection & raw()
Definition EventRouter.hpp:61
basic_json<> json
default specialization
Definition json.hpp:3422
Definition json.hpp:5363
Definition EventRouter.hpp:13
Definition EventRouter.hpp:15
Definition EventRouter.hpp:178
EventRouter & router
Definition EventRouter.hpp:179
void emit(const std::string &event, const T &data)
Definition EventRouter.hpp:183
std::string room
Definition EventRouter.hpp:180