Fulcrum/EXClient.cpp
Calin Culianu faeb64207f Finally.. finished refactoring out the RPC bits.
Now we can re-use the RPC method<->result code in the TcpServer side
which will face wallets.  Phew!  Took me long enough!

It's a minimal JSON-RPC protocol impl.. but it'll do.

The 1 nice bit is the Schema spec I came up with which more-or-less
works well enough as a first-pass validatior.

Further passes are needed in interested client code, but the initial
pass validation can be done in a thread so as to not waste the main
thread's time validating json or dict key presence/absense. Yay.
2019-04-30 19:21:47 +03:00

116 lines
3.6 KiB
C++

#include "EXClient.h"
#include "EXMgr.h"
#include "Util.h"
#include <QtNetwork>
EXClient::EXClient(EXMgr *mgr, qint64 id, const QString &host, quint16 tport, quint16 sport)
: RPC::Connection(mgr->rpcMethods(), id, nullptr), host(host), tport(tport), sport(sport), mgr(mgr)
{
Debug() << __FUNCTION__ << " host:" << host << " t:" << tport << " s:" << sport;
_thread.setObjectName(QString("%1 %2").arg("EXClient").arg(host));
setObjectName(host);
}
EXClient::~EXClient()
{
Debug() << __FUNCTION__ << " " << objectName();
stop(); ///< need to be sure to call this here rather than rely on ThreadObjectMixin, as by the time it runs, we lost our vtable
}
/// this should only be called from our thread, because it accesses socket which should only be touched from thread
QString EXClient::prettyName(bool dontTouchSocket) const
{
if (_thread.isRunning() && QThread::currentThread() != &_thread) {
Warning() << __PRETTY_FUNCTION__ << " called from another thread! FIXME!";
dontTouchSocket = true;
}
return RPC::Connection::prettyName(dontTouchSocket);
}
bool EXClient::isGood() const
{
return RPC::Connection::isGood() && _thread.isRunning() && info.isValid();
}
// runs in thread
void EXClient::on_started()
{
Debug() << "started";
reconnect();
}
// runs in thread
void EXClient::on_finished()
{
killSocket();
ThreadObjectMixin::on_finished(); // calls moveToThread
Debug() << "finished.";
}
void EXClient::killSocket()
{
if (socket && socket->state() != QAbstractSocket::UnconnectedState) {
Debug() << host << " aborting connection";
do_disconnect();
}
delete socket; socket = nullptr; // delete of nullptr ok
status = NotConnected;
}
void EXClient::reconnect()
{
killSocket();
lastConnectionAttempt = Util::getTime();
if (sport && QSslSocket::supportsSsl()) {
QSslSocket *ssl;
socket = ssl = new QSslSocket(this);
auto conf = ssl->sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
ssl->setSslConfiguration(conf);
connect(ssl, &QSslSocket::encrypted, this, [this]{
Debug() << prettyName() << " connected encrypted";
on_connected();
});
socketConnectSignals();
ssl->connectToHostEncrypted(host, static_cast<quint16>(sport));
} else if (tport) {
socket = new QTcpSocket(this);
connect(socket, &QAbstractSocket::connected, this, [this]{
Debug() << prettyName() << " connected";
on_connected();
});
socketConnectSignals();
socket->connectToHost(host, static_cast<quint16>(tport));
} else {
Error() << "Cannot connect to " << host << "; no TCP port defined and SSL is disabled on this install";
}
}
void EXClient::do_ping()
{
emit sendRequest(mgr->newId(), "server.ping");
}
void EXClient::on_connected()
{
// runs in thread
RPC::Connection::on_connected();
connectedConns.push_back(
connect(this, &RPC::Connection::gotMessage, this,
[this](RPC::Connection *c, const RPC::Message &m)
{
if (this == c) emit EXClient::gotMessage(this, m); /// re-emits as EXClient * signal (different C++ signature)
else Error() << "this != c for gotMessage fwd! FIXME!";
})
); // connection will be auto-disconnected on socket disconnect in superclass on_disconnected impl.
emit newConnection(this);
}
void EXClient::on_disconnected()
{
RPC::Connection::on_disconnected();
emit lostConnection(this); /// re-emits as EXClient * signal (different method in C++)
}