changed layout of restbed/json/webui so that JsonApi is the only server (with thread functionality in a separate class) and webui is a resource provider

This commit is contained in:
csoler 2019-11-23 00:17:17 +01:00
parent 997501a24d
commit 009ed54ce2
No known key found for this signature in database
GPG Key ID: 7BCA522266C0804C
8 changed files with 156 additions and 170 deletions

View File

@ -376,33 +376,6 @@ JsonApiServer::JsonApiServer(): configMutex("JsonApiServer config")
#include "jsonapi-wrappers.inl" #include "jsonapi-wrappers.inl"
} }
void JsonApiServer::run()
{
std::shared_ptr<rb::Settings> settings(new rb::Settings);
settings->set_port(mPort);
settings->set_bind_address(mBindAddress);
settings->set_default_header("Cache-Control", "no-cache");
{
sockaddr_storage tmp;
sockaddr_storage_inet_pton(tmp, mBindAddress);
sockaddr_storage_setport(tmp, mPort);
sockaddr_storage_ipv6_to_ipv4(tmp);
RsUrl tmpUrl(sockaddr_storage_tostring(tmp));
tmpUrl.setScheme("http");
std::cerr << "JSON API listening on " << tmpUrl.toString()
<< std::endl;
}
mService.start(settings);
}
std::vector<std::shared_ptr<restbed::Resource> > JsonApiServer::getResources() const
{
return _resources;
}
void JsonApiServer::registerHandler( void JsonApiServer::registerHandler(
const std::string& path, const std::string& path,
const std::function<void (const std::shared_ptr<restbed::Session>)>& handler, const std::function<void (const std::shared_ptr<restbed::Session>)>& handler,
@ -630,8 +603,32 @@ void JsonApiServer::handleCorsOptions(
int JsonApiServer::status() const int JsonApiServer::status() const
{ {
if(isRunning()) if(RestbedService::isRunning() && RestbedService::isClient(this))
return JSONAPI_STATUS_RUNNING; return JSONAPI_STATUS_RUNNING;
else else
return JSONAPI_STATUS_NOT_RUNNING; return JSONAPI_STATUS_NOT_RUNNING;
} }
void JsonApiServer::registerResourceProvider(const JsonApiResourceProvider *rp)
{
_resource_providers.insert(rp);
}
void JsonApiServer::unregisterResourceProvider(const JsonApiResourceProvider *rp)
{
_resource_providers.erase(rp);
}
bool JsonApiServer::hasResourceProvider(const JsonApiResourceProvider *rp)
{
return _resource_providers.find(rp) != _resource_providers.end();
}
std::vector<std::shared_ptr<rb::Resource> > JsonApiServer::getResources() const
{
auto tab = _resources;
for(auto& rp: _resource_providers)
for(auto r: rp->getResources())
tab.push_back(r);
return tab;
}

View File

@ -24,6 +24,7 @@
#include <restbed> #include <restbed>
#include <cstdint> #include <cstdint>
#include <map> #include <map>
#include <set>
#include "util/rsthreads.h" #include "util/rsthreads.h"
#include "pqi/p3cfgmgr.h" #include "pqi/p3cfgmgr.h"
@ -35,6 +36,16 @@
namespace rb = restbed; namespace rb = restbed;
class JsonApiResourceProvider
{
public:
JsonApiResourceProvider() {}
virtual ~JsonApiResourceProvider() = default;
virtual std::string getName() const =0;
virtual std::vector<std::shared_ptr<rb::Resource> > getResources() const =0;
};
/** /**
* Simple usage * Simple usage
* \code{.cpp} * \code{.cpp}
@ -50,13 +61,18 @@ public:
JsonApiServer() ; JsonApiServer() ;
virtual ~JsonApiServer() = default; virtual ~JsonApiServer() = default;
// public API // implements RestbedService
virtual bool restart() override { return RestbedService::restart();} virtual std::vector<std::shared_ptr<rb::Resource> > getResources() const override ;
virtual bool stop() override { return RestbedService::stop();}
virtual void setListeningPort(uint16_t port) override { RestbedService::setListeningPort(port) ;} // RsJsonAPI public API
virtual void setBindingAddress(const std::string& address) override { RestbedService::setBindAddress(address); }
virtual int status() const override; bool restart() override { return RestbedService::restart(); }
bool stop() override { return RestbedService::stop();}
int status() const override;
void setListeningPort(uint16_t port) override { return RestbedService::setListeningPort(port); }
void setBindingAddress(const std::string& bind_address) override { return RestbedService::setBindAddress(bind_address); }
virtual void connectToConfigManager(p3ConfigMgr *cfgmgr); virtual void connectToConfigManager(p3ConfigMgr *cfgmgr);
@ -66,6 +82,10 @@ public:
bool isAuthTokenValid(const std::string& token) override; bool isAuthTokenValid(const std::string& token) override;
bool requestNewTokenAutorization(const std::string& user) override; bool requestNewTokenAutorization(const std::string& user) override;
void registerResourceProvider(const JsonApiResourceProvider *);
void unregisterResourceProvider(const JsonApiResourceProvider *);
bool hasResourceProvider(const JsonApiResourceProvider *);
// private API. These methods may be moved to RsJsonAPI so as to be visible in http mode, if needed. // private API. These methods may be moved to RsJsonAPI so as to be visible in http mode, if needed.
/** /**
@ -123,7 +143,6 @@ public:
protected: protected:
/// @see RsSingleJobThread /// @see RsSingleJobThread
virtual void run(); virtual void run();
virtual std::vector<std::shared_ptr<rb::Resource> > getResources() const;
private: private:
@ -141,7 +160,6 @@ private:
uint16_t mPort; uint16_t mPort;
std::string mBindAddress; std::string mBindAddress;
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
/// The callback supplies the password to be used to make the token /// The callback supplies the password to be used to make the token
@ -167,10 +185,10 @@ private:
RsGenericSerializer::SerializeContext& ctx, RsGenericSerializer::SerializeContext& ctx,
const std::shared_ptr<rb::Session> session ) const std::shared_ptr<rb::Session> session )
{ {
return checkRsServicePtrReady( return checkRsServicePtrReady( serviceInstance.get(), serviceName, ctx, session );
serviceInstance.get(), serviceName, ctx, session );
} }
std::vector<std::shared_ptr<rb::Resource> > _resources; std::vector<std::shared_ptr<rb::Resource> > _resources;
std::set<JsonApiResourceProvider *> _resource_providers;
}; };

View File

@ -24,127 +24,63 @@
#include "util/rsdebug.h" #include "util/rsdebug.h"
#include "restbedservice.h" #include "restbedservice.h"
class RestbedThread: public RsThread
{
public:
RestbedThread()
{
_service = std::make_shared<restbed::Service>(); // this is a place holder, in case we request some internal values.
_listening_port = 1984;
_binding_address = "127.0.0.1";
}
void runloop() override
{
if(_resources.empty())
{
RsErr() << "(EE) please call RestbedService::setResources() before launching the service!" << std::endl;
return;
}
auto settings = std::make_shared< restbed::Settings >( );
settings->set_port( _listening_port );
settings->set_bind_address( _binding_address );
settings->set_default_header( "Connection", "close" );
if(_service->is_up())
{
std::cerr << "(II) WebUI is already running. Killing it." << std::endl;
_service->stop();
}
_service = std::make_shared<restbed::Service>();
for(auto& r:_resources)
_service->publish( r );
try
{
std::cerr << "(II) Starting restbed service on port " << std::dec << _listening_port << " and binding address \"" << _binding_address << "\"" << std::endl;
_service->start( settings );
}
catch(std::exception& e)
{
RsErr() << "Could not start web interface: " << e.what() << std::endl;
return;
}
std::cerr << "(II) restbed service stopped." << std::endl;
}
void stop()
{
_service->stop();
RsThread::ask_for_stop();
while(isRunning())
{
std::cerr << "(II) shutting down restbed service." << std::endl;
rstime::rs_usleep(1000*1000);
}
}
void setListeningPort(uint16_t p) { _listening_port = p ; }
void setBindAddress(const std::string& bindAddress) { _binding_address = bindAddress ; }
void setResources(const std::vector<std::shared_ptr<restbed::Resource> >& r) { _resources = r ; }
uint16_t listeningPort() const { return _listening_port;}
private:
std::shared_ptr<restbed::Service> _service;
std::vector<std::shared_ptr<restbed::Resource> > _resources;
uint16_t _listening_port;
std::string _binding_address;
};
RestbedService::RestbedService() RestbedService::RestbedService()
{ {
_restbed_thread = new RestbedThread(); mService = std::make_shared<restbed::Service>(); // this is a place holder, in case we request some internal values.
mListeningPort = 9092;
mBindingAddress = "127.0.0.1";
} }
RestbedService::~RestbedService()
void RestbedService::stop()
{ {
while(_restbed_thread->isRunning()) mService->stop();
{
stop(); RsThread::ask_for_stop();
std::cerr << "Deleting webUI object while webUI thread is still running. Trying shutdown...." << std::endl;
while(isRunning())
{
std::cerr << "(II) shutting down restbed service." << std::endl;
rstime::rs_usleep(1000*1000); rstime::rs_usleep(1000*1000);
} }
delete _restbed_thread;
} }
bool RestbedService::restart() void RestbedService::setListeningPort(uint16_t p) { mListeningPort = p ; }
void RestbedService::setBindAddress(const std::string& bindAddress) { mBindingAddress = bindAddress ; }
uint16_t RestbedService::listeningPort() const { return mListeningPort;}
void RestbedService::runloop() override
{ {
RsDbg() << "Restarting restbed service listening on port " << _restbed_thread->listeningPort() << std::endl; if(_resources.empty())
{
RsErr() << "(EE) please call RestbedService::setResources() before launching the service!" << std::endl;
return;
}
auto settings = std::make_shared< restbed::Settings >( );
settings->set_port( mListeningPort );
settings->set_bind_address( mBindingAddress );
settings->set_default_header( "Connection", "close" );
if(_restbed_thread->isRunning()) if(mService->is_up())
_restbed_thread->stop(); {
std::cerr << "(II) WebUI is already running. Killing it." << std::endl;
mService->stop();
}
_restbed_thread->setResources(getResources()); mService = std::make_shared<restbed::Service>(); // re-allocating mService is important because it deletes the existing service and therefore leaves the listening port open
_restbed_thread->start();
return true; for(auto& r:getResources())
mService->publish(r);
try
{
std::cerr << "(II) Starting restbed service on port " << std::dec << mListeningPort << " and binding address \"" << mBindingAddress << "\"" << std::endl;
mService->start( settings );
}
catch(std::exception& e)
{
RsErr() << "Could not start web interface: " << e.what() << std::endl;
return;
}
std::cerr << "(II) restbed service stopped." << std::endl;
} }
bool RestbedService::stop()
{
_restbed_thread->stop();
return true;
}
bool RestbedService::isRunning() const
{
return _restbed_thread->isRunning();
}
void RestbedService::setListeningPort(uint16_t port)
{
_restbed_thread->setListeningPort(port);
if(_restbed_thread->isRunning())
restart();
}
void RestbedService::setBindAddress(const std::string& bind_address)
{
_restbed_thread->setBindAddress(bind_address);
if(_restbed_thread->isRunning())
restart();
}

View File

@ -23,26 +23,34 @@
#pragma once #pragma once
#include <restbed> #include <restbed>
#include "util/rsthreads.h"
class RestbedThread; class RestbedThread;
class RestbedService class RestbedService: public RsThread
{ {
public: public:
RestbedService() ; RestbedService() ;
virtual ~RestbedService(); virtual ~RestbedService();
bool isRunning() const ; bool restart();
bool stop();
bool isRunning();
virtual bool restart(); void setListeningPort(uint16_t port) ;
virtual bool stop(); void setBindAddress(const std::string& bind_address);
virtual void setListeningPort(uint16_t port) ; // should be overloaded by sub-class in order to provide resources to the restbed Service.
virtual void setBindAddress(const std::string& bind_address);
virtual std::vector<std::shared_ptr<restbed::Resource> > getResources()const =0; virtual std::vector<std::shared_ptr<restbed::Resource> > getResources() const = 0;
protected:
void runloop() override;
private: private:
RestbedThread *_restbed_thread; std::shared_ptr<restbed::Service> mService; // managed by RestbedService because it needs to be properly deleted when restarted.
uint16_t mListeningPort;
std::string mBindingAddress;
}; };

View File

@ -23,6 +23,7 @@
#include <string> #include <string>
class p3ConfigMgr; class p3ConfigMgr;
class JsonApiResourceProvider;
class RsJsonAPI class RsJsonAPI
{ {
@ -44,6 +45,10 @@ public:
virtual void connectToConfigManager(p3ConfigMgr *cfgmgr)=0; virtual void connectToConfigManager(p3ConfigMgr *cfgmgr)=0;
virtual void registerResourceProvider(const JsonApiResourceProvider *)=0;
virtual void unregisterResourceProvider(const JsonApiResourceProvider *)=0;
virtual bool hasResourceProvider(const JsonApiResourceProvider *)=0;
/** /**
* @brief Get status of the json api server * @brief Get status of the json api server
* @jsonapi{development} * @jsonapi{development}

View File

@ -136,7 +136,7 @@ void p3WebUI::setHtmlFilesDirectory(const std::string& html_dir)
int p3WebUI::status() const int p3WebUI::status() const
{ {
if(isRunning()) if(rsJsonAPI->status()==RsJsonAPI::JSONAPI_STATUS_RUNNING && rsJsonAPI->hasResourceProvider(this))
return WEBUI_STATUS_RUNNING; return WEBUI_STATUS_RUNNING;
else else
return WEBUI_STATUS_NOT_RUNNING; return WEBUI_STATUS_NOT_RUNNING;
@ -153,3 +153,22 @@ void p3WebUI::setUserPassword(const std::string& passwd)
std::cerr << "(EE) JsonAPI is not available in this buildof Retroshare! Cannot register a user password for the WebUI" << std::endl; std::cerr << "(EE) JsonAPI is not available in this buildof Retroshare! Cannot register a user password for the WebUI" << std::endl;
#endif #endif
} }
bool p3WebUI::restart()
{
rsJsonAPI->registerResourceProvider(this);
return rsJsonAPI->restart();
}
bool p3WebUI::stop()
{
rsJsonAPI->unregisterResourceProvider(this);
if(rsJsonAPI->status()==RsJsonAPI::JSONAPI_STATUS_RUNNING)
return rsJsonAPI->restart();
else
return true;
}

View File

@ -24,23 +24,26 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include "retroshare/rswebui.h" #include "retroshare/rswebui.h"
#include "jsonapi/restbedservice.h" #include "jsonapi/jsonapi.h"
class p3WebUI: public RsWebUI, public RestbedService class p3WebUI: public RsWebUI, public JsonApiResourceProvider
{ {
public: public:
p3WebUI(){} p3WebUI(){}
virtual ~p3WebUI(){} virtual ~p3WebUI(){}
virtual void setHtmlFilesDirectory(const std::string& html_dir) override; // implements RsWebUI
virtual bool restart() override { return RestbedService::restart();} virtual void setHtmlFilesDirectory(const std::string& html_dir) override;
virtual bool stop() override { return RestbedService::stop();}
virtual void setListeningPort(uint16_t port) override { RestbedService::setListeningPort(port) ;}
virtual void setBindingAddress(const std::string& address) override { RestbedService::setBindAddress(address) ;}
virtual void setUserPassword(const std::string& passwd) override; virtual void setUserPassword(const std::string& passwd) override;
virtual int status() const override; virtual bool restart() override ;
virtual bool stop() override ;
virtual int status() const override;
// implements JsonApiResourceProvider
virtual std::string getName() const override { return "Web Interface" ;}
virtual std::vector<std::shared_ptr<restbed::Resource> > getResources() const override; virtual std::vector<std::shared_ptr<restbed::Resource> > getResources() const override;
}; };

View File

@ -398,9 +398,9 @@ int RsInit::InitRetroShare(const RsConfigOptions& conf)
RsInfo() << "Allocating jsonAPI server (not launched yet) " << std::endl; RsInfo() << "Allocating jsonAPI server (not launched yet) " << std::endl;
JsonApiServer *jas = new JsonApiServer(); JsonApiServer *jas = new JsonApiServer();
jas->setListeningPort(conf.jsonApiPort); jas->setListeningPort(conf.jsonApiPort);
jas->setBindingAddress(conf.jsonApiBindAddress); jas->setBindAddress(conf.jsonApiBindAddress);
if(conf.jsonApiPort != NULL) if(conf.jsonApiPort != 0)
{ {
RsInfo() << "Launching jsonAPI server on port " << conf.jsonApiPort << std::endl; RsInfo() << "Launching jsonAPI server on port " << conf.jsonApiPort << std::endl;
jas->restart(); jas->restart();