made JsonApiServer an singleton through static method instance()

This commit is contained in:
csoler 2019-11-12 22:32:18 +01:00
parent 3b45fc5199
commit d19d1685de
No known key found for this signature in database
GPG Key ID: 7BCA522266C0804C
9 changed files with 129 additions and 95 deletions

View File

@ -44,6 +44,9 @@
/*extern*/ JsonApiServer* jsonApiServer = nullptr; /*extern*/ JsonApiServer* jsonApiServer = nullptr;
const std::string JsonApiServer::DEFAULT_LISTENING_ADDRESS = "127.0.0.1";
p3ConfigMgr *JsonApiServer::_config_mgr = nullptr;
/*static*/ const std::multimap<std::string, std::string> /*static*/ const std::multimap<std::string, std::string>
JsonApiServer::corsHeaders = JsonApiServer::corsHeaders =
{ {
@ -115,11 +118,33 @@ JsonApiServer::corsOptionsHeaders =
return false; return false;
} }
JsonApiServer::JsonApiServer(uint16_t port, const std::string& bindAddress, JsonApiServer& JsonApiServer::instance()
const std::function<bool(const std::string&)> newAccessRequestCallback ) : {
mPort(port), mBindAddress(bindAddress), static JsonApiServer *_instance = nullptr;
mNewAccessRequestCallback(newAccessRequestCallback),
configMutex("JsonApiServer config") if(_instance == NULL)
{
_instance = new JsonApiServer();
if(_config_mgr == nullptr)
RsErr() << "JsonApiServer::instance() called before JsonApiServer::setConfigManager(). This is a bug!" << std::endl;
_config_mgr->addConfiguration("jsonapi.cfg",_instance);
}
return *_instance;
}
void JsonApiServer::start(uint16_t port, const std::string& bindAddress, const std::function<bool(const std::string&)> callback)
{
mPort = port;
mBindAddress = bindAddress;
mNewAccessRequestCallback = callback;
RsThread::start("JsonApiServer");
}
JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config")
{ {
registerHandler("/rsLoginHelper/createLocation", registerHandler("/rsLoginHelper/createLocation",
[this](const std::shared_ptr<rb::Session> session) [this](const std::shared_ptr<rb::Session> session)
@ -342,6 +367,10 @@ JsonApiServer::JsonApiServer(uint16_t port, const std::string& bindAddress,
} ); } );
}, true); }, true);
RsFileHash dummyHash;
setFilename("jsonapi.cfg"); // hack
loadConfiguration(dummyHash);
// Generated at compile time // Generated at compile time
#include "jsonapi-wrappers.inl" #include "jsonapi-wrappers.inl"
} }

View File

@ -35,11 +35,11 @@ namespace rb = restbed;
struct JsonApiServer; struct JsonApiServer;
/** // /**
* Pointer to global instance of JsonApiServer // * Pointer to global instance of JsonApiServer
* @jsonapi{development} // * @jsonapi{development}
*/ // */
extern JsonApiServer* jsonApiServer; // extern JsonApiServer* jsonApiServer;
/** /**
* Simple usage * Simple usage
@ -53,20 +53,9 @@ extern JsonApiServer* jsonApiServer;
struct JsonApiServer : RsSingleJobThread, p3Config struct JsonApiServer : RsSingleJobThread, p3Config
{ {
static const uint16_t DEFAULT_PORT = 9092 ; static const uint16_t DEFAULT_PORT = 9092 ;
static const std::string DEFAULT_LISTENING_ADDRESS ;
/** static JsonApiServer& instance() ;
* @brief construct a JsonApiServer instance with given parameters
* @param[in] port listening port fpt the JSON API socket
* @param[in] bindAddress binding address for the JSON API socket
* @param newAccessRequestCallback called when a new auth token is asked to
* be authorized via JSON API, the auth token is passed as parameter, and
* the callback should return true if the new token get access granted and
* false otherwise, this usually requires user interacion to confirm access
*/
JsonApiServer(
uint16_t port = DEFAULT_PORT,
const std::string& bindAddress = "127.0.0.1",
const std::function<bool(const std::string&)> newAccessRequestCallback = [](const std::string&){return false;} );
/** /**
* @param[in] path Path itno which publish the API call * @param[in] path Path itno which publish the API call
@ -96,6 +85,17 @@ struct JsonApiServer : RsSingleJobThread, p3Config
*/ */
void shutdown(); void shutdown();
/**
* @brief start
* Starts the json Api server.
*
* @param port port to listen to
* @param listen_address bind address to listen to
*/
void start( uint16_t port = DEFAULT_PORT,
const std::string& bindAddress = "127.0.0.1",
const std::function<bool(const std::string&)> newAccessRequestCallback = [](const std::string&){return false;});
/** /**
* @brief This function should be used by JSON API clients that aren't * @brief This function should be used by JSON API clients that aren't
* authenticated yet, to ask their token to be authorized, the success or * authenticated yet, to ask their token to be authorized, the success or
@ -175,10 +175,25 @@ struct JsonApiServer : RsSingleJobThread, p3Config
static void version( uint32_t& major, uint32_t& minor, uint32_t& mini, static void version( uint32_t& major, uint32_t& minor, uint32_t& mini,
std::string& extra, std::string&human ); std::string& extra, std::string&human );
static void setConfigMgr(p3ConfigMgr *cfg) { _config_mgr = cfg; }
protected:
/// @see RsSingleJobThread /// @see RsSingleJobThread
virtual void run(); virtual void run();
private: private:
/**
* @brief construct a JsonApiServer instance with given parameters
* @param[in] port listening port fpt the JSON API socket
* @param[in] bindAddress binding address for the JSON API socket
* @param newAccessRequestCallback called when a new auth token is asked to
* be authorized via JSON API, the auth token is passed as parameter, and
* the callback should return true if the new token get access granted and
* false otherwise, this usually requires user interacion to confirm access
*/
JsonApiServer( );
/// @see p3Config::setupSerialiser /// @see p3Config::setupSerialiser
virtual RsSerialiser* setupSerialiser(); virtual RsSerialiser* setupSerialiser();
@ -191,8 +206,8 @@ private:
/// @see p3Config::saveDone /// @see p3Config::saveDone
virtual void saveDone(); virtual void saveDone();
const uint16_t mPort; uint16_t mPort;
const std::string mBindAddress; std::string mBindAddress;
rb::Service mService; rb::Service mService;
/// Called when new JSON API auth token is requested to be authorized /// Called when new JSON API auth token is requested to be authorized
@ -220,5 +235,7 @@ private:
return checkRsServicePtrReady( return checkRsServicePtrReady(
serviceInstance.get(), serviceName, ctx, session ); serviceInstance.get(), serviceName, ctx, session );
} }
static p3ConfigMgr *_config_mgr;
}; };

View File

@ -173,15 +173,16 @@ void p3ConfigMgr::addConfiguration(std::string file, pqiConfig *conf)
} }
// Also check that the filename is not already registered for another config // Also check that the filename is not already registered for another config
for(std::list<pqiConfig*>::const_iterator it = mConfigs.begin(); it!= mConfigs.end(); ++it) for(std::list<pqiConfig*>::iterator it = mConfigs.begin(); it!= mConfigs.end();)
if((*it)->filename == filename) if((*it)->filename == filename)
{ {
std::cerr << "!!!!!!!!!! Trying to register a config for file \"" << filename << "\" that is already registered" << std::endl; std::cerr << "(WW) Registering a config for file \"" << filename << "\" that is already registered. Replacing previous component." << std::endl;
std::cerr << "!!!!!!!!!! Please correct the code !" << std::endl; it = mConfigs.erase(it);
return;
} }
else
++it;
conf->setFilename(filename); conf->setFilename(filename);// (cyril) this is quite terrible. The constructor of pqiConfig should take the filename as parameter and hold the information.
mConfigs.push_back(conf); mConfigs.push_back(conf);
} }

View File

@ -106,13 +106,12 @@ void setHash(const RsFileHash& h);
RsMutex cfgMtx; RsMutex cfgMtx;
private:
/** /**
* This sets the name of the pqi configuation file * This sets the name of the pqi configuation file
*/ */
void setFilename(const std::string& name); void setFilename(const std::string& name);
private:
/** /**
* @param an index for the Confind which contains list of configuarations that can be tracked * @param an index for the Confind which contains list of configuarations that can be tracked
*/ */

View File

@ -91,7 +91,7 @@ void RsServer::rsGlobalShutDown()
mNetMgr->shutdown(); /* Handles UPnP */ mNetMgr->shutdown(); /* Handles UPnP */
#ifdef RS_JSONAPI #ifdef RS_JSONAPI
if(jsonApiServer) jsonApiServer->shutdown(); JsonApiServer::instance().shutdown();
#endif #endif
rsAutoProxyMonitor::instance()->stopAllRSShutdown(); rsAutoProxyMonitor::instance()->stopAllRSShutdown();

View File

@ -410,10 +410,8 @@ int RsInit::InitRetroShare(const RsConfigOptions& conf)
#ifdef RS_JSONAPI #ifdef RS_JSONAPI
if(rsInitConfig->jsonApiPort) if(rsInitConfig->jsonApiPort)
{ JsonApiServer::instance().start(rsInitConfig->jsonApiPort, rsInitConfig->jsonApiBindAddress);
jsonApiServer = new JsonApiServer( rsInitConfig->jsonApiPort, rsInitConfig->jsonApiBindAddress );
jsonApiServer->start("JSON API Server");
}
#endif // ifdef RS_JSONAPI #endif // ifdef RS_JSONAPI
return RS_INIT_OK; return RS_INIT_OK;
@ -1214,14 +1212,7 @@ int RsServer::StartupRetroShare()
// //
mPluginsManager->loadPlugins(programatically_inserted_plugins) ; mPluginsManager->loadPlugins(programatically_inserted_plugins) ;
#ifdef RS_JSONAPI JsonApiServer::setConfigMgr(mConfigMgr);
if(jsonApiServer) // JsonApiServer may be disabled at runtime
{
mConfigMgr->addConfiguration("jsonApi.cfg", jsonApiServer);
RsFileHash dummyHash;
jsonApiServer->loadConfiguration(dummyHash);
}
#endif
/**** Reputation system ****/ /**** Reputation system ****/
@ -1230,7 +1221,7 @@ int RsServer::StartupRetroShare()
#ifdef RS_ENABLE_GXS #ifdef RS_ENABLE_GXS
std::string currGxsDir = RsAccounts::AccountDirectory() + "/gxs"; std::string currGxsDir = RsAccounts::AccountDirectory() + "/gxs";
RsDirUtil::checkCreateDirectory(currGxsDir); RsDirUtil::checkCreateDirectory(currGxsDir);
RsNxsNetMgr* nxsMgr = new RsNxsNetMgrImpl(serviceCtrl); RsNxsNetMgr* nxsMgr = new RsNxsNetMgrImpl(serviceCtrl);

View File

@ -83,7 +83,13 @@ void JsonApiPage::load()
whileBlocking(ui.enableCheckBox)->setChecked(Settings->getJsonApiEnabled()); whileBlocking(ui.enableCheckBox)->setChecked(Settings->getJsonApiEnabled());
whileBlocking(ui.portSpinBox)->setValue(Settings->getJsonApiPort()); whileBlocking(ui.portSpinBox)->setValue(Settings->getJsonApiPort());
whileBlocking(ui.listenAddressLineEdit)->setText(Settings->getJsonApiListenAddress()); whileBlocking(ui.listenAddressLineEdit)->setText(Settings->getJsonApiListenAddress());
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(Settings->getJsonApiAuthTokens()));
QStringList newTk;
for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
} }
QString JsonApiPage::helpText() const { return ""; } QString JsonApiPage::helpText() const { return ""; }
@ -93,52 +99,38 @@ QString JsonApiPage::helpText() const { return ""; }
checkShutdownJsonApi(); checkShutdownJsonApi();
if(Settings->getJsonApiEnabled()) if(Settings->getJsonApiEnabled())
{ JsonApiServer::instance().start( Settings->getJsonApiPort(), Settings->getJsonApiListenAddress().toStdString() );
jsonApiServer = new JsonApiServer(
Settings->getJsonApiPort(),
Settings->getJsonApiListenAddress().toStdString() );
jsonApiServer->start("jsonApiServer");
for(const QString& token : Settings->getJsonApiAuthTokens())
jsonApiServer->authorizeToken(token.toStdString());
}
return true; return true;
} }
/*static*/ void JsonApiPage::checkShutdownJsonApi() /*static*/ void JsonApiPage::checkShutdownJsonApi()
{ {
if(jsonApiServer) JsonApiServer::instance().shutdown();
{
/* It is important to make a copy of +jsonApiServer+ pointer so the old /* It is important to make a copy of +jsonApiServer+ pointer so the old
* object can be deleted later, while the original pointer is * object can be deleted later, while the original pointer is
* reassigned */ * reassigned */
JsonApiServer* oldJsonApiServer = jsonApiServer;
jsonApiServer = nullptr;
oldJsonApiServer->shutdown(); QProgressDialog* pd = new QProgressDialog(
"Stopping JSON API Server", QString(), 0, 3000);
QTimer* prtm = new QTimer;
prtm->setInterval(16); // 60 FPS
connect( prtm, &QTimer::timeout,
pd, [=](){pd->setValue(pd->value()+16);} );
pd->show();
prtm->start();
QProgressDialog* pd = new QProgressDialog( /* Must wait for deletion because stopping of the server is async.
"Stopping JSON API Server", QString(), 0, 3000);
QTimer* prtm = new QTimer;
prtm->setInterval(16); // 60 FPS
connect( prtm, &QTimer::timeout,
pd, [=](){pd->setValue(pd->value()+16);} );
pd->show();
prtm->start();
/* Must wait for deletion because stopping of the server is async.
* It is important to capture a copy so it "survive" after * It is important to capture a copy so it "survive" after
* safeStopJsonApiServer returns */ * safeStopJsonApiServer returns */
QTimer::singleShot(3*1000, [=]() QTimer::singleShot(3*1000, [=]()
{ {
delete oldJsonApiServer; prtm->stop();
prtm->stop(); pd->close();
pd->close(); prtm->deleteLater();
prtm->deleteLater(); pd->deleteLater();
pd->deleteLater(); });
});
}
} }
void JsonApiPage::onApplyClicked(bool) void JsonApiPage::onApplyClicked(bool)
@ -150,23 +142,27 @@ void JsonApiPage::onApplyClicked(bool)
void JsonApiPage::addTokenClicked(bool) void JsonApiPage::addTokenClicked(bool)
{ {
QString token(ui.tokenLineEdit->text()); QString token(ui.tokenLineEdit->text());
if(jsonApiServer) jsonApiServer->authorizeToken(token.toStdString()); JsonApiServer::instance().authorizeToken(token.toStdString());
QStringList newTk(Settings->getJsonApiAuthTokens());
newTk.removeAll(token); QStringList newTk;
newTk.append(token);
Settings->setJsonApiAuthTokens(newTk); for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk)); whileBlocking(ui.tokensListView)->setModel(new QStringListModel(newTk));
} }
void JsonApiPage::removeTokenClicked(bool) void JsonApiPage::removeTokenClicked(bool)
{ {
QString token(ui.tokenLineEdit->text()); QString token(ui.tokenLineEdit->text());
if(jsonApiServer) jsonApiServer->revokeAuthToken(token.toStdString()); JsonApiServer::instance().revokeAuthToken(token.toStdString());
QStringList newTk(Settings->getJsonApiAuthTokens());
newTk.removeAll(token); QStringList newTk;
Settings->setJsonApiAuthTokens(newTk);
whileBlocking(ui.tokensListView)->setModel( for(const auto& it : JsonApiServer::instance().getAuthorizedTokens())
new QStringListModel(Settings->getJsonApiAuthTokens()) ); newTk.push_back(QString::fromStdString(it.first)+":"+QString::fromStdString(it.second)) ;
whileBlocking(ui.tokensListView)->setModel(new QStringListModel(Settings->getJsonApiAuthTokens()) );
} }
void JsonApiPage::tokenClicked(const QModelIndex& index) void JsonApiPage::tokenClicked(const QModelIndex& index)

View File

@ -30,6 +30,9 @@
#include "rsharesettings.h" #include "rsharesettings.h"
#include "gui/MainWindow.h" #include "gui/MainWindow.h"
// for this one, we'd rather use a file in retroshare/*h list.
#include <jsonapi/jsonapi.h>
#include <retroshare/rsnotify.h> #include <retroshare/rsnotify.h>
#include <retroshare/rspeers.h> #include <retroshare/rspeers.h>
@ -1208,7 +1211,7 @@ void RshareSettings::setJsonApiEnabled(bool enabled)
uint16_t RshareSettings::getJsonApiPort() uint16_t RshareSettings::getJsonApiPort()
{ {
return valueFromGroup("JsonApi", "port", 9092).toUInt(); return valueFromGroup("JsonApi", "port", JsonApiServer::DEFAULT_PORT).toUInt();
} }
void RshareSettings::setJsonApiPort(uint16_t port) void RshareSettings::setJsonApiPort(uint16_t port)

View File

@ -233,8 +233,6 @@ feenableexcept(FE_INVALID | FE_DIVBYZERO);
RsConfigOptions conf; RsConfigOptions conf;
conf.jsonApiPort = 0 ; // disable JSon API at start. The JSonAPI preference UI will enable it according to saved parameters.
argstream as(argc,argv); argstream as(argc,argv);
as >> option('s',"stderr" ,conf.outStderr ,"output to stderr instead of log file." ) as >> option('s',"stderr" ,conf.outStderr ,"output to stderr instead of log file." )
>> option('u',"udp" ,conf.udpListenerOnly,"Only listen to UDP." ) >> option('u',"udp" ,conf.udpListenerOnly,"Only listen to UDP." )