Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
websocket_server.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
12
14{
15 using tcp = asio::ip::tcp;
16
17 // ========================================================================
18 // ws_connection_impl
19 // ========================================================================
20
22 {
23 public:
24 ws_connection_impl(const std::string& conn_id,
25 std::shared_ptr<internal::websocket_socket> ws_socket,
26 const std::string& remote_addr,
27 const std::string& ws_path = "/")
28 : connection_id_(conn_id)
29 , ws_socket_(ws_socket)
30 , remote_address_(remote_addr)
31 , path_(ws_path)
32 {
33 }
34
35 auto send_text(std::string&& message,
36 std::function<void(std::error_code, std::size_t)> handler)
37 -> VoidResult
38 {
39 std::lock_guard<std::mutex> lock(socket_mutex_);
40 if (!ws_socket_)
41 {
43 "Connection closed");
44 }
45 return ws_socket_->async_send_text(std::move(message),
46 std::move(handler));
47 }
48
49 auto send_binary(std::vector<uint8_t>&& data,
50 std::function<void(std::error_code, std::size_t)> handler)
51 -> VoidResult
52 {
53 std::lock_guard<std::mutex> lock(socket_mutex_);
54 if (!ws_socket_)
55 {
57 "Connection closed");
58 }
59 return ws_socket_->async_send_binary(std::move(data), std::move(handler));
60 }
61
62 auto close(internal::ws_close_code code, const std::string& reason)
63 -> VoidResult
64 {
65 std::lock_guard<std::mutex> lock(socket_mutex_);
66 if (!ws_socket_)
67 {
69 "Connection closed");
70 }
71 ws_socket_->async_close(code, reason, [](std::error_code) {});
72 return ok();
73 }
74
75 [[nodiscard]] auto connection_id() const -> const std::string& { return connection_id_; }
76
77 [[nodiscard]] auto remote_endpoint() const -> std::string { return remote_address_; }
78
79 [[nodiscard]] auto path() const -> std::string_view { return path_; }
80
81 [[nodiscard]] auto is_connected() const -> bool
82 {
83 std::lock_guard<std::mutex> lock(socket_mutex_);
84 return ws_socket_ && ws_socket_->is_open();
85 }
86
87 auto get_socket() -> std::shared_ptr<internal::websocket_socket>
88 {
89 std::lock_guard<std::mutex> lock(socket_mutex_);
90 return ws_socket_;
91 }
92
93 auto invalidate() -> void
94 {
95 std::lock_guard<std::mutex> lock(socket_mutex_);
96 ws_socket_.reset();
97 }
98
99 private:
100 std::string connection_id_;
101 std::shared_ptr<internal::websocket_socket> ws_socket_;
102 std::string remote_address_;
103 std::string path_;
104 mutable std::mutex socket_mutex_;
105 };
106
107 // ========================================================================
108 // ws_connection
109 // ========================================================================
110
111 ws_connection::ws_connection(std::shared_ptr<ws_connection_impl> impl) : pimpl_(std::move(impl)) {}
112
113 // ========================================================================
114 // i_websocket_session interface implementation
115 // ========================================================================
116
117 auto ws_connection::id() const -> std::string_view
118 {
119 return pimpl_->connection_id();
120 }
121
122 auto ws_connection::is_connected() const -> bool
123 {
124 return pimpl_->is_connected();
125 }
126
127 auto ws_connection::send(std::vector<uint8_t>&& data) -> VoidResult
128 {
129 return pimpl_->send_binary(std::move(data), nullptr);
130 }
131
132 auto ws_connection::close() -> void
133 {
134 pimpl_->close(internal::ws_close_code::normal, "");
135 }
136
138 {
139 return pimpl_->send_text(std::move(message), nullptr);
140 }
141
142 auto ws_connection::send_binary(std::vector<uint8_t>&& data) -> VoidResult
143 {
144 return pimpl_->send_binary(std::move(data), nullptr);
145 }
146
147 auto ws_connection::close(uint16_t code, std::string_view reason) -> void
148 {
149 pimpl_->close(static_cast<internal::ws_close_code>(code), std::string(reason));
150 }
151
152 auto ws_connection::path() const -> std::string_view
153 {
154 return pimpl_->path();
155 }
156
157 auto ws_connection::remote_endpoint() const -> std::string
158 {
159 return pimpl_->remote_endpoint();
160 }
161
162 // ========================================================================
163 // messaging_ws_server
164 // ========================================================================
165
167 : server_id_(server_id)
168 {
169 }
170
172 {
174 {
175 (void)stop_server();
176 }
177 }
178
179 // ========================================================================
180 // Lifecycle Management
181 // ========================================================================
182
184 {
185 config_ = config;
186 return start_server(config.port, config.path);
187 }
188
189 auto messaging_ws_server::start_server(uint16_t port, std::string_view path) -> VoidResult
190 {
191 if (lifecycle_.is_running())
192 {
193 return error_void(
195 "WebSocket server is already running",
196 "messaging_ws_server");
197 }
198
199 lifecycle_.set_running();
200
201 auto result = do_start_impl(port, path);
202 if (result.is_err())
203 {
204 lifecycle_.mark_stopped();
205 }
206
207 return result;
208 }
209
211 {
212 if (!lifecycle_.prepare_stop())
213 {
214 return ok();
215 }
216
217 auto result = do_stop_impl();
218
219 lifecycle_.mark_stopped();
220
221 return result;
222 }
223
224 auto messaging_ws_server::server_id() const -> const std::string&
225 {
226 return server_id_;
227 }
228
229 // ========================================================================
230 // i_network_component interface implementation
231 // ========================================================================
232
234 {
235 return lifecycle_.is_running();
236 }
237
239 {
240 lifecycle_.wait_for_stop();
241 }
242
243 // ========================================================================
244 // i_websocket_server interface implementation
245 // ========================================================================
246
248 {
249 return start_server(port);
250 }
251
253 {
254 return stop_server();
255 }
256
258 {
259 if (!session_mgr_)
260 {
261 return 0;
262 }
263 return session_mgr_->get_connection_count();
264 }
265
266 // ========================================================================
267 // Interface callback setters
268 // ========================================================================
269
272 {
273 // Adapt interface callback to internal callback type
274 if (callback) {
275 callbacks_.set<to_index(callback_index::connection)>(
276 [callback = std::move(callback)](std::shared_ptr<ws_connection> conn) {
277 // ws_connection already implements i_websocket_session
278 callback(conn);
279 });
280 } else {
281 callbacks_.set<to_index(callback_index::connection)>(connection_callback_t{});
282 }
283 }
284
287 {
288 // Adapt interface callback to internal callback type
289 if (callback) {
290 callbacks_.set<to_index(callback_index::disconnection)>(
291 [callback = std::move(callback)](const std::string& session_id,
293 const std::string& reason) {
294 callback(session_id, static_cast<uint16_t>(code), reason);
295 });
296 } else {
297 callbacks_.set<to_index(callback_index::disconnection)>(disconnection_callback_t{});
298 }
299 }
300
303 {
304 // Adapt interface callback to internal callback type
305 if (callback) {
306 callbacks_.set<to_index(callback_index::text_message)>(
307 [callback = std::move(callback)](std::shared_ptr<ws_connection> conn,
308 const std::string& message) {
309 callback(conn->id(), message);
310 });
311 } else {
312 callbacks_.set<to_index(callback_index::text_message)>(text_message_callback_t{});
313 }
314 }
315
318 {
319 // Adapt interface callback to internal callback type
320 if (callback) {
321 callbacks_.set<to_index(callback_index::binary_message)>(
322 [callback = std::move(callback)](std::shared_ptr<ws_connection> conn,
323 const std::vector<uint8_t>& data) {
324 callback(conn->id(), data);
325 });
326 } else {
327 callbacks_.set<to_index(callback_index::binary_message)>(binary_message_callback_t{});
328 }
329 }
330
333 {
334 // Interface and legacy types are compatible
335 if (callback) {
336 callbacks_.set<to_index(callback_index::error)>(
337 [callback = std::move(callback)](const std::string& session_id, std::error_code ec) {
338 callback(session_id, ec);
339 });
340 } else {
341 callbacks_.set<to_index(callback_index::error)>(error_callback_t{});
342 }
343 }
344
345 // ========================================================================
346 // Internal Implementation
347 // ========================================================================
348
349 auto messaging_ws_server::do_start_impl(uint16_t port, std::string_view path) -> VoidResult
350 {
351 try
352 {
353 // Store config if not already set
354 if (config_.port == 0 || config_.port != port)
355 {
356 config_.port = port;
357 config_.path = std::string(path);
358 }
359
360 // Create session manager
361 session_config sess_config;
362 sess_config.max_sessions = config_.max_connections;
363 session_mgr_ = std::make_shared<ws_session_manager>(sess_config);
364
365 // Create io_context and work guard
366 io_context_ = std::make_unique<asio::io_context>();
367 work_guard_ = std::make_unique<
368 asio::executor_work_guard<asio::io_context::executor_type>>(
369 asio::make_work_guard(*io_context_));
370
371 // Create acceptor
372 acceptor_ = std::make_unique<tcp::acceptor>(
373 *io_context_, tcp::endpoint(tcp::v4(), config_.port));
374
375 // Get thread pool from network context
377 if (!thread_pool_) {
378 // Fallback: create a temporary thread pool if network_context is not initialized
379 NETWORK_LOG_WARN("[messaging_ws_server] network_context not initialized, creating temporary thread pool");
380 thread_pool_ = std::make_shared<integration::basic_thread_pool>(std::thread::hardware_concurrency());
381 }
382
383 // Start io_context on thread pool
384 io_context_future_ = thread_pool_->submit([this]() {
385 try {
386 NETWORK_LOG_DEBUG("[messaging_ws_server] io_context started");
387 io_context_->run();
388 NETWORK_LOG_DEBUG("[messaging_ws_server] io_context stopped");
389 } catch (const std::exception& e) {
390 NETWORK_LOG_ERROR("[messaging_ws_server] Exception in io_context: " +
391 std::string(e.what()));
392 }
393 });
394
395 // Start accepting connections
396 do_accept();
397
398 NETWORK_LOG_INFO("[messaging_ws_server] Server started on port " +
399 std::to_string(config_.port) + " (ID: " +
400 server_id_ + ")");
401
402 return ok();
403 }
404 catch (const std::exception& e)
405 {
407 std::string("Failed to start server: ") + e.what());
408 }
409 }
410
412 {
413 try
414 {
415 // Close all connections
416 if (session_mgr_)
417 {
418 auto conns = session_mgr_->get_all_connections();
419 for (auto& conn : conns)
420 {
421 conn->close();
422 }
423 session_mgr_->clear_all_connections();
424 }
425
426 // Stop acceptor
427 // Lock to prevent race with do_accept() accessing the acceptor
428 {
429 std::lock_guard<std::mutex> lock(acceptor_mutex_);
430 if (acceptor_)
431 {
432 acceptor_->close();
433 }
434 }
435
436 // Stop io_context
437 work_guard_.reset();
438 if (io_context_)
439 {
440 io_context_->stop();
441 }
442
443 // Wait for io_context task to complete
444 if (io_context_future_.valid())
445 {
446 try {
447 io_context_future_.wait();
448 } catch (const std::exception& e) {
449 NETWORK_LOG_ERROR("[messaging_ws_server] Exception while waiting for io_context: " +
450 std::string(e.what()));
451 }
452 }
453
454 NETWORK_LOG_INFO("[messaging_ws_server] Server stopped (ID: " +
455 server_id_ + ")");
456
457 return ok();
458 }
459 catch (const std::exception& e)
460 {
462 std::string("Failed to stop server: ") + e.what());
463 }
464 }
465
466 auto messaging_ws_server::broadcast_text(const std::string& message) -> void
467 {
468 if (!session_mgr_)
469 {
470 return;
471 }
472
473 auto conns = session_mgr_->get_all_connections();
474 for (auto& conn : conns)
475 {
476 (void)conn->send_text(std::string(message));
477 }
478 }
479
480 auto messaging_ws_server::broadcast_binary(const std::vector<uint8_t>& data) -> void
481 {
482 if (!session_mgr_)
483 {
484 return;
485 }
486
487 auto conns = session_mgr_->get_all_connections();
488 for (auto& conn : conns)
489 {
490 (void)conn->send_binary(std::vector<uint8_t>(data));
491 }
492 }
493
494 auto messaging_ws_server::get_connection(const std::string& connection_id)
495 -> std::shared_ptr<ws_connection>
496 {
497 if (!session_mgr_)
498 {
499 return nullptr;
500 }
501 return session_mgr_->get_connection(connection_id);
502 }
503
504 auto messaging_ws_server::get_all_connections() -> std::vector<std::string>
505 {
506 if (!session_mgr_)
507 {
508 return {};
509 }
510 return session_mgr_->get_all_connection_ids();
511 }
512
514 {
515 // Lock to prevent race with do_stop_impl() closing the acceptor
516 std::lock_guard<std::mutex> lock(acceptor_mutex_);
517
518 // Early return if server is stopping or acceptor is invalid
519 if (!lifecycle_.is_running() || !acceptor_ || !acceptor_->is_open())
520 {
521 return;
522 }
523
524 auto socket = std::make_shared<tcp::socket>(*io_context_);
525
526 acceptor_->async_accept(*socket,
527 [this, socket](std::error_code ec)
528 {
529 if (!ec)
530 {
531 handle_new_connection(socket);
532 }
533 else
534 {
536 "[messaging_ws_server] Accept error: " +
537 ec.message());
538 }
539
540 // Continue accepting
541 if (lifecycle_.is_running())
542 {
543 do_accept();
544 }
545 });
546 }
547
548 auto messaging_ws_server::handle_new_connection(std::shared_ptr<tcp::socket> socket) -> void
549 {
550 // Check connection limit using session manager
551 if (!session_mgr_ || !session_mgr_->can_accept_connection())
552 {
553 NETWORK_LOG_WARN("[messaging_ws_server] Connection limit reached");
554 return;
555 }
556
557 // Generate connection ID
558 auto remote_ep = socket->remote_endpoint();
559 std::string conn_id = remote_ep.address().to_string() + ":" +
560 std::to_string(remote_ep.port());
561 std::string remote_addr = conn_id;
562
563 // Wrap in tcp_socket
564 auto tcp_sock = std::make_shared<internal::tcp_socket>(std::move(*socket));
565
566 // Wrap in websocket_socket
567 auto ws_socket =
568 std::make_shared<internal::websocket_socket>(tcp_sock, false);
569
570 // Create connection wrapper
571 auto conn_impl = std::make_shared<ws_connection_impl>(conn_id, ws_socket, remote_addr, config_.path);
572 auto conn = std::make_shared<ws_connection>(conn_impl);
573
574 // Set WebSocket callbacks
575 ws_socket->set_message_callback(
576 [this, conn](const internal::ws_message& msg)
577 { on_message(conn, msg); });
578
579 ws_socket->set_ping_callback(
580 [this, ws_socket](const std::vector<uint8_t>& payload)
581 {
582 if (config_.auto_pong)
583 {
584 ws_socket->async_send_ping(std::vector<uint8_t>(payload),
585 [](std::error_code) {});
586 }
587 });
588
589 ws_socket->set_close_callback(
590 [this, conn_id](internal::ws_close_code code,
591 const std::string& reason)
592 { on_close(conn_id, code, reason); });
593
594 ws_socket->set_error_callback(
595 [this, conn_id](std::error_code ec) { on_error(conn_id, ec); });
596
597 // Accept WebSocket handshake
598 ws_socket->async_accept(
599 [this, conn_id, conn](std::error_code ec)
600 {
601 if (ec)
602 {
603 NETWORK_LOG_ERROR("[messaging_ws_server] Handshake failed: " +
604 ec.message());
605 return;
606 }
607
608 // Add to session manager
609 auto added_id = session_mgr_->add_connection(conn, conn_id);
610 if (added_id.empty())
611 {
613 "[messaging_ws_server] Failed to add connection");
614 return;
615 }
616
617 NETWORK_LOG_INFO("[messaging_ws_server] Client connected: " +
618 conn_id);
619
620 // Start reading frames
621 auto ws_sock = conn->get_impl()->get_socket();
622 if (ws_sock)
623 {
624 ws_sock->start_read();
625 }
626
627 // Invoke connection callback
628 invoke_connection_callback(conn);
629 });
630 }
631
632 auto messaging_ws_server::on_message(std::shared_ptr<ws_connection> conn,
633 const internal::ws_message& msg) -> void
634 {
635 invoke_message_callback(conn, msg);
636 }
637
638 auto messaging_ws_server::on_close(const std::string& conn_id,
640 const std::string& reason) -> void
641 {
642 // Remove from session manager
643 if (session_mgr_)
644 {
645 auto conn = session_mgr_->get_connection(conn_id);
646 if (conn)
647 {
648 conn->get_impl()->invalidate();
649 }
650 session_mgr_->remove_connection(conn_id);
651 }
652
653 NETWORK_LOG_INFO("[messaging_ws_server] Client disconnected: " + conn_id);
654
655 invoke_disconnection_callback(conn_id, code, reason);
656 }
657
658 auto messaging_ws_server::on_error(const std::string& conn_id,
659 std::error_code ec) -> void
660 {
661 NETWORK_LOG_ERROR("[messaging_ws_server] Connection error (" + conn_id +
662 "): " + ec.message());
663 invoke_error_callback(conn_id, ec);
664 }
665
666 // ========================================================================
667 // Internal Callback Helpers
668 // ========================================================================
669
670 auto messaging_ws_server::invoke_connection_callback(std::shared_ptr<ws_connection> conn) -> void
671 {
672 callbacks_.invoke<to_index(callback_index::connection)>(conn);
673 }
674
677 const std::string& reason) -> void
678 {
679 callbacks_.invoke<to_index(callback_index::disconnection)>(conn_id, code, reason);
680 }
681
682 auto messaging_ws_server::invoke_message_callback(std::shared_ptr<ws_connection> conn,
683 const internal::ws_message& msg) -> void
684 {
685 callbacks_.invoke<to_index(callback_index::message)>(conn, msg);
686
687 if (msg.type == internal::ws_message_type::text)
688 {
689 callbacks_.invoke<to_index(callback_index::text_message)>(conn, msg.as_text());
690 }
691 else if (msg.type == internal::ws_message_type::binary)
692 {
693 callbacks_.invoke<to_index(callback_index::binary_message)>(conn, msg.as_binary());
694 }
695 }
696
697 auto messaging_ws_server::invoke_error_callback(const std::string& conn_id,
698 std::error_code ec) -> void
699 {
700 callbacks_.invoke<to_index(callback_index::error)>(conn_id, ec);
701 }
702
703} // namespace kcenon::network::core
auto do_accept() -> void
Starts accepting new connections.
auto set_binary_callback(interfaces::i_websocket_server::binary_callback_t callback) -> void override
Sets the callback for binary messages (interface version).
std::function< void(std::shared_ptr< ws_connection >)> connection_callback_t
Callback type for new connections.
~messaging_ws_server() noexcept override
Destructor. Automatically stops the server if still running.
auto broadcast_binary(const std::vector< uint8_t > &data) -> void
Broadcasts a binary message to all connected clients.
std::shared_ptr< ws_session_manager > session_mgr_
std::function< void(const std::string &, std::error_code)> error_callback_t
Callback type for errors.
auto set_disconnection_callback(interfaces::i_websocket_server::disconnection_callback_t callback) -> void override
Sets the callback for disconnections (interface version).
auto get_all_connections() -> std::vector< std::string >
Gets all connection IDs.
auto set_connection_callback(interfaces::i_websocket_server::connection_callback_t callback) -> void override
Sets the callback for new connections (interface version).
auto handle_new_connection(std::shared_ptr< asio::ip::tcp::socket > socket) -> void
Handles a new connection.
auto do_stop_impl() -> VoidResult
WebSocket-specific implementation of server stop.
auto wait_for_stop() -> void override
Blocks until stop() is called.
auto invoke_message_callback(std::shared_ptr< ws_connection > conn, const internal::ws_message &msg) -> void
Invokes the message callback.
auto invoke_error_callback(const std::string &conn_id, std::error_code ec) -> void
Invokes the error callback.
auto is_running() const -> bool override
Checks if the server is currently running.
auto set_error_callback(interfaces::i_websocket_server::error_callback_t callback) -> void override
Sets the callback for errors (interface version).
auto invoke_connection_callback(std::shared_ptr< ws_connection > conn) -> void
Invokes the connection callback.
auto stop_server() -> VoidResult
Stops the server and releases all resources.
auto do_start_impl(uint16_t port, std::string_view path) -> VoidResult
WebSocket-specific implementation of server start.
auto start(uint16_t port) -> VoidResult override
Starts the WebSocket server on the specified port.
auto start_server(const ws_server_config &config) -> VoidResult
Starts the server with full configuration.
auto connection_count() const -> size_t override
Gets the number of active WebSocket connections.
auto on_error(const std::string &conn_id, std::error_code ec) -> void
Handles errors.
auto server_id() const -> const std::string &
Returns the server identifier.
auto on_message(std::shared_ptr< ws_connection > conn, const internal::ws_message &msg) -> void
Handles received WebSocket messages.
std::function< void(std::shared_ptr< ws_connection >, const std::string &)> text_message_callback_t
Callback type for text messages.
auto invoke_disconnection_callback(const std::string &conn_id, internal::ws_close_code code, const std::string &reason) -> void
Invokes the disconnection callback.
messaging_ws_server(std::string_view server_id)
Constructs a WebSocket server.
auto set_text_callback(interfaces::i_websocket_server::text_callback_t callback) -> void override
Sets the callback for text messages (interface version).
std::function< void(std::shared_ptr< ws_connection >, const std::vector< uint8_t > &)> binary_message_callback_t
Callback type for binary messages.
auto get_connection(const std::string &connection_id) -> std::shared_ptr< ws_connection >
Gets a connection by ID.
auto broadcast_text(const std::string &message) -> void
Broadcasts a text message to all connected clients.
std::function< void(const std::string &, internal::ws_close_code, const std::string &)> disconnection_callback_t
Callback type for disconnections.
auto on_close(const std::string &conn_id, internal::ws_close_code code, const std::string &reason) -> void
Handles connection close.
auto stop() -> VoidResult override
Stops the WebSocket server.
std::shared_ptr< kcenon::network::integration::thread_pool_interface > get_thread_pool()
Get current thread pool.
static network_context & instance()
Get the singleton instance.
auto remote_endpoint() const -> std::string
auto send_binary(std::vector< uint8_t > &&data, std::function< void(std::error_code, std::size_t)> handler) -> VoidResult
ws_connection_impl(const std::string &conn_id, std::shared_ptr< internal::websocket_socket > ws_socket, const std::string &remote_addr, const std::string &ws_path="/")
auto get_socket() -> std::shared_ptr< internal::websocket_socket >
auto send_text(std::string &&message, std::function< void(std::error_code, std::size_t)> handler) -> VoidResult
auto close(internal::ws_close_code code, const std::string &reason) -> VoidResult
std::shared_ptr< internal::websocket_socket > ws_socket_
auto connection_id() const -> const std::string &
auto path() const -> std::string_view
auto is_connected() const -> bool override
Checks if the session is currently connected.
std::shared_ptr< ws_connection_impl > pimpl_
ws_connection(std::shared_ptr< class ws_connection_impl > impl)
auto send_text(std::string &&message) -> VoidResult override
Sends a text message to the client.
auto send_binary(std::vector< uint8_t > &&data) -> VoidResult override
Sends a binary message to the client.
auto path() const -> std::string_view override
Gets the requested path from the handshake.
auto close() -> void override
Closes the session.
auto id() const -> std::string_view override
Gets the unique identifier for this session.
auto send(std::vector< uint8_t > &&data) -> VoidResult override
Sends data to the client.
auto remote_endpoint() const -> std::string
Gets the remote endpoint address.
virtual std::future< void > submit(std::function< void()> task)=0
Submit a task to the thread pool.
std::function< void(std::string_view, const std::vector< uint8_t > &)> binary_callback_t
Callback type for binary messages (session_id, data)
std::function< void(std::string_view, std::error_code)> error_callback_t
Callback type for errors (session_id, error)
std::function< void(std::string_view, const std::string &)> text_callback_t
Callback type for text messages (session_id, message)
std::function< void( std::string_view session_id, uint16_t code, std::string_view reason)> disconnection_callback_t
Callback type for disconnections (with close code and reason)
std::function< void(std::shared_ptr< i_websocket_session >)> connection_callback_t
Callback type for new connections.
auto is_running() const -> bool
Checks if the component is currently running.
tracing_config config
Definition exporters.cpp:29
Logger system integration interface for network_system.
#define NETWORK_LOG_WARN(msg)
#define NETWORK_LOG_INFO(msg)
#define NETWORK_LOG_ERROR(msg)
#define NETWORK_LOG_DEBUG(msg)
@ text
Text message (UTF-8 encoded)
ws_close_code
WebSocket close status codes (RFC 6455 Section 7.4).
constexpr auto to_index(E e) noexcept -> std::size_t
Helper to convert enum to std::size_t for callback_manager access.
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()
Global context for shared network system resources.
Configuration for session management.
Configuration for WebSocket server.
Represents a complete WebSocket message.