Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
quic_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
7#define NETWORK_USE_EXPERIMENTAL
9
15
16#include <random>
17#include <sstream>
18
20{
21
23 : server_id_(server_id)
24{
25}
26
28{
30 {
31 (void)stop_server(); // Ignore result in destructor
32 }
33}
34
35auto messaging_quic_server::server_id() const -> const std::string&
36{
37 return server_id_;
38}
39
41{
42 return lifecycle_.is_running();
43}
44
46{
47 lifecycle_.wait_for_stop();
48}
49
51{
52 // Use default config with no TLS (for development/testing only)
53 return start_server(port, quic_server_config{});
54}
55
56auto messaging_quic_server::start_server(unsigned short port,
57 const quic_server_config& config)
58 -> VoidResult
59{
60 if (lifecycle_.is_running())
61 {
62 return error_void(
64 "QUIC server is already running",
65 "messaging_quic_server::start_server");
66 }
67
68 config_ = config;
69 lifecycle_.set_running();
70
71 auto result = do_start_impl(port);
72 if (result.is_err())
73 {
74 lifecycle_.mark_stopped();
75 }
76
77 return result;
78}
79
81{
82 if (!lifecycle_.is_running())
83 {
84 return error_void(
86 "QUIC server is not running",
87 "messaging_quic_server::stop_server");
88 }
89
90 if (!lifecycle_.prepare_stop())
91 {
92 return error_void(
94 "QUIC server is already stopping",
95 "messaging_quic_server::stop_server");
96 }
97
98 auto result = do_stop_impl();
99 lifecycle_.mark_stopped();
100
101 return result;
102}
103
105{
106 return start_server(port);
107}
108
110{
111 return stop_server();
112}
113
115{
116 return session_count();
117}
118
120{
121 try
122 {
123 // Create io_context
124 io_context_ = std::make_unique<asio::io_context>();
125 work_guard_ = std::make_unique<
126 asio::executor_work_guard<asio::io_context::executor_type>>(
127 asio::make_work_guard(*io_context_));
128
129 // Create UDP socket
130 udp_socket_ = std::make_unique<asio::ip::udp::socket>(
131 *io_context_,
132 asio::ip::udp::endpoint(asio::ip::udp::v4(), port));
133
134 // Create cleanup timer
135 cleanup_timer_ = std::make_unique<asio::steady_timer>(*io_context_);
136
137 // Start receiving packets
138 start_receive();
139
140 // Start periodic cleanup timer
141 start_cleanup_timer();
142
143 // Get thread pool from network context
145 if (!thread_pool_)
146 {
147 thread_pool_ = std::make_shared<integration::basic_thread_pool>(2);
148 }
149
150 // Submit io_context run task to thread pool
151 io_context_future_ = thread_pool_->submit(
152 [this]()
153 {
154 try
155 {
157 "[messaging_quic_server] Starting io_context on thread pool");
158 io_context_->run();
160 "[messaging_quic_server] io_context stopped");
161 }
162 catch (const std::exception& e)
163 {
165 "[messaging_quic_server] Exception in io_context: "
166 + std::string(e.what()));
167 }
168 });
169
170 NETWORK_LOG_INFO("[messaging_quic_server] Started listening on port "
171 + std::to_string(port));
172 return ok();
173 }
174 catch (const std::system_error& e)
175 {
176 if (e.code() == asio::error::address_in_use ||
177 e.code() == std::errc::address_in_use)
178 {
180 "Failed to bind to port: address already in use",
181 "messaging_quic_server::do_start_impl",
182 "Port: " + std::to_string(port));
183 }
184 else if (e.code() == asio::error::access_denied ||
185 e.code() == std::errc::permission_denied)
186 {
188 "Failed to bind to port: permission denied",
189 "messaging_quic_server::do_start_impl",
190 "Port: " + std::to_string(port));
191 }
192
194 "Failed to start server: " + std::string(e.what()),
195 "messaging_quic_server::do_start_impl",
196 "Port: " + std::to_string(port));
197 }
198 catch (const std::exception& e)
199 {
201 "Failed to start server: " + std::string(e.what()),
202 "messaging_quic_server::do_start_impl",
203 "Port: " + std::to_string(port));
204 }
205}
206
208{
209 try
210 {
211 // Cancel cleanup timer
212 if (cleanup_timer_)
213 {
214 cleanup_timer_->cancel();
215 }
216
217 // Close UDP socket
218 if (udp_socket_)
219 {
220 asio::error_code ec;
221 udp_socket_->cancel(ec);
222 if (udp_socket_->is_open())
223 {
224 udp_socket_->close(ec);
225 }
226 }
227
228 // Stop all sessions
229 disconnect_all(0);
230
231 // Release work guard
232 if (work_guard_)
233 {
234 work_guard_.reset();
235 }
236
237 // Stop io_context
238 if (io_context_)
239 {
240 io_context_->stop();
241 }
242
243 // Wait for io_context task
244 if (io_context_future_.valid())
245 {
246 io_context_future_.wait();
247 }
248
249 // Release resources
250 udp_socket_.reset();
251 cleanup_timer_.reset();
252 thread_pool_.reset();
253 io_context_.reset();
254
255 NETWORK_LOG_INFO("[messaging_quic_server] Stopped.");
256 return ok();
257 }
258 catch (const std::exception& e)
259 {
261 "Failed to stop server: " + std::string(e.what()),
262 "messaging_quic_server::do_stop_impl",
263 "Server ID: " + server_id_);
264 }
265}
266
268 -> std::vector<std::shared_ptr<session::quic_session>>
269{
270 std::shared_lock<std::shared_mutex> lock(sessions_mutex_);
271 std::vector<std::shared_ptr<session::quic_session>> result;
272 result.reserve(sessions_.size());
273 for (const auto& [id, session] : sessions_)
274 {
275 result.push_back(session);
276 }
277 return result;
278}
279
280auto messaging_quic_server::get_session(const std::string& session_id)
281 -> std::shared_ptr<session::quic_session>
282{
283 std::shared_lock<std::shared_mutex> lock(sessions_mutex_);
284 auto it = sessions_.find(session_id);
285 if (it != sessions_.end())
286 {
287 return it->second;
288 }
289 return nullptr;
290}
291
293{
294 std::shared_lock<std::shared_mutex> lock(sessions_mutex_);
295 return sessions_.size();
296}
297
298auto messaging_quic_server::disconnect_session(const std::string& session_id,
299 uint64_t error_code)
300 -> VoidResult
301{
302 std::shared_ptr<session::quic_session> session;
303 {
304 std::unique_lock<std::shared_mutex> lock(sessions_mutex_);
305 auto it = sessions_.find(session_id);
306 if (it == sessions_.end())
307 {
309 "Session not found",
310 "messaging_quic_server::disconnect_session",
311 "Session ID: " + session_id);
312 }
313 session = it->second;
314 sessions_.erase(it);
315 }
316
317 if (session)
318 {
319 return session->close(error_code);
320 }
321 return ok();
322}
323
324auto messaging_quic_server::disconnect_all(uint64_t error_code) -> void
325{
326 std::vector<std::shared_ptr<session::quic_session>> sessions_to_close;
327 {
328 std::unique_lock<std::shared_mutex> lock(sessions_mutex_);
329 sessions_to_close.reserve(sessions_.size());
330 for (auto& [id, session] : sessions_)
331 {
332 sessions_to_close.push_back(session);
333 }
334 sessions_.clear();
335 }
336
337 for (auto& session : sessions_to_close)
338 {
339 if (session)
340 {
341 auto result = session->close(error_code);
342 (void)result;
343 }
344 }
345}
346
347auto messaging_quic_server::broadcast(std::vector<uint8_t>&& data)
348 -> VoidResult
349{
350 auto sessions_list = sessions();
351 for (auto& session : sessions_list)
352 {
353 if (session && session->is_active())
354 {
355 std::vector<uint8_t> data_copy(data);
356 auto result = session->send(std::move(data_copy));
357 if (result.is_err())
358 {
359 NETWORK_LOG_WARN("[messaging_quic_server] Failed to send to session "
360 + session->session_id() + ": "
361 + result.error().message);
362 }
363 }
364 }
365 return ok();
366}
367
369 const std::vector<std::string>& session_ids,
370 std::vector<uint8_t>&& data) -> VoidResult
371{
372 for (const auto& session_id : session_ids)
373 {
374 auto session = get_session(session_id);
375 if (session && session->is_active())
376 {
377 std::vector<uint8_t> data_copy(data);
378 auto result = session->send(std::move(data_copy));
379 if (result.is_err())
380 {
381 NETWORK_LOG_WARN("[messaging_quic_server] Failed to send to session "
382 + session_id + ": " + result.error().message);
383 }
384 }
385 }
386 return ok();
387}
388
389#if KCENON_WITH_COMMON_SYSTEM
390auto messaging_quic_server::set_monitor(
391 kcenon::common::interfaces::IMonitor* monitor) -> void
392{
393 monitor_ = monitor;
394}
395
396auto messaging_quic_server::get_monitor() const
397 -> kcenon::common::interfaces::IMonitor*
398{
399 return monitor_;
400}
401#endif
402
404{
405 if (!is_running() || !udp_socket_)
406 {
407 return;
408 }
409
410 auto self = shared_from_this();
411 udp_socket_->async_receive_from(
412 asio::buffer(recv_buffer_),
413 recv_endpoint_,
414 [this, self](std::error_code ec, std::size_t bytes_received)
415 {
416 if (!is_running())
417 {
418 return;
419 }
420
421 if (ec)
422 {
423 if (ec != asio::error::operation_aborted)
424 {
426 "[messaging_quic_server] Receive error: " + ec.message());
427
428 invoke_error_callback(ec);
429 }
430 return;
431 }
432
433 // Handle the received packet
434 std::span<const uint8_t> packet_data(recv_buffer_.data(),
435 bytes_received);
436 handle_packet(packet_data, recv_endpoint_);
437
438 // Continue receiving
439 start_receive();
440 });
441}
442
443auto messaging_quic_server::handle_packet(std::span<const uint8_t> data,
444 const asio::ip::udp::endpoint& from)
445 -> void
446{
447 if (data.empty())
448 {
449 return;
450 }
451
452 // Parse packet header to get destination connection ID
453 auto header_result = protocols::quic::packet_parser::parse_header(data);
454 if (header_result.is_err())
455 {
456 NETWORK_LOG_DEBUG("[messaging_quic_server] Invalid packet from "
457 + from.address().to_string());
458 return;
459 }
460
461 // Extract destination connection ID from header
463 std::visit(
464 [&dcid](auto&& hdr)
465 {
466 dcid = hdr.dest_conn_id;
467 },
468 header_result.value().first);
469
470 // Find or create session for this connection
471 auto session = find_or_create_session(dcid, from);
472 if (!session)
473 {
475 "[messaging_quic_server] Could not find or create session for packet");
476 return;
477 }
478
479 // Delegate packet handling to session
480 session->handle_packet(data);
481}
482
485 const asio::ip::udp::endpoint& endpoint)
486 -> std::shared_ptr<session::quic_session>
487{
488 // First, check existing sessions
489 {
490 std::shared_lock<std::shared_mutex> lock(sessions_mutex_);
491 for (const auto& [id, session] : sessions_)
492 {
493 if (session->matches_connection_id(dcid))
494 {
495 return session;
496 }
497 }
498 }
499
500 // Check connection limit
501 if (session_count() >= config_.max_connections)
502 {
504 "[messaging_quic_server] Connection limit reached, rejecting new connection");
505 return nullptr;
506 }
507
508 // Create new session for this connection
509 auto session_id = generate_session_id();
510
511 // Create QUIC socket for the new session
512 asio::ip::udp::socket session_socket(
513 *io_context_, asio::ip::udp::endpoint(asio::ip::udp::v4(), 0));
514 session_socket.connect(endpoint);
515
516 auto quic_socket = std::make_shared<internal::quic_socket>(
517 std::move(session_socket), internal::quic_role::server);
518
519 // Accept the connection with TLS config
520 if (!config_.cert_file.empty() && !config_.key_file.empty())
521 {
522 auto accept_result =
523 quic_socket->accept(config_.cert_file, config_.key_file);
524 if (accept_result.is_err())
525 {
527 "[messaging_quic_server] Failed to accept connection: "
528 + accept_result.error().message);
529 return nullptr;
530 }
531 }
532
533 auto session =
534 std::make_shared<session::quic_session>(quic_socket, session_id);
535
536 // Set up session callbacks
537 auto self = weak_from_this();
538
539 session->set_receive_callback(
540 [self, session_id](const std::vector<uint8_t>& data)
541 {
542 if (auto server = self.lock())
543 {
544 auto sess = server->get_session(session_id);
545 if (sess)
546 {
547 server->invoke_receive_callback(sess, data);
548 }
549 }
550 });
551
552 session->set_stream_receive_callback(
553 [self, session_id](uint64_t stream_id,
554 const std::vector<uint8_t>& data,
555 bool fin)
556 {
557 if (auto server = self.lock())
558 {
559 auto sess = server->get_session(session_id);
560 if (sess)
561 {
562 server->invoke_stream_receive_callback(sess, stream_id, data, fin);
563 }
564 }
565 });
566
567 session->set_close_callback(
568 [self, session_id]()
569 {
570 if (auto server = self.lock())
571 {
572 server->on_session_close(session_id);
573 }
574 });
575
576 // Add session to map
577 {
578 std::unique_lock<std::shared_mutex> lock(sessions_mutex_);
579 sessions_[session_id] = session;
580 }
581
582 // Start the session
583 session->start_session();
584
585 // Report metrics
588
589 // Invoke connection callback
590 invoke_connection_callback(session);
591
592#if KCENON_WITH_COMMON_SYSTEM
593 if (monitor_)
594 {
595 monitor_->record_metric("active_connections",
596 static_cast<double>(session_count()));
597 }
598#endif
599
600 NETWORK_LOG_INFO("[messaging_quic_server] New session created: "
601 + session_id + " from " + endpoint.address().to_string());
602
603 return session;
604}
605
607{
608 auto counter = session_counter_.fetch_add(1);
609 std::ostringstream oss;
610 oss << server_id_ << "-" << counter;
611 return oss.str();
612}
613
614auto messaging_quic_server::on_session_close(const std::string& session_id)
615 -> void
616{
617 std::shared_ptr<session::quic_session> session;
618 {
619 std::unique_lock<std::shared_mutex> lock(sessions_mutex_);
620 auto it = sessions_.find(session_id);
621 if (it != sessions_.end())
622 {
623 session = it->second;
624 sessions_.erase(it);
625 }
626 }
627
628 if (session)
629 {
630 // Report metrics
632
633 // Invoke disconnection callback
634 invoke_disconnection_callback(session);
635
636#if KCENON_WITH_COMMON_SYSTEM
637 if (monitor_)
638 {
639 monitor_->record_metric("active_connections",
640 static_cast<double>(session_count()));
641 }
642#endif
643
644 NETWORK_LOG_INFO("[messaging_quic_server] Session closed: "
645 + session_id);
646 }
647}
648
650{
651 if (!cleanup_timer_ || !is_running())
652 {
653 return;
654 }
655
656 // Schedule cleanup every 30 seconds
657 cleanup_timer_->expires_after(std::chrono::seconds(30));
658
659 auto self = shared_from_this();
660 cleanup_timer_->async_wait(
661 [this, self](const std::error_code& ec)
662 {
663 if (!ec && is_running())
664 {
665 cleanup_dead_sessions();
666 start_cleanup_timer(); // Reschedule
667 }
668 });
669}
670
672{
673 std::vector<std::string> dead_session_ids;
674
675 {
676 std::shared_lock<std::shared_mutex> lock(sessions_mutex_);
677 for (const auto& [id, session] : sessions_)
678 {
679 if (!session || !session->is_active())
680 {
681 dead_session_ids.push_back(id);
682 }
683 }
684 }
685
686 for (const auto& id : dead_session_ids)
687 {
688 on_session_close(id);
689 }
690
691 if (!dead_session_ids.empty())
692 {
694 "[messaging_quic_server] Cleaned up "
695 + std::to_string(dead_session_ids.size())
696 + " dead sessions. Active: " + std::to_string(session_count()));
697 }
698}
699
700// =============================================================================
701// Callback invocation helpers
702// =============================================================================
703
705 std::shared_ptr<session::quic_session> session) -> void
706{
707 callbacks_.invoke<to_index(callback_index::connection)>(session);
708}
709
711 std::shared_ptr<session::quic_session> session) -> void
712{
713 callbacks_.invoke<to_index(callback_index::disconnection)>(session);
714}
715
717 std::shared_ptr<session::quic_session> session,
718 const std::vector<uint8_t>& data) -> void
719{
720 callbacks_.invoke<to_index(callback_index::receive)>(session, data);
721}
722
724 std::shared_ptr<session::quic_session> session,
725 uint64_t stream_id,
726 const std::vector<uint8_t>& data,
727 bool fin) -> void
728{
729 callbacks_.invoke<to_index(callback_index::stream_receive)>(session, stream_id, data, fin);
730}
731
732auto messaging_quic_server::invoke_error_callback(std::error_code ec) -> void
733{
734 callbacks_.invoke<to_index(callback_index::error)>(ec);
735}
736
737// =============================================================================
738// Legacy callback setters
739// =============================================================================
740
742{
743 callbacks_.set<to_index(callback_index::connection)>(std::move(callback));
744}
745
747{
748 callbacks_.set<to_index(callback_index::disconnection)>(std::move(callback));
749}
750
752{
753 callbacks_.set<to_index(callback_index::receive)>(std::move(callback));
754}
755
757{
758 callbacks_.set<to_index(callback_index::stream_receive)>(std::move(callback));
759}
760
762{
763 callbacks_.set<to_index(callback_index::error)>(std::move(callback));
764}
765
766// =============================================================================
767// i_quic_server interface callback implementations
768// =============================================================================
769
772{
773 // Convert callback to internal type (quic_session to i_quic_session)
774 set_connection_callback(
775 [cb = std::move(callback)](std::shared_ptr<session::quic_session> session) {
776 if (cb && session)
777 {
778 // quic_session implements i_quic_session, so we can pass it directly
779 cb(session);
780 }
781 });
782}
783
786{
787 set_disconnection_callback(
788 [cb = std::move(callback)](std::shared_ptr<session::quic_session> session) {
789 if (cb && session)
790 {
791 cb(session->session_id());
792 }
793 });
794}
795
798{
799 set_receive_callback(
800 [cb = std::move(callback)](std::shared_ptr<session::quic_session> session,
801 const std::vector<uint8_t>& data) {
802 if (cb && session)
803 {
804 cb(session->session_id(), data);
805 }
806 });
807}
808
811{
812 set_stream_receive_callback(
813 [cb = std::move(callback)](std::shared_ptr<session::quic_session> session,
814 uint64_t stream_id,
815 const std::vector<uint8_t>& data,
816 bool fin) {
817 if (cb && session)
818 {
819 cb(session->session_id(), stream_id, data, fin);
820 }
821 });
822}
823
826{
827 // Store the interface callback and wrap it for base class
828 // Note: Base class error_callback_t only takes error_code, not session
829 // We need to store the interface callback separately and invoke it from
830 // the receive side where we have session context
831 interface_error_cb_ = std::move(callback);
832}
833
834} // namespace kcenon::network::core
auto invoke_error_callback(std::error_code ec) -> void
Invokes the error callback.
auto is_running() const -> bool override
Checks if the server is currently running.
std::function< void(std::error_code)> error_callback_t
Callback type for errors.
auto on_session_close(const std::string &session_id) -> void
auto stop_server() -> VoidResult
Stops the server and releases all resources.
auto disconnect_all(uint64_t error_code=0) -> void
Disconnect all active sessions.
messaging_quic_server(std::string_view server_id)
Constructs a QUIC server with a given identifier.
auto sessions() const -> std::vector< std::shared_ptr< session::quic_session > >
Get all active sessions.
auto stop() -> VoidResult override
Stops the QUIC server.
auto disconnect_session(const std::string &session_id, uint64_t error_code=0) -> VoidResult
Disconnect a specific session.
auto broadcast(std::vector< uint8_t > &&data) -> VoidResult
Send data to all connected clients.
auto handle_packet(std::span< const uint8_t > data, const asio::ip::udp::endpoint &from) -> void
auto invoke_connection_callback(std::shared_ptr< session::quic_session > session) -> void
Invokes the connection callback.
auto start(uint16_t port) -> VoidResult override
Starts the QUIC server on the specified port.
auto set_stream_receive_callback(stream_receive_callback_t callback) -> void
Sets the callback for stream data reception (legacy version).
auto set_receive_callback(interfaces::i_quic_server::receive_callback_t callback) -> void override
Sets the callback for received data on default stream (interface version).
auto set_connection_callback(interfaces::i_quic_server::connection_callback_t callback) -> void override
Sets the callback for new connections (interface version).
auto session_count() const -> size_t
Get the number of active sessions.
auto start_server(unsigned short port) -> VoidResult
Start the server with default configuration.
std::function< void(std::shared_ptr< session::quic_session >, uint64_t, const std::vector< uint8_t > &, bool)> stream_receive_callback_t
Callback type for stream data (session, stream_id, data, fin)
auto do_start_impl(unsigned short port) -> VoidResult
QUIC-specific implementation of server start.
auto invoke_receive_callback(std::shared_ptr< session::quic_session > session, const std::vector< uint8_t > &data) -> void
Invokes the receive callback.
std::function< void(std::shared_ptr< session::quic_session >, const std::vector< uint8_t > &)> receive_callback_t
Callback type for received data (session, data)
auto invoke_disconnection_callback(std::shared_ptr< session::quic_session > session) -> void
Invokes the disconnection callback.
std::function< void(std::shared_ptr< session::quic_session >)> disconnection_callback_t
Callback type for disconnections.
auto wait_for_stop() -> void override
Blocks until stop() is called.
std::map< std::string, std::shared_ptr< session::quic_session > > sessions_
auto get_session(const std::string &session_id) -> std::shared_ptr< session::quic_session >
Get a session by its ID.
~messaging_quic_server() noexcept override
Destructor; automatically calls stop_server() if running.
auto set_disconnection_callback(interfaces::i_quic_server::disconnection_callback_t callback) -> void override
Sets the callback for disconnections (interface version).
auto connection_count() const -> size_t override
Gets the number of active QUIC connections (interface version).
auto server_id() const -> const std::string &
Returns the server identifier.
auto do_stop_impl() -> VoidResult
QUIC-specific implementation of server stop.
auto find_or_create_session(const protocols::quic::connection_id &dcid, const asio::ip::udp::endpoint &endpoint) -> std::shared_ptr< session::quic_session >
auto invoke_stream_receive_callback(std::shared_ptr< session::quic_session > session, uint64_t stream_id, const std::vector< uint8_t > &data, bool fin) -> void
Invokes the stream receive callback.
auto set_stream_callback(interfaces::i_quic_server::stream_callback_t callback) -> void override
Sets the callback for stream data (interface version).
auto set_error_callback(interfaces::i_quic_server::error_callback_t callback) -> void override
Sets the callback for errors (interface version).
auto multicast(const std::vector< std::string > &session_ids, std::vector< uint8_t > &&data) -> VoidResult
Send data to specific sessions.
std::function< void(std::shared_ptr< session::quic_session >)> connection_callback_t
Callback type for new connections.
std::shared_ptr< kcenon::network::integration::thread_pool_interface > get_thread_pool()
Get current thread pool.
static network_context & instance()
Get the singleton instance.
virtual std::future< void > submit(std::function< void()> task)=0
Submit a task to the thread pool.
std::function< void( std::string_view, uint64_t, const std::vector< uint8_t > &, bool)> stream_callback_t
Callback type for stream data (session_id, stream_id, data, is_fin)
std::function< void(std::string_view, const std::vector< uint8_t > &)> receive_callback_t
Callback type for default stream data (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::shared_ptr< i_quic_session >)> connection_callback_t
Callback type for new connections.
std::function< void(std::string_view)> disconnection_callback_t
Callback type for disconnections (session_id)
static void report_connection_accepted()
Report a new connection accepted.
static void report_active_connections(size_t count)
Report active connections count.
QUIC Connection ID (RFC 9000 Section 5.1)
static auto parse_header(std::span< const uint8_t > data) -> Result< std::pair< packet_header, size_t > >
Parse a packet header (without header protection removal)
Definition packet.cpp:168
auto is_running() const -> bool
Checks if the component is currently running.
Feature flags for network_system.
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)
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()
Network system metrics definitions and reporting utilities.
QUIC-specific session with stream multiplexing.
Configuration options for QUIC server.
Definition quic_server.h:63