Reputation: 125
I need to write a class that handle ssl connection (read/write char array) to a black box server. I need to implement disconnect/connect function. But it ain't work as expected.
Use case:
handle_connect()
return error connection timed out
)Here is the source code to the class and how I use it:
boost::asio::io_service &mioService;
SSLHandler* mpSSLConnection;
void Connector::setupConnection()
{
try{
std::string port = std::to_string(mPort);
boost::asio::ip::tcp::resolver resolver(mioService);
boost::asio::ip::tcp::resolver::query query(mHost, port);
boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
boost::asio::ssl::context context(boost::asio::ssl::context::sslv23);
// context.load_verify_file("key.pem");
if(mpSSLConnection == nullptr)
mpSSLConnection = new SSLHandler(mioService,context,iterator,this);
}catch (std::exception& e){
std::cerr << "Exception: " << e.what() << "\n";
}
// actually this line is called from outside the func
mpSSLConnection->connectToServer();
}
and disconnect like this
void Connector::disconnect()
{
isSetDisconnectedToSrv = true;
mpSSLConnection->setIsDestructing(true);
QThread::msleep(500);
delete mpSSLConnection;
mpSSLConnection = nullptr;
// setupConnection();
isConnectedToServer =false; // we did delete the object handle the ssl connection so...
mpHandler->onServerDisconnected(); // just report to upper layer
}
Finally, the class source code:
#ifndef SSLHANDLER_H
#define SSLHANDLER_H
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <queue>
#include <boost/lockfree/spsc_queue.hpp>
class Connector;
const int READ_SIZE =0;
const int READ_MSG=1;
class SSLHandler
{
public:
SSLHandler(boost::asio::io_service& io_service, boost::asio::ssl::context& context, boost::asio::ip::tcp::resolver::iterator endpoint_iterator, Connector* pConnector)
: socket_(io_service, context) , mEndpointIterator (endpoint_iterator) , mpConnector (pConnector),
timer_{ io_service},
isConnectionOk {false}
{
LOG_TRACE << "creating new sslhandler";
socket_.set_verify_mode(boost::asio::ssl::context::verify_none);
socket_.set_verify_callback(boost::bind(&SSLHandler::verify_certificate, this, _1, _2));
mode = READ_SIZE;
}
~SSLHandler();
bool verify_certificate(bool preverified, boost::asio::ssl::verify_context& ctx);
void handle_connect(const boost::system::error_code& error);
void handle_handshake(const boost::system::error_code& error);
void handle_write(const boost::system::error_code& error, size_t bytes_transferred);
void handle_write_auth(const boost::system::error_code& error, size_t bytes_transferred);
void handle_read_msgsize(const boost::system::error_code& error, size_t bytes_transferred);
void handle_read_message(const boost::system::error_code& error, size_t bytes_transferred);
void connectToServer();
void do_reconnect();
void handle_reconnect_timer(boost::system::error_code ec);
void writeMessage(std::vector<char> &array);
void setRequestMsg(std::vector<char> &&array);
void setIsDestructing(bool value);
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::ip::tcp::resolver::iterator mEndpointIterator;
boost::asio::deadline_timer timer_;
char reply_[0x1 << 16]; //=65356 bytes
int mode;
uint32_t size;
std::vector<char> requestMsg;
std::vector<char> replyMsg;
Connector* mpConnector; // ptr to object compose message
std::queue<std::vector < char> > mQueueMsg;
bool isConnectionOk;
bool isDestructing =false;
private:
void writeMessageWithQueue(std::vector<char> &array);
};
#endif // SSLHANDLER_H
#include "sslhandler.h"
#include "connector.h"
#include "BoostLogger.h"
#include <QThread>
#include "boost/enable_shared_from_this.hpp"
SSLHandler::~SSLHandler()
{
LOG_FATAL << "ssl handler shutdown";
if(isConnectionOk){
socket_.lowest_layer().close();
boost::system::error_code ec;
socket_.shutdown(ec);
if(ec){
LOG_FATAL << "ssl handler socket shutdown with err: " << ec.message();
}
LOG_TRACE << "ssl handler shutdown complete";
}
}
bool SSLHandler::verify_certificate(bool preverified, boost::asio::ssl::verify_context &ctx)
{
char subject_name[256];
X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
std::cout << "Verifying:\n" << subject_name << std::endl;
return preverified;
}
void SSLHandler::handle_connect(const boost::system::error_code &error)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
LOG_TRACE << "get past destructing";
if(!error){
isConnectionOk = true;
LOG_TRACE << "Connection OK!" << std::endl;
socket_.async_handshake(boost::asio::ssl::stream_base::client, boost::bind(&SSLHandler::handle_handshake, this, boost::asio::placeholders::error));
}else{
LOG_FATAL << "Connect failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_handshake(const boost::system::error_code &error)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if(!error){
std::cout << "Sending request: " << std::endl;
boost::asio::async_write(socket_,
boost::asio::buffer(requestMsg.data(), requestMsg.size()),
boost::bind(&SSLHandler::handle_write_auth, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Handshake failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_write(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (error) {
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
Q_UNUSED(bytes_transferred);
usleep(1e4);
if (!mQueueMsg.empty()) {
mQueueMsg.pop();
if(!mQueueMsg.empty()){
auto msg = mQueueMsg.front();
writeMessageWithQueue(msg);
}
}
else{
LOG_ERROR << "Empty queue messages!";
}
}
void SSLHandler::handle_write_auth(const boost::system::error_code &error, size_t bytes_transferred)
{
usleep(1e5);
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
if(mode==READ_SIZE){
mode = READ_MSG;
std::cerr << "\nSending request read size OK!\n" << std::endl;
// char respond[bytes_transferred] = "";
boost::asio::async_read(socket_, boost::asio::buffer(reply_,4),
boost::bind(&SSLHandler::handle_read_msgsize,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// std::cerr << "respond is " ;
}
}else{
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_msgsize(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
//first 4 bytes contain size of message
size = getFirstFour();
mode = READ_SIZE;
boost::asio::async_read(socket_, boost::asio::buffer(reply_,size),
boost::bind(&SSLHandler::handle_read_message,
this,
// mWriteId++,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_message(const boost::system::error_code &error, size_t bytes_transferred)
{
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
if (!error){
replyMsg.clear();
replyMsg.assign(reply_,reply_+ size);
mpConnector->setReadMsg(replyMsg);
mode = READ_SIZE;
// read next message size
boost::asio::async_read(socket_, boost::asio::buffer(reply_,4),
boost::bind(&SSLHandler::handle_read_msgsize,
this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}else{
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::connectToServer()
{
// boost::asio::ip::tcp::resolver r(socket_.get_io_service());
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
boost::asio::async_connect(socket_.lowest_layer(), mEndpointIterator, boost::bind(&SSLHandler::handle_connect, this, boost::asio::placeholders::error));
LOG_TRACE << "async_connect called";
}
void SSLHandler::do_reconnect()
{
// return;
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
// socket_.shutdown();
isConnectionOk = false;
else{
socket_.lowest_layer().cancel();
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&SSLHandler::handle_reconnect_timer, this, boost::asio::placeholders::error()));
}
}
void SSLHandler::handle_reconnect_timer(boost::system::error_code ec)
{
if(!ec){
connectToServer();
}
else{
LOG_TRACE << "Error with reconnect timer : " << ec.message();
}
}
void SSLHandler::writeMessageWithQueue(std::vector<char> &array)
{
// std::cerr << "write : " << (void*) array.data() << " | " << array.size() << std::endl;
if(isDestructing){
LOG_TRACE << "Is destructing ssl connect so abort " ;
return;
}
boost::asio::async_write(socket_,
boost::asio::buffer(array.data(), array.size()),
boost::bind(&SSLHandler::handle_write, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void SSLHandler::writeMessage(std::vector<char> &array)
{
if(mQueueMsg.size()==0){
mQueueMsg.push(array);
writeMessageWithQueue(array);
}
else
mQueueMsg.push(array);
}
void SSLHandler::setRequestMsg(std::vector<char> &&array)
{
requestMsg = std::move(array);
}
void SSLHandler::setIsDestructing(bool value)
{
LOG_INFO << "ssl connection destructing set as " << value;
isDestructing = value;
if(isDestructing == true){
if(isConnectionOk){
socket_.lowest_layer().cancel();
// socket_.shutdown();
LOG_INFO << "ssl connection destructing get pass shutdown";
}
}
}
PS: Here is the handler tracking which is weird, after (call) disconnect()
and reconnect again: no hander was fired, so no handshake after connection is established :(
@asio|1521627714.863517|0|resolver@0x7fbe2affc898.cancel Empty fz params!Empty fz rules!Empty fz param!@asio____________ socket_: 0x7fbe10005150 @asio____________192.168.2.36 @asio|1521627714.864161|0*1|socket@0x7fbe10005150.async_connect @asio: connect called @asio|1521627714.865136|>1|ec=system:0 @asio|1521627714.865375|1*2|socket@0x7fbe10005150.async_send @asio|1521627714.865416|<1| @asio|1521627714.865421|>2|ec=system:0,bytes_transferred=517 @asio|1521627714.865429|2*3|socket@0x7fbe10005150.async_receive @asio|1521627714.865451|<2| @asio|1521627714.866829|>3|ec=system:0,bytes_transferred=994 @asio|1521627714.867764|3*4|socket@0x7fbe10005150.async_send @asio|1521627714.867792|<3| @asio|1521627714.867798|>4|ec=system:0,bytes_transferred=326 @asio|1521627714.867809|4*5|socket@0x7fbe10005150.async_receive @asio|1521627714.867817|<4| @asio|1521627714.870094|>5|ec=system:0,bytes_transferred=234 @asio|1521627714.870271|5*6|socket@0x7fbe10005150.async_send @asio|1521627714.870318|<5| @asio|1521627714.870333|>6|ec=system:0,bytes_transferred=154 @asio|1521627714.970430|6*7|socket@0x7fbe10005150.async_receive @asio|1521627714.970443|<6| @asio|1521627714.970449|>7|ec=system:0,bytes_transferred=138 @asio|1521627714.970470|7*8|socket@0x7fbe10005150.async_receive @asio|1521627714.970475|<7| @asio|1521627714.970479|>8|ec=system:0,bytes_transferred=0 @asio|1521627714.971418|8*9|socket@0x7fbe10005150.async_send @asio|1521627714.971771|8*10|deadline_timer@0x5290628.async_wait @asio|1521627714.972004|8*11|socket@0x7fbe10005150.async_receive @asio|1521627714.972012|<8| @asio|1521627714.972017|>9|ec=system:0,bytes_transferred=138 @asio|1521627714.982098|9*12|socket@0x7fbe10005150.async_send @asio|1521627714.982115|<9| @asio|1521627714.982121|>12|ec=system:0,bytes_transferred=138 @asio|1521627714.992214|12*13|socket@0x7fbe10005150.async_send @asio|1521627714.992244|<12| @asio|1521627714.992255|>11|ec=system:0,bytes_transferred=292 @asio|1521627714.992278|11*14|socket@0x7fbe10005150.async_receive @asio|1521627714.992284|<11| @asio|1521627714.992290|>13|ec=system:0,bytes_transferred=186 @asio|1521627715.002355|<13| @asio|1521627715.002363|>14|ec=system:0,bytes_transferred=0 @asio|1521627715.002469|14*15|socket@0x7fbe10005150.async_receive @asio|1521627715.002479|<14| @asio|1521627715.002487|>15|ec=system:0,bytes_transferred=0 @asio|1521627715.002495|15*16|socket@0x7fbe10005150.async_receive @asio|1521627715.002500|<15| @asio|1521627715.002505|>16|ec=system:0,bytes_transferred=0 @asio|1521627715.002550|16*17|socket@0x7fbe10005150.async_receive @asio|1521627715.002561|<16| @asio|1521627715.002566|>17|ec=system:0,bytes_transferred=154 @asio|1521627715.002581|17*18|socket@0x7fbe10005150.async_receive @asio|1521627715.002586|<17| @asio|1521627715.002590|>18|ec=system:0,bytes_transferred=0 @asio|1521627715.002636|18*19|socket@0x7fbe10005150.async_receive @asio|1521627715.002653|<18| @asio|1521627721.861983|>10|ec=system:0 @asio|1521627721.862105|10*20|socket@0x7fbe10005150.async_send @asio|1521627721.862139|10|deadline_timer@0x5290628.cancel @asio|1521627721.862144|10*21|deadline_timer@0x5290628.async_wait @asio|1521627721.862258|<10| @asio|1521627721.862268|>20|ec=system:0,bytes_transferred=138 @asio|1521627721.872365|<20| @asio|1521627721.872398|>19|ec=system:0,bytes_transferred=138 @asio|1521627721.872436|19*22|socket@0x7fbe10005150.async_receive @asio|1521627721.872443|<19| @asio|1521627721.872447|>22|ec=system:0,bytes_transferred=0 @asio|1521627721.872503|22*23|socket@0x7fbe10005150.async_receive @asio|1521627721.872515|<22| @asio|1521627724.861966|>21|ec=system:0 @asio|1521627724.862091|21*24|socket@0x7fbe10005150.async_send @asio|1521627724.862148|21|deadline_timer@0x5290628.cancel @asio|1521627724.862157|21*25|deadline_timer@0x5290628.async_wait @asio|1521627724.862272|<21| @asio|1521627724.862286|>24|ec=system:0,bytes_transferred=138 @asio|1521627724.872375|<24| @asio|1521627724.872409|>23|ec=system:0,bytes_transferred=138 @asio|1521627724.872457|23*26|socket@0x7fbe10005150.async_receive @asio|1521627724.872465|<23| @asio|1521627724.872469|>26|ec=system:0,bytes_transferred=0 @asio|1521627724.872510|26*27|socket@0x7fbe10005150.async_receive @asio|1521627724.872516|<26| @asio|1521627727.861968|>25|ec=system:0 @asio|1521627727.862084|25*28|socket@0x7fbe10005150.async_send @asio|1521627727.862120|25|deadline_timer@0x5290628.cancel @asio|1521627727.862125|25*29|deadline_timer@0x5290628.async_wait @asio|1521627727.862204|<25| @asio|1521627727.862211|>28|ec=system:0,bytes_transferred=138 @asio|1521627727.872283|<28| @asio|1521627727.872314|>27|ec=system:0,bytes_transferred=138 @asio|1521627727.872362|27*30|socket@0x7fbe10005150.async_receive @asio|1521627727.872366|<27| @asio|1521627727.872371|>30|ec=system:0,bytes_transferred=0 @asio|1521627727.872412|30*31|socket@0x7fbe10005150.async_receive @asio|1521627727.872418|<30| @asio|1521627730.861967|>29|ec=system:0 @asio|1521627730.862072|29*32|socket@0x7fbe10005150.async_send @asio|1521627730.862118|29|deadline_timer@0x5290628.cancel @asio|1521627730.862125|29*33|deadline_timer@0x5290628.async_wait @asio|1521627730.862217|<29| @asio|1521627730.862227|>32|ec=system:0,bytes_transferred=138 @asio|1521627730.872315|<32| @asio|1521627730.872360|>31|ec=system:0,bytes_transferred=138 @asio|1521627730.872406|31*34|socket@0x7fbe10005150.async_receive @asio|1521627730.872412|<31| @asio|1521627730.872416|>34|ec=system:0,bytes_transferred=0 @asio|1521627730.872458|34*35|socket@0x7fbe10005150.async_receive @asio|1521627730.872465|<34| @asio|1521627733.862001|>33|ec=system:0 @asio|1521627733.862114|33*36|socket@0x7fbe10005150.async_send @asio|1521627733.862153|33|deadline_timer@0x5290628.cancel @asio|1521627733.862158|33*37|deadline_timer@0x5290628.async_wait @asio|1521627733.862244|<33| @asio|1521627733.862250|>36|ec=system:0,bytes_transferred=138 @asio|1521627733.872342|<36| @asio|1521627733.872379|>35|ec=system:0,bytes_transferred=138 @asio|1521627733.872416|35*38|socket@0x7fbe10005150.async_receive @asio|1521627733.872422|<35| @asio|1521627733.872424|>38|ec=system:0,bytes_transferred=0 @asio|1521627733.872461|38*39|socket@0x7fbe10005150.async_receive @asio|1521627733.872466|<38| @asio|1521627736.861976|>37|ec=system:0 @asio|1521627736.862158|37*40|socket@0x7fbe10005150.async_send @asio|1521627736.862235|37|deadline_timer@0x5290628.cancel @asio|1521627736.862242|37*41|deadline_timer@0x5290628.async_wait @asio|1521627736.862406|<37| @asio|1521627736.862414|>40|ec=system:0,bytes_transferred=138 @asio|1521627736.872497|<40| @asio|1521627736.872555|>39|ec=system:0,bytes_transferred=138 @asio|1521627736.872622|39*42|socket@0x7fbe10005150.async_receive @asio|1521627736.872638|<39| @asio|1521627736.872641|>42|ec=system:0,bytes_transferred=0 @asio|1521627736.872720|42*43|socket@0x7fbe10005150.async_receive @asio|1521627736.872726|<42| @asio|1521627739.861978|>41|ec=system:0 @asio|1521627739.862096|41*44|socket@0x7fbe10005150.async_send @asio|1521627739.862144|41|deadline_timer@0x5290628.cancel @asio|1521627739.862148|41*45|deadline_timer@0x5290628.async_wait @asio|1521627739.862243|<41| @asio|1521627739.862249|>44|ec=system:0,bytes_transferred=138 @asio|1521627739.872335|<44| @asio|1521627739.872375|>43|ec=system:0,bytes_transferred=138 @asio|1521627739.872421|43*46|socket@0x7fbe10005150.async_receive @asio|1521627739.872425|<43| @asio|1521627739.872429|>46|ec=system:0,bytes_transferred=0 @asio|1521627739.872477|46*47|socket@0x7fbe10005150.async_receive @asio|1521627739.872492|<46| @asio|1521627742.861953|>45|ec=system:0 @asio|1521627742.862121|45*48|socket@0x7fbe10005150.async_send @asio|1521627742.862204|45|deadline_timer@0x5290628.cancel @asio|1521627742.862211|45*49|deadline_timer@0x5290628.async_wait @asio|1521627742.862392|<45| @asio|1521627742.862406|>48|ec=system:0,bytes_transferred=138 @asio|1521627742.872491|<48| @asio|1521627742.872543|>47|ec=system:0,bytes_transferred=138 @asio|1521627742.872592|47*50|socket@0x7fbe10005150.async_receive @asio|1521627742.872600|<47| @asio|1521627742.872605|>50|ec=system:0,bytes_transferred=0 @asio|1521627742.872675|50*51|socket@0x7fbe10005150.async_receive @asio|1521627742.872688|<50| @asio|1521627745.316714|0|socket@0x7fbe10005150.close @asio|1521627745.316777|>51|ec=system:125,bytes_transferred=0 @asio|1521627745.316858|<51| @asio: ~SSLHandler @asio|1521627745.817594|0|resolver@0x7fbe00ff8758.cancel @asio|1521627745.861965|>49|ec=system:0 @asio|1521627745.861984|<49| @asio|1521627749.757091|0|resolver@0x7fbe00ff8648.cancel @asio____________ socket_: 0x7fbde4008890 @asio____________192.168.2.36 @asio|1521627749.757178|0*52|socket@0x7fbde4008890.async_connect @asio: connect called
Upvotes: 0
Views: 1966
Reputation: 393769
I fixed the sample to be selfcontained, and ran it against a demo server:
#include <iostream>
#include <sstream>
#include <vector>
#ifdef STANDALONE
namespace {
struct LogTx {
std::stringstream _ss;
std::ostream& _os;
bool _armed = true;
LogTx(std::ostream& os) : _os(os) {}
LogTx(LogTx&& rhs) : _ss(std::move(rhs._ss)), _os(rhs._os) { rhs._armed = false; }
~LogTx() { if (_armed) _os << _ss.rdbuf() << std::endl; }
LogTx operator<<(std::ostream&(&v)(std::ostream&)) { _ss << v; return std::move(*this); }
template <typename T> LogTx operator<<(T&& v) { _ss << v; return std::move(*this); }
};
}
# define LOG_FATAL LogTx(std::cerr) << "FATAL: "
# define LOG_TRACE LogTx(std::clog) << "TRACE: "
# define LOG_ERROR LogTx(std::cerr) << "ERROR: "
# define LOG_INFO LogTx(std::clog) << "INFO: "
# define Q_UNUSED(a) static_cast<void>(a)
namespace {
struct Connector {
void sendDisconnectedStatus() { LOG_INFO << "Disconnected"; }
void setReadMsg(std::vector<char> const& v) { LOG_INFO << "response: '" << std::string(v.begin(), v.end()) << "'"; }
};
}
#endif
#ifndef SSLHANDLER_H
#define SSLHANDLER_H
#include <boost/endian/arithmetic.hpp> // for big_uint32_t
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/bind.hpp>
#include <queue>
#include <string>
#include <thread>
const int READ_SIZE = 0;
const int READ_MSG = 1;
class SSLHandler {
public:
SSLHandler(boost::asio::io_service &io_service, boost::asio::ssl::context &context,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator, Connector *pConnector)
: socket_(io_service, context), mEndpointIterator(endpoint_iterator),
mpConnector(pConnector), timer_{ io_service }, isConnectionOk{ false }
{
LOG_TRACE << "creating new sslhandler";
socket_.set_verify_mode(boost::asio::ssl::context::verify_none);
socket_.set_verify_callback(boost::bind(&SSLHandler::verify_certificate, this, _1, _2));
mode = READ_SIZE;
}
~SSLHandler();
bool verify_certificate(bool preverified, boost::asio::ssl::verify_context &ctx);
void handle_connect(const boost::system::error_code &error);
void handle_handshake(const boost::system::error_code &error);
void handle_write(const boost::system::error_code &error, size_t bytes_transferred);
void handle_write_auth(const boost::system::error_code &error, size_t bytes_transferred);
void handle_read_msgsize(const boost::system::error_code &error, size_t bytes_transferred);
void handle_read_message(const boost::system::error_code &error, size_t bytes_transferred);
void connectToServer();
void do_reconnect();
void handle_reconnect_timer(boost::system::error_code ec);
void writeMessage(std::vector<char> &array);
void setRequestMsg(std::vector<char> &&array);
void setIsDestructing(bool value);
private:
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
boost::asio::ip::tcp::resolver::iterator mEndpointIterator;
Connector *mpConnector; // ptr to object compose message
boost::asio::deadline_timer timer_;
char reply_[0x1 << 16]; //=65356 bytes
size_t getFirstFour() {
return *boost::asio::buffer_cast<boost::endian::big_uint32_t *>(boost::asio::buffer(reply_));
};
int mode;
uint32_t size;
std::vector<char> requestMsg;
std::vector<char> replyMsg;
std::queue<std::vector<char> > mQueueMsg;
bool isConnectionOk;
bool isDestructing = false;
private:
void writeMessageWithQueue(std::vector<char> &array);
};
#endif // SSLHANDLER_H
//#include "sslhandler.h"
//#include "connector.h"
//#include "BoostLogger.h"
//#include <QThread>
//#include "boost/enable_shared_from_this.hpp"
SSLHandler::~SSLHandler() {
LOG_FATAL << "ssl handler shutdown";
if (isConnectionOk) {
socket_.lowest_layer().close();
boost::system::error_code ec;
socket_.shutdown(ec);
if (ec) {
LOG_FATAL << "ssl handler socket shutdown with err: " << ec.message();
}
LOG_TRACE << "ssl handler shutdown complete";
}
}
bool SSLHandler::verify_certificate(bool preverified, boost::asio::ssl::verify_context &ctx) {
char subject_name[256];
X509 *cert = X509_STORE_CTX_get_current_cert(ctx.native_handle());
X509_NAME_oneline(X509_get_subject_name(cert), subject_name, 256);
std::cout << "Verifying:\n" << subject_name << std::endl;
return preverified;
}
void SSLHandler::handle_connect(const boost::system::error_code &error) {
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
LOG_TRACE << "get past destructing";
if (!error) {
isConnectionOk = true;
LOG_TRACE << "Connection OK!" << std::endl;
socket_.async_handshake(boost::asio::ssl::stream_base::client,
boost::bind(&SSLHandler::handle_handshake, this, boost::asio::placeholders::error));
} else {
LOG_FATAL << "Connect failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_handshake(const boost::system::error_code &error) {
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
if (!error) {
std::cout << "Sending request: " << std::endl;
boost::asio::async_write(socket_, boost::asio::buffer(requestMsg.data(), requestMsg.size()),
boost::bind(&SSLHandler::handle_write_auth, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
LOG_FATAL << "Handshake failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_write(const boost::system::error_code &error, size_t bytes_transferred) {
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
if (error) {
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
Q_UNUSED(bytes_transferred);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (!mQueueMsg.empty()) {
mQueueMsg.pop();
if (!mQueueMsg.empty()) {
auto msg = mQueueMsg.front();
writeMessageWithQueue(msg);
}
} else {
LOG_ERROR << "Empty queue messages!";
}
}
void SSLHandler::handle_write_auth(const boost::system::error_code &error, size_t bytes_transferred) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
if (!error) {
if (mode == READ_SIZE) {
mode = READ_MSG;
std::cerr << "\nSending request read size OK!\n" << std::endl;
// char respond[bytes_transferred] = "";
boost::asio::async_read(socket_, boost::asio::buffer(reply_, 4),
boost::bind(&SSLHandler::handle_read_msgsize, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
// std::cerr << "respond is " ;
}
} else {
LOG_FATAL << "Write failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_msgsize(const boost::system::error_code &error, size_t bytes_transferred) {
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
if (!error) {
// first 4 bytes contain size of message
size = getFirstFour();
LOG_TRACE << "Decoded size: " << size;
mode = READ_SIZE;
boost::asio::async_read(socket_, boost::asio::buffer(reply_, size),
boost::bind(&SSLHandler::handle_read_message, this,
// mWriteId++,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::handle_read_message(const boost::system::error_code &error, size_t bytes_transferred) {
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
if (!error) {
replyMsg.clear();
replyMsg.assign(reply_, reply_ + size);
mpConnector->setReadMsg(replyMsg);
mode = READ_SIZE;
// read next message size
boost::asio::async_read(socket_, boost::asio::buffer(reply_, 4),
boost::bind(&SSLHandler::handle_read_msgsize, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
} else {
LOG_FATAL << "Read failed: " << error.message() << std::endl;
mpConnector->sendDisconnectedStatus();
do_reconnect();
}
}
void SSLHandler::connectToServer() {
// boost::asio::ip::tcp::resolver r(socket_.get_io_service());
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
boost::asio::async_connect(socket_.lowest_layer(), mEndpointIterator,
boost::bind(&SSLHandler::handle_connect, this, boost::asio::placeholders::error));
LOG_TRACE << "async_connect called";
}
void SSLHandler::do_reconnect() {
// socket_.shutdown();
isConnectionOk = false;
// return;
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
} else {
socket_.lowest_layer().cancel();
timer_.expires_from_now(boost::posix_time::millisec(500));
timer_.async_wait(boost::bind(&SSLHandler::handle_reconnect_timer, this, boost::asio::placeholders::error()));
}
}
void SSLHandler::handle_reconnect_timer(boost::system::error_code ec) {
if (!ec) {
connectToServer();
} else {
LOG_TRACE << "Error with reconnect timer : " << ec.message();
}
}
void SSLHandler::writeMessageWithQueue(std::vector<char> &array) {
// std::cerr << "write : " << (void*) array.data() << " | " << array.size() << std::endl;
if (isDestructing) {
LOG_TRACE << "Is destructing ssl connect so abort ";
return;
}
boost::asio::async_write(socket_, boost::asio::buffer(array.data(), array.size()),
boost::bind(&SSLHandler::handle_write, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void SSLHandler::writeMessage(std::vector<char> &array) {
if (mQueueMsg.size() == 0) {
mQueueMsg.push(array);
writeMessageWithQueue(array);
}
else
mQueueMsg.push(array);
}
void SSLHandler::setRequestMsg(std::vector<char> &&array) { requestMsg = std::move(array); }
void SSLHandler::setIsDestructing(bool value) {
LOG_INFO << "ssl connection destructing set as " << value;
isDestructing = value;
if (isDestructing == true) {
if (isConnectionOk) {
socket_.lowest_layer().cancel();
// socket_.shutdown();
LOG_INFO << "ssl connection destructing get pass shutdown";
}
}
}
int main() {
Connector c;
boost::asio::io_service svc;
boost::asio::ssl::context ctx(boost::asio::ssl::context_base::sslv23_client);
SSLHandler h(svc, ctx, boost::asio::ip::tcp::resolver{svc}.resolve({{}, 6767}), &c);
h.setRequestMsg({'h','e','l','l','o','\n','w','o','r','l','d'});
h.connectToServer();
svc.run();
}
Now, I didn't see any issue, regardless of how long it took for the server to be back after interruption.
You mention
Hi, I mean user actively call disconnect function (not connection dropped by network like last time). If the user call disconnect() then wait more than 10 secs, then call connect() the connection is failed with error: connection timed out. – Patton 11 hours ago
There's no such disconnect()
function in your code, nor can I see how it's likely implemented. Therefore, either
Upvotes: 1