15using tcp = asio::ip::tcp;
19 : client_id_(client_id),
20 verify_cert_(verify_cert) {
23 std::make_unique<asio::ssl::context>(asio::ssl::context::tls_client);
27 asio::ssl::context::default_workarounds | asio::ssl::context::no_sslv2 |
28 asio::ssl::context::no_sslv3 | asio::ssl::context::no_tlsv1 |
29 asio::ssl::context::no_tlsv1_1);
36 SSL_CTX_set_min_proto_version(native_ctx, TLS1_3_VERSION);
37 SSL_CTX_set_max_proto_version(native_ctx, TLS1_3_VERSION);
40 SSL_CTX_set_ciphersuites(native_ctx,
41 "TLS_AES_256_GCM_SHA384:"
42 "TLS_CHACHA20_POLY1305_SHA256:"
43 "TLS_AES_128_GCM_SHA256");
54 "TLS 1.3 only and certificate verification");
60 "TLS 1.3 only WITHOUT certificate verification");
76 if (lifecycle_.is_running()) {
79 "Client is already running",
80 "secure_messaging_client::start_client");
86 "Host cannot be empty",
87 "secure_messaging_client::start_client");
90 if (!lifecycle_.try_start()) {
93 "Client is already starting",
94 "secure_messaging_client::start_client");
97 is_connected_.store(
false, std::memory_order_release);
100 auto result = do_start_impl(host, port);
101 if (result.is_err()) {
102 lifecycle_.mark_stopped();
109 if (!lifecycle_.is_running()) {
113 if (!lifecycle_.prepare_stop()) {
117 is_connected_.store(
false, std::memory_order_release);
120 auto result = do_stop_impl();
123 lifecycle_.mark_stopped();
126 invoke_disconnected_callback();
132 lifecycle_.wait_for_stop();
152 if (!is_connected_.load(std::memory_order_acquire)) {
156 "secure_messaging_client::send_packet");
162 "Data cannot be empty",
163 "secure_messaging_client::send_packet");
166 return do_send_impl(std::move(data));
174 callbacks_.set<
to_index(callback_index::receive)>(std::move(callback));
178 callbacks_.set<
to_index(callback_index::connected)>(std::move(callback));
182 callbacks_.set<
to_index(callback_index::disconnected)>(std::move(callback));
186 callbacks_.set<
to_index(callback_index::error)>(std::move(callback));
194 is_connected_.store(
connected, std::memory_order_release);
198 callbacks_.invoke<
to_index(callback_index::receive)>(data);
202 callbacks_.invoke<
to_index(callback_index::connected)>();
206 callbacks_.invoke<
to_index(callback_index::disconnected)>();
210 callbacks_.invoke<
to_index(callback_index::error)>(ec);
221 io_context_ = std::make_unique<asio::io_context>();
224 work_guard_ = std::make_unique<
225 asio::executor_work_guard<asio::io_context::executor_type>>(
226 asio::make_work_guard(*io_context_));
229 tcp::resolver resolver(*io_context_);
230 auto endpoints = resolver.resolve(std::string(host), std::to_string(port));
233 tcp::socket tcp_sock(*io_context_);
236 std::error_code connect_ec;
237 asio::connect(tcp_sock, endpoints, connect_ec);
241 "Failed to connect to server: " + connect_ec.message(),
242 "secure_messaging_client::do_start_impl",
243 "Host: " + std::string(host) +
244 ", Port: " + std::to_string(port));
248 socket_ = std::make_shared<internal::secure_tcp_socket>(std::move(tcp_sock),
252 auto self = shared_from_this();
253 socket_->set_receive_callback(
254 [
this, self](
const std::vector<uint8_t> &data) { on_receive(data); });
255 socket_->set_error_callback(
256 [
this, self](std::error_code ec) { on_error(ec); });
259 std::promise<std::error_code> handshake_promise;
260 auto handshake_future = handshake_promise.get_future();
262 socket_->async_handshake(asio::ssl::stream_base::client,
263 [&handshake_promise](std::error_code ec) {
264 handshake_promise.set_value(ec);
273 "[secure_messaging_client::" + client_id_ +
274 "] network_context not initialized, creating temporary thread pool");
275 thread_pool_ = std::make_shared<integration::basic_thread_pool>(
276 std::thread::hardware_concurrency());
280 io_context_future_ = thread_pool_->
submit([
this]() {
283 "] io_context started");
286 "] io_context stopped");
287 }
catch (
const std::exception &e) {
289 "[secure_messaging_client::" + client_id_ +
290 "] Exception in io_context: " + std::string(e.what()));
295 auto status = handshake_future.wait_for(std::chrono::seconds(10));
297 if (status == std::future_status::timeout) {
299 "SSL handshake timeout",
300 "secure_messaging_client::do_start_impl",
301 "Host: " + std::string(host) +
302 ", Port: " + std::to_string(port));
305 auto handshake_ec = handshake_future.get();
308 "SSL handshake failed: " + handshake_ec.message(),
309 "secure_messaging_client::do_start_impl",
310 "Host: " + std::string(host) +
311 ", Port: " + std::to_string(port));
315 socket_->start_read();
321 std::string(host) +
":" + std::to_string(port) +
322 " (TLS/SSL secured)");
325 invoke_connected_callback();
328 }
catch (
const std::system_error &e) {
330 "Failed to connect: " + std::string(e.what()),
331 "secure_messaging_client::do_start_impl",
332 "Host: " + std::string(host) +
333 ", Port: " + std::to_string(port));
334 }
catch (
const std::exception &e) {
336 "Failed to connect: " + std::string(e.what()),
337 "secure_messaging_client::do_start_impl",
338 "Host: " + std::string(host) +
339 ", Port: " + std::to_string(port));
362 if (io_context_future_.valid()) {
364 io_context_future_.wait();
365 }
catch (
const std::exception &e) {
367 "] Exception while waiting for io_context: " +
368 std::string(e.what()));
374 thread_pool_.reset();
379 }
catch (
const std::exception &e) {
381 "Failed to stop client: " + std::string(e.what()),
382 "secure_messaging_client::do_stop_impl",
383 "Client ID: " + client_id_);
392 "secure_messaging_client::do_send_impl",
"Client ID: " + client_id_);
397 std::move(data), [
this](std::error_code ec, std::size_t bytes_transferred) {
403 std::to_string(bytes_transferred) +
" bytes");
416 if (!is_connected()) {
421 std::to_string(data.size()) +
" bytes.");
424 invoke_receive_callback(data);
431 invoke_error_callback(ec);
434 if (is_connected()) {
435 invoke_disconnected_callback();
439 set_connected(
false);
std::shared_ptr< kcenon::network::integration::thread_pool_interface > get_thread_pool()
Get current thread pool.
static network_context & instance()
Get the singleton instance.
std::function< void()> connected_callback_t
Callback type for connection established.
utils::lifecycle_manager lifecycle_
~secure_messaging_client() noexcept
Destructor. Calls stop_client() if still connected.
auto on_receive(const std::vector< uint8_t > &data) -> void
Callback for when encrypted data arrives from the server.
auto send_packet(std::vector< uint8_t > &&data) -> VoidResult
Sends data to the connected server.
auto invoke_disconnected_callback() -> void
Invokes the disconnected callback.
auto client_id() const -> const std::string &
Returns the client identifier.
auto invoke_connected_callback() -> void
Invokes the connected callback.
auto set_receive_callback(receive_callback_t callback) -> void
Sets the callback for received data.
auto set_connected_callback(connected_callback_t callback) -> void
Sets the callback for connection established.
auto on_error(std::error_code ec) -> void
Callback for handling socket errors.
auto stop_client() -> VoidResult
Stops the client and disconnects from the server.
auto do_stop_impl() -> VoidResult
Secure TCP-specific implementation of client stop.
std::function< void(std::error_code)> error_callback_t
Callback type for errors.
auto do_send_impl(std::vector< uint8_t > &&data) -> VoidResult
Secure TCP-specific implementation of data send.
auto invoke_receive_callback(const std::vector< uint8_t > &data) -> void
Invokes the receive callback.
auto wait_for_stop() -> void
Blocks until stop_client() is called.
secure_messaging_client(std::string_view client_id, bool verify_cert=true)
Constructs a secure messaging client.
std::atomic< bool > is_connected_
auto is_running() const noexcept -> bool
Checks if the client is currently running.
auto set_error_callback(error_callback_t callback) -> void
Sets the callback for errors.
std::function< void(const std::vector< uint8_t > &)> receive_callback_t
Callback type for received data.
auto start_client(std::string_view host, unsigned short port) -> VoidResult
Starts the client and connects to the specified host and port.
auto set_connected(bool connected) -> void
Sets the connected state.
std::function< void()> disconnected_callback_t
Callback type for disconnection.
std::unique_ptr< asio::ssl::context > ssl_context_
auto is_connected() const noexcept -> bool
Checks if the client is connected to the server.
auto invoke_error_callback(std::error_code ec) -> void
Invokes the error callback.
auto do_start_impl(std::string_view host, unsigned short port) -> VoidResult
Secure TCP-specific implementation of client start.
auto set_disconnected_callback(disconnected_callback_t callback) -> void
Sets the callback for disconnection.
virtual std::future< void > submit(std::function< void()> task)=0
Submit a task to the thread pool.
auto is_running() const -> bool
Checks if the component is currently running.
struct ssl_ctx_st SSL_CTX
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 int invalid_argument
constexpr int internal_error
constexpr int already_exists
constexpr int send_failed
constexpr int connection_closed
constexpr int connection_timeout
constexpr int connection_failed
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="")
Global context for shared network system resources.
Secure TCP client class with TLS/SSL support.