mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-07-29 09:18:45 -04:00
added resource_api and rs-nogui-webui (requires libmicrohttpd, Html files are not included)
git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@8047 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
parent
97831d0667
commit
ad1bc7f3b8
38 changed files with 5547 additions and 2 deletions
351
libresapi/src/api/ApiServer.cpp
Normal file
351
libresapi/src/api/ApiServer.cpp
Normal file
|
@ -0,0 +1,351 @@
|
|||
#include "ApiServer.h"
|
||||
|
||||
#include <retroshare/rspeers.h>
|
||||
#include <retroshare/rsmsgs.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sstream>
|
||||
#include "json.h"
|
||||
|
||||
#include <retroshare/rsservicecontrol.h>
|
||||
#ifdef FIXME
|
||||
#include "rswall.h"
|
||||
#endif
|
||||
#include "JsonStream.h"
|
||||
#include "StateTokenServer.h" // for the state token serialisers
|
||||
|
||||
/*
|
||||
data types in json http://json.org/
|
||||
string (utf-8 unicode)
|
||||
number (int and float)
|
||||
object (key value pairs, key must be a string)
|
||||
true
|
||||
false
|
||||
null
|
||||
|
||||
data types in lua http://www.lua.org/pil/2.html
|
||||
nil
|
||||
boolean
|
||||
number (double)
|
||||
string (8-bit)
|
||||
table (key value pairs, keys can be anything except nil)
|
||||
|
||||
data types in QML http://qt-project.org/doc/qt-5/qtqml-typesystem-basictypes.html
|
||||
bool
|
||||
string
|
||||
real/double
|
||||
int
|
||||
list
|
||||
object types?
|
||||
|
||||
QML has many more types with special meaning like date
|
||||
|
||||
|
||||
C++ delivers
|
||||
std::string
|
||||
bool
|
||||
int
|
||||
(double? i don't know)
|
||||
enum
|
||||
bitflags
|
||||
raw binary data
|
||||
|
||||
objects
|
||||
std::vector
|
||||
std::list
|
||||
|
||||
different types of ids/hashes
|
||||
-> convert to/from string with a generic operator
|
||||
-> the operator signals ok/fail to the stream
|
||||
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
data types to handle:
|
||||
- bool
|
||||
- string
|
||||
- bitflags
|
||||
- enums
|
||||
|
||||
containers:
|
||||
- arrays: collection of objects or values without name, usually of the same type
|
||||
- objects: objects and values with names
|
||||
|
||||
careful: the json lib has many asserts, so retroshare will stop if the smalles thing goes wrong
|
||||
-> check type of json before usage
|
||||
|
||||
there are two possible implementations:
|
||||
- with a virtual base class for the serialisation targets
|
||||
- better documentation of the interface
|
||||
- with templates
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
the general idea is:
|
||||
want output in many different formats, while the retrival of the source data is always the same
|
||||
|
||||
get, put
|
||||
|
||||
ressource adress like
|
||||
.org.retroshare.api.peers
|
||||
|
||||
generic router from adress to the ressource handler object
|
||||
|
||||
data formats for input and output:
|
||||
- json
|
||||
- lua
|
||||
- QObject
|
||||
- maybe just a typed c++ object
|
||||
|
||||
rest inspired / resource based interface
|
||||
- have resources with adresses
|
||||
- requests:
|
||||
- put
|
||||
- get
|
||||
- request has parameters
|
||||
- response:
|
||||
- returncode:
|
||||
- ok
|
||||
- not modified
|
||||
- error
|
||||
- data = object or list of objects
|
||||
|
||||
want to have a typesafe interface at the top?
|
||||
probably not, a system with generic return values is enough
|
||||
this interface is for scripting languages which don't have types
|
||||
|
||||
the interface at the top should look like this:
|
||||
template <class RequestDataFormatT, class ResponseDataFormatT>
|
||||
processRequest(const RequestMeta& req, const RequestDataFormatT& reqData,
|
||||
ResponseMeta& respMeta, ResponseDataFormatT& respData);
|
||||
|
||||
idea: pass all the interfaces to the retroshare core to this function,
|
||||
or have this function as part of an object
|
||||
|
||||
the processor then applies all members of the request and response data to the data format like this:
|
||||
reqData << "member1" << member1
|
||||
<< "member2" << member2 ... ;
|
||||
|
||||
these operators have to be implemented for common things like boolean, int, std::string, std::vector, std::list ...
|
||||
request data gets only deserialised
|
||||
response data gets only serialised
|
||||
|
||||
response and request meta contains things like resource address, method and additional parameters
|
||||
|
||||
want generic resource caching mechanism
|
||||
- on first request a request handler is created
|
||||
- request handler is stored with its input data
|
||||
- if a request handler for a given resource adress and parameters exists
|
||||
then the request handler is asked if the result is still valid
|
||||
if yes the result from the existing handler is used
|
||||
- request handler gets deleted after timeout
|
||||
- can have two types of resource handlers: static handlers and dynamic handlers
|
||||
- static handlers don't get deleted, because they don't contain result data
|
||||
- dynamic handlers contain result data, and thus get deleted after a while
|
||||
|
||||
it is even possible to implement a resource-changed check at the highest level
|
||||
this allows to compute everything on the server side and only send chanes to the client
|
||||
the different resource providers don't have to implement a resource changed check then
|
||||
a top level change detector will poll them
|
||||
of course this does not wokr with a deep resource tree with millions of nodes
|
||||
|
||||
for this we have the dynamic handlers,
|
||||
they are created on demand and know how to listen for changes which affect them
|
||||
|
||||
*/
|
||||
|
||||
namespace resource_api{
|
||||
|
||||
// old code, only to copy and paste from
|
||||
// to be removed
|
||||
/*
|
||||
class ChatlobbiesHandler
|
||||
{
|
||||
public:
|
||||
ChatlobbiesHandler(RsMsgs* msgs): mMsgs(msgs) {}
|
||||
|
||||
template <class InputT, class OutputT>
|
||||
void handleRequest(Request& req, InputT& reqData, Response& resp, OutputT& respData)
|
||||
{
|
||||
if(req.mMethod == "GET")
|
||||
{
|
||||
typename OutputT::Array result;
|
||||
// subscribed lobbies
|
||||
std::list<ChatLobbyInfo> slobbies;
|
||||
mMsgs->getChatLobbyList(slobbies);
|
||||
for(std::list<ChatLobbyInfo>::iterator lit = slobbies.begin(); lit != slobbies.end(); lit++)
|
||||
{
|
||||
typename OutputT::Object lobby;
|
||||
ChatLobbyInfo& lobbyRecord = *lit;
|
||||
lobby["name"] = lobbyRecord.lobby_name;
|
||||
RsPeerId pid;
|
||||
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
|
||||
lobby["id"] = pid.toStdString();
|
||||
lobby["subscribed"] = true;
|
||||
result.push_back(lobby);
|
||||
}
|
||||
// unsubscirbed lobbies
|
||||
std::vector<VisibleChatLobbyRecord> ulobbies;
|
||||
mMsgs->getListOfNearbyChatLobbies(ulobbies);
|
||||
for(std::vector<VisibleChatLobbyRecord>::iterator vit = ulobbies.begin(); vit != ulobbies.end(); vit++)
|
||||
{
|
||||
typename OutputT::Object lobby;
|
||||
VisibleChatLobbyRecord& lobbyRecord = *vit;
|
||||
lobby["name"] = lobbyRecord.lobby_name;
|
||||
RsPeerId pid;
|
||||
mMsgs->getVirtualPeerId(lobbyRecord.lobby_id, pid);
|
||||
lobby["id"] = pid.toStdString();
|
||||
lobby["subscribed"] = false;
|
||||
result.push_back(lobby);
|
||||
}
|
||||
respData = result;
|
||||
}
|
||||
else if(req.mMethod == "PUT")
|
||||
{
|
||||
RsPeerId id = RsPeerId(req.mAdress.substr(1));
|
||||
|
||||
if(!id.isNull() && reqData.HasKey("msg"))
|
||||
{
|
||||
// for now can send only id as message
|
||||
mMsgs->sendPrivateChat(id, reqData["msg"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RsMsgs* mMsgs;
|
||||
};
|
||||
*/
|
||||
|
||||
class ApiServerMainModules
|
||||
{
|
||||
public:
|
||||
ApiServerMainModules(ResourceRouter& router, StateTokenServer* sts, const RsPlugInInterfaces &ifaces):
|
||||
mPeersHandler(sts, ifaces.mNotify, ifaces.mPeers, ifaces.mMsgs),
|
||||
mIdentityHandler(ifaces.mIdentity),
|
||||
mServiceControlHandler(rsServiceControl), // TODO: don't use global variable here
|
||||
mFileSearchHandler(sts, ifaces.mNotify, ifaces.mTurtle, ifaces.mFiles),
|
||||
mTransfersHandler(sts, ifaces.mFiles)
|
||||
{
|
||||
// the dynamic cast is to not confuse the addResourceHandler template like this:
|
||||
// addResourceHandler(derived class, parent class)
|
||||
// the template would then be instantiated using derived class as parameter
|
||||
// and then parent class would not match the type
|
||||
router.addResourceHandler("peers",dynamic_cast<ResourceRouter*>(&mPeersHandler),
|
||||
&PeersHandler::handleRequest);
|
||||
router.addResourceHandler("identity", dynamic_cast<ResourceRouter*>(&mIdentityHandler),
|
||||
&IdentityHandler::handleRequest);
|
||||
router.addResourceHandler("servicecontrol", dynamic_cast<ResourceRouter*>(&mServiceControlHandler),
|
||||
&ServiceControlHandler::handleRequest);
|
||||
router.addResourceHandler("filesearch", dynamic_cast<ResourceRouter*>(&mFileSearchHandler),
|
||||
&FileSearchHandler::handleRequest);
|
||||
router.addResourceHandler("transfers", dynamic_cast<ResourceRouter*>(&mTransfersHandler),
|
||||
&TransfersHandler::handleRequest);
|
||||
}
|
||||
|
||||
PeersHandler mPeersHandler;
|
||||
IdentityHandler mIdentityHandler;
|
||||
ServiceControlHandler mServiceControlHandler;
|
||||
FileSearchHandler mFileSearchHandler;
|
||||
TransfersHandler mTransfersHandler;
|
||||
};
|
||||
#ifdef FIXME
|
||||
class ApiServerWallModule
|
||||
{
|
||||
public:
|
||||
ApiServerWallModule(ResourceRouter& router, const RsPlugInInterfaces& ifaces, RsWall::RsWall* wall):
|
||||
mWallHandler(wall, ifaces.mIdentity)
|
||||
{
|
||||
router.addResourceHandler("wall", dynamic_cast<ResourceRouter*>(&mWallHandler),
|
||||
&WallHandler::handleRequest);
|
||||
}
|
||||
|
||||
WallHandler mWallHandler;
|
||||
};
|
||||
#endif
|
||||
ApiServer::ApiServer():
|
||||
mStateTokenServer(),
|
||||
mMainModules(0),
|
||||
mWallModule(0)
|
||||
{
|
||||
mRouter.addResourceHandler("statetokenservice", dynamic_cast<ResourceRouter*>(&mStateTokenServer),
|
||||
&StateTokenServer::handleRequest);
|
||||
}
|
||||
|
||||
ApiServer::~ApiServer()
|
||||
{
|
||||
if(mMainModules)
|
||||
delete mMainModules;
|
||||
if(mWallModule)
|
||||
delete mWallModule;
|
||||
}
|
||||
|
||||
void ApiServer::loadMainModules(const RsPlugInInterfaces &ifaces)
|
||||
{
|
||||
if(mMainModules == 0)
|
||||
mMainModules = new ApiServerMainModules(mRouter, &mStateTokenServer, ifaces);
|
||||
}
|
||||
#ifdef FIXME
|
||||
void ApiServer::loadWallModule(const RsPlugInInterfaces &ifaces, RsWall::RsWall* wall)
|
||||
{
|
||||
if(mWallModule == 0)
|
||||
mWallModule = new ApiServerWallModule(mRouter, ifaces, wall);
|
||||
}
|
||||
#endif
|
||||
std::string ApiServer::handleRequest(Request &request)
|
||||
{
|
||||
resource_api::JsonStream outstream;
|
||||
std::stringstream debugString;
|
||||
|
||||
StreamBase& data = outstream.getStreamToMember("data");
|
||||
resource_api::Response resp(data, debugString);
|
||||
|
||||
ResponseTask* task = mRouter.handleRequest(request, resp);
|
||||
|
||||
time_t start = time(NULL);
|
||||
while(task && task->doWork(request, resp))
|
||||
{
|
||||
usleep(10*1000);
|
||||
/*if(time(NULL) > (start+5))
|
||||
{
|
||||
std::cerr << "ApiServer::handleRequest() Error: task timed out" << std::endl;
|
||||
resp.mDebug << "Error: task timed out." << std::endl;
|
||||
resp.mReturnCode = resource_api::Response::FAIL;
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
if(task)
|
||||
delete task;
|
||||
|
||||
std::string returncode;
|
||||
switch(resp.mReturnCode){
|
||||
case resource_api::Response::NOT_SET:
|
||||
returncode = "not_set";
|
||||
break;
|
||||
case resource_api::Response::OK:
|
||||
returncode = "ok";
|
||||
break;
|
||||
case resource_api::Response::WARNING:
|
||||
returncode = "warning";
|
||||
break;
|
||||
case resource_api::Response::FAIL:
|
||||
returncode = "fail";
|
||||
break;
|
||||
}
|
||||
|
||||
// evil HACK, remove this
|
||||
if(data.isRawData())
|
||||
return data.getRawData();
|
||||
|
||||
outstream << resource_api::makeKeyValue("debug_msg", debugString.str());
|
||||
outstream << resource_api::makeKeyValueReference("returncode", returncode);
|
||||
if(!resp.mStateToken.isNull())
|
||||
outstream << resource_api::makeKeyValueReference("statetoken", resp.mStateToken);
|
||||
return outstream.getJsonString();
|
||||
}
|
||||
|
||||
} // namespace resource_api
|
Loading…
Add table
Add a link
Reference in a new issue