.
This commit is contained in:
+228
-140
@@ -11,18 +11,26 @@ namespace service {
|
||||
namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
using namespace httpserver;
|
||||
using namespace ::libvariant;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
class ResourceException : public std::runtime_error {
|
||||
|
||||
/**
|
||||
* Used for internal error handling.
|
||||
*/
|
||||
class ResourceException
|
||||
: public std::runtime_error {
|
||||
protected:
|
||||
int errorCode;
|
||||
public:
|
||||
ResourceException(std::string msg, int errorCode)
|
||||
: std::runtime_error(msg), errorCode(errorCode) {}
|
||||
|
||||
int getErrorCode() { return errorCode; }
|
||||
ResourceException(const std::string& msg, int errorCode)
|
||||
: std::runtime_error(msg), errorCode(errorCode) {}
|
||||
int getErrorCode() {
|
||||
return errorCode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,169 +40,249 @@ void DevicesHTTPResource::init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
WebServerService& service ) {
|
||||
this->prefixPath = prefixPath;
|
||||
disallow_all();
|
||||
set_allowing(MHD_HTTP_METHOD_GET, true);
|
||||
set_allowing(MHD_HTTP_METHOD_POST, true);
|
||||
webserver.register_resource(absolutePath, this, true);
|
||||
webResource.prefixPath = prefixPath;
|
||||
webResource.disallow_all();
|
||||
webResource.set_allowing(MHD_HTTP_METHOD_GET, true);
|
||||
webResource.set_allowing(MHD_HTTP_METHOD_POST, true);
|
||||
webserver.register_resource(absolutePath, &webResource, true);
|
||||
}
|
||||
|
||||
void DevicesHTTPResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
|
||||
|
||||
DevicesHTTPResource::WebResource::WebResource(devicerepository::DeviceRepositoryService* repoService)
|
||||
: repoService(repoService) {
|
||||
infoCategoryFuncs.insert({"type", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getDeviceType();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"status_topic", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getStatusTopic();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"command_topic", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getCmdTopic();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"current_state", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getCurrentState();
|
||||
} });
|
||||
infoCategoryFuncs.insert({"last_state", [](devicerepository::Device* device, const httpserver::http_request& req, const std::string& category)->Variant {
|
||||
return device->getLastState();
|
||||
} });
|
||||
/*infoCategoryFuncs.insert({"url", [this](devicerepository::Device* device, const std::string& category, const httpserver::http_request& req)->Variant {
|
||||
std::stringstream ss;
|
||||
req.
|
||||
ss << http
|
||||
return device->getLastState();
|
||||
} });*/
|
||||
}
|
||||
|
||||
|
||||
void DevicesHTTPResource::WebResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
std::cout << "DevicesHTTPResource::render_GET()" << std::endl;
|
||||
std::cout << req;
|
||||
|
||||
try {
|
||||
|
||||
if (req.get_path_pieces_size() < prefixPath.size() + 1) {
|
||||
ROS_ERROR_STREAM("Requested path " << req.get_path() << " has fewer pieces than prefix path.");
|
||||
throw ResourceException("Internal Server Error", 500);
|
||||
} else {
|
||||
|
||||
bool isSetCommand = false;
|
||||
Variant setValue;
|
||||
if (req.get_arg("command").compare("set") == 0) {
|
||||
isSetCommand = true;
|
||||
std::string value = req.get_arg("value");
|
||||
if (value.empty()) {
|
||||
throw ResourceException("No value given for set command.", 400);
|
||||
} else {
|
||||
boost::algorithm::trim(value);
|
||||
if (boost::starts_with(value, "{") || boost::starts_with(value, "[")) {
|
||||
setValue = DeserializeJSON(value);
|
||||
} else {
|
||||
setValue = Variant(value);
|
||||
}
|
||||
// Get method parameter
|
||||
DataMethod dataMethod = DataMethod::GET; // Default method is get
|
||||
std::string methodValue;
|
||||
std::string methodRaw = req.get_arg("method");
|
||||
if (!methodRaw.empty()) {
|
||||
if (methodRaw.compare("get") == 0) {
|
||||
// do nothing
|
||||
} else if (methodRaw.compare("command") == 0) {
|
||||
dataMethod = DataMethod::COMMAND;
|
||||
methodValue = req.get_arg("value");
|
||||
if (methodValue.empty()) {
|
||||
throw ResourceException((boost::format("Method parameter \"%1%\" requires a value parameter.") % methodRaw).str(), 400);
|
||||
}
|
||||
}
|
||||
|
||||
if (req.get_path_pieces_size() == prefixPath.size() + 1) {
|
||||
if (isSetCommand) {
|
||||
throw ResourceException("No device given for set command.", 400);
|
||||
} else if (methodRaw.compare("statusupdate") == 0) {
|
||||
dataMethod = DataMethod::STATUSUPDATE;
|
||||
methodValue = req.get_arg("value");
|
||||
if (methodValue.empty()) {
|
||||
throw ResourceException((boost::format("Method parameter \"%1%\" requires a value parameter.") % methodRaw).str(), 400);
|
||||
}
|
||||
Variant result;
|
||||
for (auto d : repoService->getDevices()) {
|
||||
result[d.lock()->getDeviceID()] = getDeviceInfo(d.lock().get());
|
||||
}
|
||||
*res = new http_response(http_response_builder(SerializeJSON(result, true), 200).string_response());
|
||||
} else {
|
||||
std::string devid = req.get_path_piece(prefixPath.size() + 2);
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
if (isSetCommand) {
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 2; i < req.get_path_pieces_size(); i++) {
|
||||
path.push_back(req.get_path_piece(i));
|
||||
}
|
||||
try {
|
||||
setDeviceState(device.lock().get(), setValue, path);
|
||||
*res = new http_response(http_response_builder("OK", 200).string_response());
|
||||
} catch (std::exception& e) {
|
||||
auto msg = (boost::format("ERROR: %1%") % e.what()).str();
|
||||
*res = new http_response(http_response_builder(msg , 400).string_response());
|
||||
}
|
||||
} else {
|
||||
std::string info;
|
||||
if (req.get_path_pieces_size() > prefixPath.size() + 2) {
|
||||
info = req.get_path_piece(prefixPath.size() + 2);
|
||||
}
|
||||
std::stringstream ss;
|
||||
for (int i = prefixPath.size() + 3; i < req.get_path_pieces_size(); i++) {
|
||||
ss << "/" << req.get_path_piece(i);
|
||||
}
|
||||
std::string path = ss.str();
|
||||
Variant result = getDeviceInfo(device.lock().get(), info, path);
|
||||
*res = new http_response(http_response_builder(SerializeJSON(result, true), 200).string_response());
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Could not find device \"%1%\"") % devid).str(), 404);
|
||||
}
|
||||
throw ResourceException((boost::format("Unknown method parameter \"%1%\"") % methodRaw).str(), 400);
|
||||
}
|
||||
}
|
||||
} catch (ResourceException& e) {
|
||||
*res = new http_response(http_response_builder(e.what(), e.getErrorCode()).string_response());
|
||||
} catch (std::exception& e) {
|
||||
*res = new http_response(http_response_builder(e.what(), 500).string_response());
|
||||
}
|
||||
|
||||
// No device given
|
||||
if (req.get_path_pieces_size() <= prefixPath.size() + 1) {
|
||||
if (dataMethod != DataMethod::GET) {
|
||||
throw ResourceException("Requested method requires a device.", 400);
|
||||
}
|
||||
Variant result;
|
||||
for (auto d : repoService->getDevices()) {
|
||||
auto dev = d.lock();
|
||||
result[dev->getDeviceID()] = getDeviceInfo(dev.get(), req);
|
||||
}
|
||||
*res = new http_response(http_response_builder(SerializeJSON(result, true), 200).string_response());
|
||||
|
||||
// Device given
|
||||
} else {
|
||||
std::string devid = req.get_path_piece(prefixPath.size() + 1);
|
||||
auto device = repoService->getDevice(devid);
|
||||
if (!device.expired()) {
|
||||
switch (dataMethod) {
|
||||
case DataMethod::GET:
|
||||
{
|
||||
std::string category = req.get_path_piece(prefixPath.size() + 2);
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 3; i < req.get_path_pieces_size(); i++) {
|
||||
path.push_back(req.get_path_piece(i));
|
||||
}
|
||||
Variant result = getDeviceInfo(device.lock().get(), req, category, path);
|
||||
std::string msg;
|
||||
switch (getDataFormat(req)) {
|
||||
case DataFormat::JSON:
|
||||
msg = SerializeJSON(result, true);
|
||||
break;
|
||||
case DataFormat::JSONP:
|
||||
{
|
||||
std::string padding = req.get_arg("callback");
|
||||
if (padding.empty()) {
|
||||
padding = req.get_arg("jsonp");
|
||||
}
|
||||
if (padding.empty()) {
|
||||
throw ResourceException("No callback parameter specified for JSONP.", 500);
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << padding << "(" << SerializeJSON(result, false) << ")";
|
||||
msg = ss.str();
|
||||
}
|
||||
break;
|
||||
case DataFormat::YAML:
|
||||
msg = SerializeYAML(result);
|
||||
break;
|
||||
case DataFormat::XMLPLIST:
|
||||
msg = SerializeXMLPLIST(result, true);
|
||||
break;
|
||||
case DataFormat::BUNDLEHDR:
|
||||
msg = SerializeBundleHdr(result);
|
||||
break;
|
||||
case DataFormat::MSGPACK:
|
||||
msg = Serialize(result, SERIALIZE_MSGPACK);
|
||||
break;
|
||||
default:
|
||||
throw ResourceException("Internal server error", 500);
|
||||
}
|
||||
*res = new http_response(http_response_builder(msg, 200).string_response());
|
||||
}
|
||||
break;
|
||||
case DataMethod::COMMAND:
|
||||
{
|
||||
std::vector<std::string> path;
|
||||
for (int i = prefixPath.size() + 2; i < req.get_path_pieces_size(); i++) {
|
||||
path.push_back(req.get_path_piece(i));
|
||||
}
|
||||
sendDeviceCommand(device.lock().get(), req, methodValue, path);
|
||||
*res = new http_response(http_response_builder("OK: Command send.", 200).string_response());
|
||||
}
|
||||
break;
|
||||
case DataMethod::STATUSUPDATE:
|
||||
throw ResourceException("ToDo: Implement method.", 500);
|
||||
default:
|
||||
throw ResourceException("Internal server error", 500);
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Could not find device \"%1%\"") % devid).str(), 404);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ResourceException& e) {
|
||||
*res = new http_response(http_response_builder((boost::format("ERROR: %1%") % e.what()).str(), e.getErrorCode()).string_response());
|
||||
} catch (std::exception& e) {
|
||||
*res = new http_response(http_response_builder((boost::format("ERROR: %1%") % e.what()).str(), 500).string_response());
|
||||
}
|
||||
|
||||
std::cout << **res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Variant DevicesHTTPResource::getDeviceInfo(devicerepository::Device* device, const std::string& info, const std::string& path) {
|
||||
Variant retval;
|
||||
if (info.empty()) {
|
||||
retval["type"] = device->getDeviceType();
|
||||
retval["status_topic"] = device->getStatusTopic();
|
||||
retval["command_topic"] = device->getCmdTopic();
|
||||
retval["current_state"] = device->getCurrentState();
|
||||
retval["last_state"] = device->getLastState();
|
||||
} else {
|
||||
if (info.compare("type") == 0) {
|
||||
if (path.empty()) {
|
||||
retval = device->getDeviceType();
|
||||
} else {
|
||||
throw ResourceException("Device info \"type\" has no path.", 400);
|
||||
}
|
||||
} else if (info.compare("status_topic") == 0) {
|
||||
if (path.empty()) {
|
||||
retval = device->getStatusTopic();
|
||||
} else {
|
||||
throw ResourceException("Device info \"status_topic\" has no path.", 400);
|
||||
}
|
||||
} else if (info.compare("command_topic") == 0) {
|
||||
if (path.empty()) {
|
||||
retval = device->getCmdTopic();
|
||||
} else {
|
||||
throw ResourceException("Device info \"command_topic\" has no path.", 400);
|
||||
}
|
||||
} else if (info.compare("current_state") == 0) {
|
||||
retval = device->getCurrentState();
|
||||
if (!path.empty()) {
|
||||
if (retval.HasPath(path)) {
|
||||
retval = retval.GetPath(path);
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown path \"%1%\" for device info \"current_state\".") % path).str(), 404);
|
||||
}
|
||||
}
|
||||
} else if (info.compare("last_state") == 0) {
|
||||
retval = device->getLastState();
|
||||
if (!path.empty()) {
|
||||
if (retval.HasPath(path)) {
|
||||
retval = retval.GetPath(path);
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown path \"%1%\" for device info \"last_state\".") % path).str(), 404);
|
||||
}
|
||||
}
|
||||
void DevicesHTTPResource::WebResource::render_POST(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
std::cout << "DevicesHTTPResource::render_POST()" << std::endl;
|
||||
std::cout << req;
|
||||
std::cout << "content: " << req.get_content() << std::endl;
|
||||
*res = new http_response(http_response_builder("ToDo: Implement POST", 500).string_response());
|
||||
std::cout << **res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
DevicesHTTPResource::WebResource::DataFormat DevicesHTTPResource::WebResource::getDataFormat(const httpserver::http_request& req) {
|
||||
DataFormat retval = DataFormat::JSON;
|
||||
std::string formatRaw = req.get_arg("format");
|
||||
if (!formatRaw.empty()) {
|
||||
if (formatRaw.compare("json") == 0) {
|
||||
// do nothing
|
||||
} else if (formatRaw.compare("jsonp") == 0) {
|
||||
retval = DataFormat::JSONP;
|
||||
} else if (formatRaw.compare("yaml") == 0) {
|
||||
retval = DataFormat::YAML;
|
||||
} else if (formatRaw.compare("xmlplist") == 0) {
|
||||
retval = DataFormat::XMLPLIST;
|
||||
} else if (formatRaw.compare("bundlehdr") == 0) {
|
||||
retval = DataFormat::BUNDLEHDR;
|
||||
} else if (formatRaw.compare("msgpack") == 0) {
|
||||
retval = DataFormat::MSGPACK;
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown device info \"%1%\"") % info).str(), 404);
|
||||
throw ResourceException((boost::format("Unknown format parameter \"%1%\"") % formatRaw).str(), 400);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
void DevicesHTTPResource::setDeviceState(devicerepository::Device* device, const Variant& cmd, std::vector<std::string> path) {
|
||||
Variant cmdMsg;
|
||||
if (path.size() > 0) {
|
||||
Variant& current = cmdMsg;
|
||||
for (int i = 0; i < path.size()-1; i++) {
|
||||
Variant tmp;
|
||||
current[path[i]] = tmp;
|
||||
current = tmp;
|
||||
Variant DevicesHTTPResource::WebResource::getDeviceInfo(devicerepository::Device* device, const httpserver::http_request& req, const std::string& category, const std::vector<std::string>& path) {
|
||||
Variant retval;
|
||||
|
||||
// We want all info categories
|
||||
if (category.empty()) {
|
||||
for (auto c : infoCategoryFuncs) {
|
||||
retval[c.first] = c.second(device, req, category);
|
||||
}
|
||||
current[path[path.size()-1]] = cmd;
|
||||
|
||||
// We want a single info category
|
||||
} else {
|
||||
cmdMsg = cmd;
|
||||
auto c = infoCategoryFuncs.find(category);
|
||||
if (c != infoCategoryFuncs.end()) {
|
||||
retval = c->second(device, req, category);
|
||||
if (path.size() > 0) {
|
||||
std::stringstream ss;
|
||||
for (auto p : path) {
|
||||
ss << "/" << p;
|
||||
}
|
||||
std::string pathstr = ss.str();
|
||||
if (retval.HasPath(pathstr)) {
|
||||
retval = retval.GetPath(pathstr);
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(), 404);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw ResourceException((boost::format("Unknown device info category \"%1%\"") % category).str(), 404);
|
||||
}
|
||||
}
|
||||
device->sendCommand(cmdMsg);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
void DevicesHTTPResource::render_POST(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
std::cout << "DevicesHTTPResource::render_POST()" << std::endl;
|
||||
std::cout << req;
|
||||
*res = new http_response(http_response_builder("ToDo: Implement POST", 500).string_response());
|
||||
std::cout << **res;
|
||||
void DevicesHTTPResource::WebResource::sendDeviceCommand(devicerepository::Device* device, const httpserver::http_request& req, const std::string& cmd, const std::vector<std::string>& path) {
|
||||
Variant cmdMsg;
|
||||
VariantRef mPart = cmdMsg.Ref();
|
||||
if (path.size() > 0) {
|
||||
for (int i = 0; i < path.size(); i++) {
|
||||
mPart[path[i]] = Variant();
|
||||
mPart.ReassignRef(mPart[path[i]]);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* The libvariant json deserializer knows nothing about RFC 7159, therefore non-object or non-array values are rejected.
|
||||
* As a workaround we use the yaml deserializer (yaml can be considered a super-set of json).
|
||||
* Fun fact: the json serializer accepts non-object or non-array values.
|
||||
*/
|
||||
//mPart = DeserializeJSON(cmd);
|
||||
mPart = DeserializeYAML(cmd);
|
||||
device->sendCommand(cmdMsg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+65
-16
@@ -8,6 +8,7 @@
|
||||
#include <ros/ros.h>
|
||||
#include <httpserver.hpp>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
|
||||
|
||||
@@ -19,19 +20,77 @@ namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implements a REST API for accessing the device repository and getting info about a device.
|
||||
*
|
||||
* HTTP GET REQUESTS:
|
||||
* 1. http://[prefixPath]/devices - Returns all known devices
|
||||
* 2. http://[prefixPath]/devices/[deviceID] - Returns complete info about the given device as JSON object.
|
||||
* 3. http://[prefixPath]/devices/[deviceID]/[infoCategory]/[infoPath] - Returns selected info about the given device as JSON object ([infoCategory]/[infoPath] is treated as JSON Path selecting parts of the complete JSON info object from 1).
|
||||
* 4. http://[prefixPath]/devices/[deviceID]/[cmdPath]?method=command&value=[cmdValue] - Sends the command [cmdPath]/[cmdValue] to the given device.
|
||||
* 5. http://[prefixPath]/devices/[deviceID]/[statusPath]?method=statusupdate&value=[statusValue] - Sends the status update [statusPath]/[statusValue] to the given device (Only useful for virtual devices).
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/x-www-form-urlencoded):
|
||||
* - Basically the same as 4 and 5 from the HTTP GET REQUESTS list, but parameters are transmitted in the request body instead of the URL.
|
||||
*
|
||||
* HTTP POST REQUESTS (content-type: application/json):
|
||||
* - http://[prefixPath]/devices/[deviceID]/[method]/[methodPath] - Semantically the same as 4 and 5 from the HTTP GET REQUESTS list, but the method parameter is part of the URL and the request body only contains a single JSON text.
|
||||
*
|
||||
*
|
||||
* A note about JSON object returned by the above URLs: According to RFC 4627 a JSON text is a serialized object or array.
|
||||
* This was later relaxed by RFC 7159 which defines that a JSON text is a serialized value. Our API adheres to RFC 7159 and may return
|
||||
* JSON texts (namely single strings, numbers or boolean values) that are not valid JSON texts according to RFC 4627.
|
||||
* Keep in mind that there are still a lot of JSON libraries and validators out there which still no nothing about RFC 7159.
|
||||
*/
|
||||
class DevicesHTTPResource
|
||||
: public virtual webserver::WebResource,
|
||||
public httpserver::http_resource<DevicesHTTPResource> {
|
||||
: public virtual webserver::WebResource {
|
||||
|
||||
private:
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
|
||||
/**
|
||||
* libhttpserver does a static_cast with any class registered as http resource. Unfortunately this breaks classes
|
||||
* using virtual inheritance. Therefore we register this inner class which does not use virtual inheritance.
|
||||
*/
|
||||
class WebResource
|
||||
: public httpserver::http_resource<WebResource> {
|
||||
|
||||
public:
|
||||
enum class DataMethod { GET, COMMAND, STATUSUPDATE };
|
||||
enum class DataFormat { JSON, JSONP, YAML, XMLPLIST, BUNDLEHDR, MSGPACK };
|
||||
|
||||
typedef std::function<libvariant::Variant( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& category )> infoCategoryFunc_t;
|
||||
|
||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
std::map<std::string, infoCategoryFunc_t> infoCategoryFuncs;
|
||||
|
||||
WebResource(devicerepository::DeviceRepositoryService* repoService);
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
void render_POST(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
DataFormat getDataFormat(const httpserver::http_request& req);
|
||||
|
||||
libvariant::Variant getDeviceInfo( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& category = std::string(),
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
void sendDeviceCommand( devicerepository::Device* device,
|
||||
const httpserver::http_request& req,
|
||||
const std::string& cmd,
|
||||
const std::vector<std::string>& path = std::vector<std::string>() );
|
||||
|
||||
} webResource;
|
||||
|
||||
|
||||
public:
|
||||
|
||||
DevicesHTTPResource(devicerepository::DeviceRepositoryService* repoService)
|
||||
: repoService(repoService) {}
|
||||
: webResource(repoService) {}
|
||||
|
||||
virtual void init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& registeredPath,
|
||||
@@ -39,16 +98,6 @@ public:
|
||||
httpserver::webserver& webserver,
|
||||
webserver::WebServerService& service );
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
void render_POST(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
libvariant::Variant getDeviceInfo( devicerepository::Device* device,
|
||||
const std::string& info = std::string(),
|
||||
const std::string& path = std::string() );
|
||||
|
||||
void setDeviceState( devicerepository::Device* device,
|
||||
const libvariant::Variant& cmd,
|
||||
std::vector<std::string> path = std::vector<std::string>() );
|
||||
};
|
||||
|
||||
} // namespace resources
|
||||
|
||||
+12
-11
@@ -23,30 +23,31 @@ void SchemaHTTPResource::init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
WebServerService& service ) {
|
||||
this->prefixPath = prefixPath;
|
||||
disallow_all();
|
||||
set_allowing(MHD_HTTP_METHOD_GET, true);
|
||||
webserver.register_resource(absolutePath, this, true);
|
||||
webResource.prefixPath = prefixPath;
|
||||
webResource.disallow_all();
|
||||
webResource.set_allowing(MHD_HTTP_METHOD_GET, true);
|
||||
webserver.register_resource(absolutePath, &webResource, true);
|
||||
}
|
||||
|
||||
|
||||
void SchemaHTTPResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
if (req.get_path_pieces_size() < prefixPath.size() + 1) {
|
||||
ROS_ERROR_STREAM("Requested path " << req.get_path() << " has fewer pieces than prefix path.");
|
||||
*res = new http_response(http_response_builder("Internal Server Error", 500).string_response());
|
||||
} else if (req.get_path_pieces_size() == prefixPath.size() + 1) {
|
||||
*res = new http_response(http_response_builder("No schema given", 400).string_response());
|
||||
void SchemaHTTPResource::WebResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) {
|
||||
// Requested URL is too short to contain any schema
|
||||
if (req.get_path_pieces_size() <= prefixPath.size() + 1) {
|
||||
*res = new http_response(http_response_builder("ERROR: No schema given", 400).string_response());
|
||||
// Requested URL may contain a valid schema
|
||||
} else {
|
||||
// Get requested schema
|
||||
std::stringstream ss;
|
||||
for (int i = prefixPath.size() + 1; i < req.get_path_pieces_size(); i++) {
|
||||
ss << "/" << req.get_path_piece(i);
|
||||
}
|
||||
auto schemaPath = (boost::format("hos:schema:/%1%") % ss.str()).str();
|
||||
// Try to retrieve schema
|
||||
try {
|
||||
auto schema = schemaLoader->GetSchema(schemaPath);
|
||||
*res = new http_response(http_response_builder(SerializeJSON(schema, true), 200).string_response());
|
||||
} catch(std::exception& e) {
|
||||
*res = new http_response(http_response_builder((boost::format("Could not find schema \"%1%\": %2%") % schemaPath % e.what()).str(), 404).string_response());
|
||||
*res = new http_response(http_response_builder((boost::format("ERROR: Could not find schema \"%1%\": %2%") % schemaPath % e.what()).str(), 404).string_response());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+22
-8
@@ -20,25 +20,39 @@ namespace resources {
|
||||
|
||||
|
||||
class SchemaHTTPResource
|
||||
: public virtual webserver::WebResource,
|
||||
public httpserver::http_resource<SchemaHTTPResource> {
|
||||
: public virtual webserver::WebResource {
|
||||
|
||||
private:
|
||||
ros::NodeHandle* nodehandle = nullptr;
|
||||
utils::SchemaLoader* schemaLoader = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
|
||||
/**
|
||||
* libhttpserver does a static_cast with any class registered as http resource. Unfortunately this breaks classes
|
||||
* using virtual inheritance. Therefore we register this inner class which does not use virtual inheritance.
|
||||
*/
|
||||
class WebResource
|
||||
: public httpserver::http_resource<WebResource> {
|
||||
|
||||
public:
|
||||
|
||||
ros::NodeHandle* nodehandle = nullptr;
|
||||
utils::SchemaLoader* schemaLoader = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
|
||||
WebResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader)
|
||||
: nodehandle(nodehandle), schemaLoader(schemaLoader) {}
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
} webResource;
|
||||
|
||||
public:
|
||||
SchemaHTTPResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader)
|
||||
: nodehandle(nodehandle), schemaLoader(schemaLoader) {}
|
||||
: webResource(nodehandle, schemaLoader) {}
|
||||
|
||||
virtual void init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& registeredPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
WebServerService& service );
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
};
|
||||
|
||||
} // namespace resources
|
||||
|
||||
Reference in New Issue
Block a user