14SSLClient::SSLClient(
const std::shared_ptr<Service>& service,
const std::shared_ptr<SSLContext>& context,
const std::string& address,
int port)
17 _io_service(_service->GetAsioService()),
18 _strand(*_io_service),
19 _strand_required(_service->IsStrandRequired()),
23 _stream(*_io_service, *_context),
35 _send_buffer_flush_offset(0),
36 _option_keep_alive(
false),
37 _option_no_delay(
false)
41 throw CppCommon::ArgumentException(
"Asio service is invalid!");
45 throw CppCommon::ArgumentException(
"SSL context is invalid!");
48SSLClient::SSLClient(
const std::shared_ptr<Service>& service,
const std::shared_ptr<SSLContext>& context,
const std::string& address,
const std::string& scheme)
51 _io_service(_service->GetAsioService()),
52 _strand(*_io_service),
53 _strand_required(_service->IsStrandRequired()),
58 _stream(*_io_service, *_context),
70 _send_buffer_flush_offset(0),
71 _option_keep_alive(
false),
72 _option_no_delay(
false)
76 throw CppCommon::ArgumentException(
"Asio service is invalid!");
80 throw CppCommon::ArgumentException(
"SSL context is invalid!");
83SSLClient::SSLClient(
const std::shared_ptr<Service>& service,
const std::shared_ptr<SSLContext>& context,
const asio::ip::tcp::endpoint& endpoint)
86 _io_service(_service->GetAsioService()),
87 _strand(*_io_service),
88 _strand_required(_service->IsStrandRequired()),
90 _port(endpoint.port()),
93 _stream(*_io_service, *_context),
105 _send_buffer_flush_offset(0),
106 _option_keep_alive(
false),
107 _option_no_delay(
false)
111 throw CppCommon::ArgumentException(
"Asio service is invalid!");
115 throw CppCommon::ArgumentException(
"SSL context is invalid!");
124 asio::socket_base::receive_buffer_size
option;
125 _stream.next_layer().get_option(
option);
131 asio::socket_base::send_buffer_size
option;
132 _stream.next_layer().get_option(
option);
138 asio::socket_base::receive_buffer_size
option((
int)
size);
144 asio::socket_base::send_buffer_size
option((
int)
size);
154 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_service, *_context);
159 _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (
unsigned short)_port);
177 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
180 socket().set_option(asio::ip::tcp::no_delay(
true));
200 _stream.handshake(asio::ssl::stream_base::client,
ec);
218 if (_send_buffer_main.empty())
230 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_service, *_context);
235 asio::ip::tcp::resolver::query
query(_address, (_scheme.empty() ? std::to_string(_port) : _scheme));
265 socket().set_option(asio::ip::tcp::socket::keep_alive(
true));
268 socket().set_option(asio::ip::tcp::no_delay(
true));
288 _stream.handshake(asio::ssl::stream_base::client,
ec);
306 if (_send_buffer_main.empty())
312bool SSLClient::DisconnectInternal()
314 if (!
IsConnected() || _resolving || _connecting || _handshaking)
323 _handshaking =
false;
367 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_service, *_context);
407 _handshaking =
false;
424 if (_send_buffer_main.empty())
431 DisconnectInternalAsync(
true);
434 if (_strand_required)
449 _endpoint = asio::ip::tcp::endpoint(asio::ip::make_address(_address), (
unsigned short)_port);
451 if (_strand_required)
456 if (_strand_required)
479 _stream = asio::ssl::stream<asio::ip::tcp::socket>(*_io_service, *_context);
533 _handshaking =
false;
550 if (_send_buffer_main.empty())
557 DisconnectInternalAsync(
true);
560 if (_strand_required)
573 if (_strand_required)
588 asio::ip::tcp::resolver::query
query(_address, (_scheme.empty() ? std::to_string(_port) : _scheme));
589 if (_strand_required)
594 if (_strand_required)
602bool SSLClient::DisconnectInternalAsync(
bool dispatch)
604 if (!
IsConnected() || _resolving || _connecting || _handshaking)
611 if (!
IsConnected() || _resolving || _connecting || _handshaking)
621 if (_strand_required)
622 _stream.async_shutdown(bind_executor(_strand, async_shutdown_handler));
624 _stream.async_shutdown(async_shutdown_handler);
626 if (_strand_required)
629 _strand.dispatch(disconnect_handler);
631 _strand.post(disconnect_handler);
636 _io_service->dispatch(disconnect_handler);
638 _io_service->post(disconnect_handler);
650 CppCommon::Thread::Yield();
663 assert((
buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
698 assert((
buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
704 std::condition_variable
cv;
705 asio::error_code error;
706 asio::system_timer
timer(_stream.get_executor());
711 std::unique_lock<std::mutex>
lck(
mtx);
722 timer.expires_from_now(timeout.chrono());
730 std::unique_lock<std::mutex>
lck(
mtx);
731 cv.wait(
lck, [&]() {
return done == 2; });
744 if (error && (error != asio::error::timed_out))
761 assert((
buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
766 std::scoped_lock
locker(_send_lock);
769 bool send_required = _send_buffer_main.empty() || _send_buffer_flush.empty();
772 if (((_send_buffer_main.size() +
size) > _send_buffer_limit) && (_send_buffer_limit > 0))
774 SendError(asio::error::no_buffer_space);
780 _send_buffer_main.insert(_send_buffer_main.end(),
bytes,
bytes +
size);
783 _bytes_pending = _send_buffer_main.size();
797 if (_strand_required)
813 assert((
buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
855 assert((
buffer !=
nullptr) &&
"Pointer to the buffer should not be null!");
861 std::condition_variable
cv;
862 asio::error_code error;
863 asio::system_timer
timer(_stream.get_executor());
868 std::unique_lock<std::mutex>
lck(
mtx);
879 timer.expires_from_now(timeout.chrono());
887 std::unique_lock<std::mutex>
lck(
mtx);
888 cv.wait(
lck, [&]() {
return done == 2; });
901 if (error && (error != asio::error::timed_out))
923void SSLClient::TryReceive()
945 _bytes_received +=
size;
951 if (_receive_buffer.size() ==
size)
954 if (((2 *
size) > _receive_buffer_limit) && (_receive_buffer_limit > 0))
956 SendError(asio::error::no_buffer_space);
957 DisconnectInternalAsync(
true);
961 _receive_buffer.resize(2 * size);
971 DisconnectInternalAsync(
true);
974 if (_strand_required)
975 _stream.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), bind_executor(_strand, async_receive_handler));
977 _stream.async_read_some(asio::buffer(_receive_buffer.data(), _receive_buffer.size()), async_receive_handler);
980void SSLClient::TrySend()
989 if (_send_buffer_flush.empty())
991 std::scoped_lock locker(_send_lock);
994 _send_buffer_flush.swap(_send_buffer_main);
995 _send_buffer_flush_offset = 0;
999 _bytes_sending += _send_buffer_flush.size();
1003 if (_send_buffer_flush.empty())
1012 auto self(this->shared_from_this());
1013 auto async_write_handler =
make_alloc_handler(_send_storage, [
this, self](std::error_code ec,
size_t size)
1024 _bytes_sending -= size;
1025 _bytes_sent += size;
1028 _send_buffer_flush_offset += size;
1031 if (_send_buffer_flush_offset == _send_buffer_flush.size())
1034 _send_buffer_flush.clear();
1035 _send_buffer_flush_offset = 0;
1048 DisconnectInternalAsync(
true);
1051 if (_strand_required)
1052 _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));
1054 _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);
1057void SSLClient::ClearBuffers()
1060 std::scoped_lock locker(_send_lock);
1063 _send_buffer_main.clear();
1064 _send_buffer_flush.clear();
1065 _send_buffer_flush_offset = 0;
1073void SSLClient::SendError(std::error_code ec)
1076 if ((ec == asio::error::connection_aborted) ||
1077 (ec == asio::error::connection_refused) ||
1078 (ec == asio::error::connection_reset) ||
1079 (ec == asio::error::eof) ||
1080 (ec == asio::error::operation_aborted))
1084 if (ec == asio::ssl::error::stream_truncated)
1086 if (ec.category() == asio::error::get_ssl_category())
1088 if ((ERR_GET_REASON(ec.value()) == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC) ||
1089 (ERR_GET_REASON(ec.value()) == SSL_R_PROTOCOL_IS_SHUTDOWN) ||
1090 (ERR_GET_REASON(ec.value()) == SSL_R_WRONG_VERSION_NUMBER))
1094 onError(ec.value(), ec.category().name(), ec.message());
Asio allocate handler wrapper.
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.
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 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)
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.
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.
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.
AllocateHandler< THandler > make_alloc_handler(HandlerStorage &storage, THandler handler)
Helper function to wrap a handler object to add custom allocation.
C++ Server project definitions.