15 : _id(CppCommon::UUID::Sequential()),
17 _io_context(_service->GetAsioContext()),
18 _strand(*_io_context),
19 _strand_required(_service->IsStrandRequired()),
23 _stream(*_io_context, *_context),
36 _send_buffer_flush_offset(0),
37 _option_keep_alive(false),
38 _option_no_delay(false)
40 assert((
service !=
nullptr) &&
"Asio service is invalid!");
42 throw CppCommon::ArgumentException(
"Asio service is invalid!");
44 assert((
context !=
nullptr) &&
"SSL context is invalid!");
46 throw CppCommon::ArgumentException(
"SSL context is invalid!");
50 : _id(CppCommon::UUID::Sequential()),
52 _io_context(_service->GetAsioContext()),
53 _strand(*_io_context),
54 _strand_required(_service->IsStrandRequired()),
59 _stream(*_io_context, *_context),
72 _send_buffer_flush_offset(0),
73 _option_keep_alive(false),
74 _option_no_delay(false)
76 assert((
service !=
nullptr) &&
"Asio service is invalid!");
78 throw CppCommon::ArgumentException(
"Asio service is invalid!");
80 assert((
context !=
nullptr) &&
"SSL context is invalid!");
82 throw CppCommon::ArgumentException(
"SSL context is invalid!");
86 : _id(CppCommon::UUID::Sequential()),
88 _io_context(_service->GetAsioContext()),
89 _strand(*_io_context),
90 _strand_required(_service->IsStrandRequired()),
95 _stream(*_io_context, *_context),
108 _send_buffer_flush_offset(0),
109 _option_keep_alive(false),
110 _option_no_delay(false)
112 assert((
service !=
nullptr) &&
"Asio service is invalid!");
114 throw CppCommon::ArgumentException(
"Asio service is invalid!");
116 assert((
context !=
nullptr) &&
"SSL context is invalid!");
118 throw CppCommon::ArgumentException(
"SSL context is invalid!");
127 asio::socket_base::receive_buffer_size option;
128 _stream.next_layer().get_option(option);
129 return option.value();
134 asio::socket_base::send_buffer_size option;
135 _stream.next_layer().get_option(option);
136 return option.value();
141 asio::socket_base::receive_buffer_size option((
int)size);
142 socket().set_option(option);
147 asio::socket_base::send_buffer_size option((
int)size);
148 socket().set_option(option);
157 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_context, *_context);
162 _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (
unsigned short)_port);
171 socket().connect(_endpoint, ec);
189 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
192 socket().set_option(asio::ip::tcp::no_delay(
true));
215 _stream.handshake(asio::ssl::stream_base::client, ec);
233 if (_send_buffer_main.empty())
245 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_context, *_context);
250 auto endpoints = resolver->resolver().resolve(_address, (_scheme.empty() ? std::to_string(_port) : _scheme), ec);
270 _endpoint = asio::connect(
socket(), endpoints, ec);
288 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
291 socket().set_option(asio::ip::tcp::no_delay(
true));
314 _stream.handshake(asio::ssl::stream_base::client, ec);
332 if (_send_buffer_main.empty())
338bool SSLClient::DisconnectInternal()
340 if (!
IsConnected() || _resolving || _connecting || _handshaking || _shutting)
343 auto self(this->shared_from_this());
352 _handshaking =
false;
388 auto self(this->shared_from_this());
395 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_context, *_context);
398 _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (
unsigned short)_port);
407 auto async_connect_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec1)
419 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
422 socket().set_option(asio::ip::tcp::no_delay(
true));
448 auto async_handshake_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec2)
451 _handshaking =
false;
468 if (_send_buffer_main.empty())
475 DisconnectInternalAsync(
true);
478 if (_strand_required)
479 _stream.async_handshake(asio::ssl::stream_base::client, bind_executor(_strand, async_handshake_handler));
481 _stream.async_handshake(asio::ssl::stream_base::client, async_handshake_handler);
491 if (_strand_required)
492 socket().async_connect(_endpoint, bind_executor(_strand, async_connect_handler));
494 socket().async_connect(_endpoint, async_connect_handler);
496 if (_strand_required)
497 asio::post(_strand, connect_handler);
499 asio::post(*_io_context, connect_handler);
510 auto self(this->shared_from_this());
519 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_context, *_context);
522 auto async_resolve_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec1, asio::ip::tcp::resolver::results_type endpoints)
538 auto async_connect_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec2,
const asio::ip::tcp::endpoint&
endpoint)
553 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
556 socket().set_option(asio::ip::tcp::no_delay(
true));
582 auto async_handshake_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec3)
585 _handshaking =
false;
602 if (_send_buffer_main.empty())
609 DisconnectInternalAsync(
true);
612 if (_strand_required)
613 _stream.async_handshake(asio::ssl::stream_base::client, bind_executor(_strand, async_handshake_handler));
615 _stream.async_handshake(asio::ssl::stream_base::client, async_handshake_handler);
625 if (_strand_required)
626 asio::async_connect(
socket(), endpoints, bind_executor(_strand, async_connect_handler));
628 asio::async_connect(
socket(), endpoints, async_connect_handler);
640 if (_strand_required)
641 resolver->resolver().async_resolve(_address, (_scheme.empty() ? std::to_string(_port) : _scheme), bind_executor(_strand, async_resolve_handler));
643 resolver->resolver().async_resolve(_address, (_scheme.empty() ? std::to_string(_port) : _scheme), async_resolve_handler);
645 if (_strand_required)
646 asio::post(_strand, connect_handler);
648 asio::post(*_io_context, connect_handler);
653bool SSLClient::DisconnectInternalAsync(
bool dispatch)
655 if (!
IsConnected() || _resolving || _connecting || _handshaking || _shutting)
659 auto self(this->shared_from_this());
662 if (!
IsConnected() || _resolving || _connecting || _handshaking || _shutting)
673 auto async_shutdown_handler =
make_alloc_handler(_connect_storage, [
this, self](std::error_code ec2)
677 DisconnectInternal();
679 if (_strand_required)
680 _stream.async_shutdown(bind_executor(_strand, async_shutdown_handler));
682 _stream.async_shutdown(async_shutdown_handler);
684 if (_strand_required)
687 asio::dispatch(_strand, disconnect_handler);
689 asio::post(_strand, disconnect_handler);
694 asio::dispatch(*_io_context, disconnect_handler);
696 asio::post(*_io_context, disconnect_handler);
708 CppCommon::Thread::Yield();
721 assert((buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
722 if (buffer ==
nullptr)
728 size_t sent = asio::write(_stream, asio::buffer(buffer, size), ec);
748size_t SSLClient::Send(
const void* buffer,
size_t size,
const CppCommon::Timespan& timeout)
756 assert((buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
757 if (buffer ==
nullptr)
762 std::condition_variable cv;
763 asio::error_code error;
764 asio::system_timer timer(_stream.get_executor());
767 auto async_done_handler = [&](asio::error_code ec)
769 std::unique_lock<std::mutex> lck(mtx);
780 timer.expires_after(timeout.chrono());
781 timer.async_wait([&](
const asio::error_code& ec) { async_done_handler(ec ? ec : asio::error::timed_out); });
785 _stream.async_write_some(asio::buffer(buffer, size), [&](std::error_code ec,
size_t write) { async_done_handler(ec); sent = write; });
788 std::unique_lock<std::mutex> lck(mtx);
789 cv.wait(lck, [&]() {
return done == 2; });
802 if (error && (error != asio::error::timed_out))
819 assert((buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
820 if (buffer ==
nullptr)
824 std::scoped_lock locker(_send_lock);
827 bool send_required = _send_buffer_main.empty() || _send_buffer_flush.empty();
830 if (((_send_buffer_main.size() + size) > _send_buffer_limit) && (_send_buffer_limit > 0))
832 SendError(asio::error::no_buffer_space);
837 const uint8_t* bytes = (
const uint8_t*)buffer;
838 _send_buffer_main.insert(_send_buffer_main.end(), bytes, bytes + size);
841 _bytes_pending = _send_buffer_main.size();
849 auto self(this->shared_from_this());
850 auto send_handler = [
this, self]()
855 if (_strand_required)
856 asio::dispatch(_strand, send_handler);
858 asio::dispatch(*_io_context, send_handler);
871 assert((buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
872 if (buffer ==
nullptr)
878 size_t received = _stream.read_some(asio::buffer(buffer, size), ec);
882 _bytes_received += received;
900 std::string text(size, 0);
901 text.resize(
Receive(text.data(), text.size()));
913 assert((buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
914 if (buffer ==
nullptr)
919 std::condition_variable cv;
920 asio::error_code error;
921 asio::system_timer timer(_stream.get_executor());
924 auto async_done_handler = [&](asio::error_code ec)
926 std::unique_lock<std::mutex> lck(mtx);
937 timer.expires_after(timeout.chrono());
938 timer.async_wait([&](
const asio::error_code& ec) { async_done_handler(ec ? ec : asio::error::timed_out); });
942 _stream.async_read_some(asio::buffer(buffer, size), [&](std::error_code ec,
size_t read) { async_done_handler(ec); received = read; });
945 std::unique_lock<std::mutex> lck(mtx);
946 cv.wait(lck, [&]() {
return done == 2; });
952 _bytes_received += received;
959 if (error && (error != asio::error::timed_out))
970 std::string text(size, 0);
971 text.resize(
Receive(text.data(), text.size(), timeout));
981void SSLClient::TryReceive()
991 auto self(this->shared_from_this());
992 auto async_receive_handler =
make_alloc_handler(_receive_storage, [
this, self](std::error_code ec,
size_t size)
1003 _bytes_received += size;
1009 if (_receive_buffer.size() == size)
1012 if (((2 * size) > _receive_buffer_limit) && (_receive_buffer_limit > 0))
1014 SendError(asio::error::no_buffer_space);
1015 DisconnectInternalAsync(
true);
1019 _receive_buffer.resize(2 * size);
1029 DisconnectInternalAsync(
true);
1032 if (_strand_required)
1033 _stream.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), bind_executor(_strand, async_receive_handler));
1035 _stream.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), async_receive_handler);
1038void SSLClient::TrySend()
1047 if (_send_buffer_flush.empty())
1049 std::scoped_lock locker(_send_lock);
1052 _send_buffer_flush.swap(_send_buffer_main);
1053 _send_buffer_flush_offset = 0;
1057 _bytes_sending += _send_buffer_flush.size();
1061 if (_send_buffer_flush.empty())
1070 auto self(this->shared_from_this());
1071 auto async_write_handler =
make_alloc_handler(_send_storage, [
this, self](std::error_code ec,
size_t size)
1082 _bytes_sending -= size;
1083 _bytes_sent += size;
1086 _send_buffer_flush_offset += size;
1089 if (_send_buffer_flush_offset == _send_buffer_flush.size())
1092 _send_buffer_flush.clear();
1093 _send_buffer_flush_offset = 0;
1106 DisconnectInternalAsync(
true);
1109 if (_strand_required)
1110 _stream.async_write_some(asio::buffer(_send_buffer_flush.data() + _send_buffer_flush_offset, _send_buffer_flush.size() - _send_buffer_flush_offset), bind_executor(_strand, async_write_handler));
1112 _stream.async_write_some(asio::buffer(_send_buffer_flush.data() + _send_buffer_flush_offset, _send_buffer_flush.size() - _send_buffer_flush_offset), async_write_handler);
1115void SSLClient::ClearBuffers()
1118 std::scoped_lock locker(_send_lock);
1121 _send_buffer_main.clear();
1122 _send_buffer_flush.clear();
1123 _send_buffer_flush_offset = 0;
1131void SSLClient::SendError(std::error_code ec)
1134 if ((ec == asio::error::connection_aborted) ||
1135 (ec == asio::error::connection_refused) ||
1136 (ec == asio::error::connection_reset) ||
1137 (ec == asio::error::eof) ||
1138 (ec == asio::error::operation_aborted))
1142 if (ec == asio::ssl::error::stream_truncated)
1144 if (ec.category() == asio::error::get_ssl_category())
1146 if ((ERR_GET_REASON(ec.value()) == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC) ||
1147 (ERR_GET_REASON(ec.value()) == SSL_R_PROTOCOL_IS_SHUTDOWN) ||
1148 (ERR_GET_REASON(ec.value()) == SSL_R_WRONG_VERSION_NUMBER))
1152 onError(ec.value(), ec.category().name(), ec.message());
bool option_keep_alive() const noexcept
Get the option: keep alive.
virtual size_t Send(const void *buffer, size_t size)
Send data to the server (synchronous).
asio::ssl::stream< asio::ip::tcp::socket >::next_layer_type & socket() noexcept
Get the client socket.
virtual void onError(int error, const std::string &category, const std::string &message)
Handle error notification.
SSLClient(const std::shared_ptr< Service > &service, const std::shared_ptr< SSLContext > &context, const std::string &address, int port)
Initialize SSL client with a given Asio service, SSL context, server address and port number.
virtual void onSent(size_t sent, size_t pending)
Handle buffer sent notification.
const std::string & scheme() const noexcept
Get the scheme name.
virtual void ReceiveAsync()
Receive data from the server (asynchronous).
bool IsHandshaked() const noexcept
Is the session handshaked?
virtual void onHandshaked()
Handle session handshaked notification.
bool IsConnected() const noexcept
Is the client connected?
virtual void onEmpty()
Handle empty send buffer notification.
virtual bool SendAsync(const void *buffer, size_t size)
Send data to the server (asynchronous).
virtual bool ConnectAsync()
Connect the client (asynchronous).
virtual void onConnecting()
Handle client connecting notification.
virtual bool DisconnectAsync()
Disconnect the client (asynchronous).
virtual void onDisconnected()
Handle client disconnected notification.
virtual size_t Receive(void *buffer, size_t size)
Receive data from the server (synchronous).
virtual void onHandshaking()
Handle session handshaking notification.
void SetupSendBufferSize(size_t size)
Setup option: send buffer size.
virtual bool ReconnectAsync()
Reconnect the client (asynchronous).
virtual bool Disconnect()
Disconnect the client (synchronous).
bool option_no_delay() const noexcept
Get the option: no delay.
const std::string & address() const noexcept
Get the server address.
asio::ip::tcp::endpoint & endpoint() noexcept
Get the client endpoint.
void SetupReceiveBufferSize(size_t size)
Setup option: receive buffer size.
std::shared_ptr< Service > & service() noexcept
Get the Asio service.
virtual bool Reconnect()
Reconnect the client (synchronous).
virtual void onReceived(const void *buffer, size_t size)
Handle buffer received notification.
virtual void onDisconnecting()
Handle client disconnecting notification.
size_t option_receive_buffer_size() const
Get the option: receive buffer size.
virtual bool Connect()
Connect the client (synchronous).
virtual void onConnected()
Handle client connected notification.
std::shared_ptr< SSLContext > & context() noexcept
Get the client SSL context.
size_t option_send_buffer_size() const
Get the option: send buffer size.
uint64_t bytes_pending() const noexcept
Get the number of bytes pending sent by the client.
int port() const noexcept
Get the server port number.
AllocateHandler< THandler > make_alloc_handler(HandlerStorage &storage, THandler handler)
Helper function to wrap a handler object to add custom allocation.
constexpr size_t MIN_RECEIVE_BUFFER_SIZE
Minimal receive buffer size.
constexpr size_t MIN_SEND_BUFFER_SIZE
Minimal send buffer size.
C++ Server project definitions.