From 6031718d0eac548931cc4fa5fb49e74deb9f5997 Mon Sep 17 00:00:00 2001 From: Matthias Janetschek Date: Tue, 26 May 2015 17:13:51 +0200 Subject: [PATCH] . --- .../resources/DevicesHTTPResource.cpp | 368 +++++++++++------- .../webserver/resources/DevicesHTTPResource.h | 81 +++- .../resources/SchemaHTTPResource.cpp | 23 +- .../webserver/resources/SchemaHTTPResource.h | 30 +- 4 files changed, 327 insertions(+), 175 deletions(-) diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.cpp index 2be546d4..189501f2 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.cpp @@ -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& 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 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 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 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 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& 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& 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); } diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.h index b234d281..fe23cea2 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource.h @@ -8,6 +8,7 @@ #include #include #include +#include @@ -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 { + : public virtual webserver::WebResource { private: - devicerepository::DeviceRepositoryService* repoService = nullptr; - std::vector 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 { + + public: + enum class DataMethod { GET, COMMAND, STATUSUPDATE }; + enum class DataFormat { JSON, JSONP, YAML, XMLPLIST, BUNDLEHDR, MSGPACK }; + + typedef std::function infoCategoryFunc_t; + + devicerepository::DeviceRepositoryService* repoService = nullptr; + std::vector prefixPath; + std::map 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& path = std::vector() ); + + void sendDeviceCommand( devicerepository::Device* device, + const httpserver::http_request& req, + const std::string& cmd, + const std::vector& path = std::vector() ); + + } webResource; + public: DevicesHTTPResource(devicerepository::DeviceRepositoryService* repoService) - : repoService(repoService) {} + : webResource(repoService) {} virtual void init( const std::vector& 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 path = std::vector() ); }; } // namespace resources diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp index a4eaa87b..c095ec5d 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp @@ -23,30 +23,31 @@ void SchemaHTTPResource::init( const std::vector& 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()); } } } diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h index 7e79077c..24391638 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h @@ -20,25 +20,39 @@ namespace resources { class SchemaHTTPResource - : public virtual webserver::WebResource, - public httpserver::http_resource { + : public virtual webserver::WebResource { private: - ros::NodeHandle* nodehandle = nullptr; - utils::SchemaLoader* schemaLoader = nullptr; - std::vector 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 { + + public: + + ros::NodeHandle* nodehandle = nullptr; + utils::SchemaLoader* schemaLoader = nullptr; + std::vector 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& 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