From 632a792f3a983c790cdc243e64aef3dcc93b3d1e Mon Sep 17 00:00:00 2001 From: matzman Date: Wed, 10 Jun 2015 20:28:48 +0200 Subject: [PATCH] . --- .../webresource/DeviceRepoHandler.cpp | 131 +++----- .../webresource/DeviceRepoHandler.h | 10 +- .../webresource/tmp/DeviceRepoHandler.cpp | 306 ------------------ .../webresource/tmp/DeviceRepoHandler.h | 250 -------------- src/hos_cosgi_ruleengine/API-Notes.txt | 16 + .../service/ruleengine/RuleEngineService.h | 2 + .../hos/cosgi/service/ruleengine/rules/Rule.h | 2 + .../rules/conditions/RuleCondition.h | 2 + .../rules/conditions/RuleConditionImpl.h | 25 +- .../ruleengine/RuleEngineServiceImpl.cpp | 9 +- .../ruleengine/RuleEngineServiceImpl.h | 7 +- .../cosgi/service/ruleengine/rules/RuleImpl.h | 4 + .../rules/conditions/DeviceMsgCondition.cpp | 9 +- .../rules/conditions/DeviceMsgCondition.h | 19 +- .../rules/conditions/LogicalAndCondition.cpp | 3 +- .../rules/conditions/LogicalAndCondition.h | 4 +- .../rules/conditions/LogicalOrCondition.cpp | 3 +- .../rules/conditions/LogicalOrCondition.h | 4 +- .../webresource/RuleEngineHandler.cpp | 109 ++----- .../webresource/RuleEngineHandler.h | 10 +- .../webroot/libs/dstore/.directory | 4 + .../webroot/libs/hos/DeviceRepo.js | 9 +- .../webroot/libs/hos/Socket.js | 228 +++++++++++++ .../libs/hos/stores/DeviceRepoStore.js | 29 ++ .../webresource/WebResourceHandlerImpl.h | 115 +++++-- .../webserver/WebServerServiceImpl.cpp | 17 +- .../service/webserver/WebServerServiceImpl.h | 13 +- .../webresource/DirectoryResource.cpp | 25 +- .../webserver/webresource/DirectoryResource.h | 8 +- .../webserver/webresource/SchemaResource.cpp | 70 ++-- .../webserver/webresource/SchemaResource.h | 2 +- .../webserver/webresource/WebSocketTest.cpp | 2 +- .../webserver/webresource/WebSocketTest.h | 2 +- 33 files changed, 595 insertions(+), 854 deletions(-) delete mode 100644 src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.cpp delete mode 100644 src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.h create mode 100644 src/hos_cosgi_ruleengine/API-Notes.txt create mode 100644 src/hos_cosgi_testwebapp/webroot/libs/dstore/.directory create mode 100644 src/hos_cosgi_testwebapp/webroot/libs/hos/Socket.js create mode 100644 src/hos_cosgi_testwebapp/webroot/libs/hos/stores/DeviceRepoStore.js diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.cpp b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.cpp index c0f0d70f..6ce50dfa 100644 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.cpp +++ b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.cpp @@ -88,9 +88,9 @@ Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::U } -void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { +void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { if(req.find("Upgrade") != req.end() && Poco::icompare(req["Upgrade"], "websocket") == 0) { - __handleEvent(req, resp, true); + __handleEvent(req, resp, form, true); /*throw webserver::webresource::WebResourceHandlerException("ToDo", Net::HTTPResponse::HTTP_NOT_IMPLEMENTED);*/ } else { @@ -99,13 +99,13 @@ void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco:: throw webserver::webresource::WebResourceHandlerException("Still a ToDo.", Net::HTTPResponse::HTTP_NOT_IMPLEMENTED); } else if (req.getMethod() == Net::HTTPRequest::HTTP_GET || req.getMethod() == Net::HTTPRequest::HTTP_POST) { - DeviceAPIMethod method = getDeviceAPIMethod(req); + DeviceAPIMethod method = getDeviceAPIMethod(req, form); if (method == DeviceAPIMethod::GET) { - __handleInfo(req, resp); + __handleInfo(req, resp, form); } else if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::STATUSUPDATE) { - __handleCommand(req, resp, method); + __handleCommand(req, resp, method, form); } else if (method == DeviceAPIMethod::EVENT) { - __handleEvent(req, resp, false); + __handleEvent(req, resp, form, false); } else { throw webserver::webresource::WebResourceHandlerException("Could not understood request", Net::HTTPResponse::HTTP_BAD_REQUEST); @@ -119,51 +119,20 @@ void DeviceRepoHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco:: -void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { - +void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { std::vector pathPieces, matchPieces; matchUri.getPathSegments(matchPieces); Poco::URI uri(req.getURI()); uri.getPathSegments(pathPieces); - std::unique_ptr form; - if (req.getMethod() == Net::HTTPRequest::HTTP_POST) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } // No device given, query mode (there is no real query functionality implemented yet though) if (pathPieces.size() <= matchPieces.size()) { auto devices = repoService->getDevices(); - // Parse HTTP header "Range" parameter (to support dojo/store) + // Range header parameter size_t rangeStart = 0; size_t rangeStop = devices.size() - 1; - std::string rangeRaw; - if (req.has("Range")) { - rangeRaw = req.get("Range"); - } else if (req.has("X-Range")) { - rangeRaw = req.get("X-Range"); - } - if (!rangeRaw.empty()) { - if (boost::algorithm::starts_with(rangeRaw, "items=")) { - auto range = rangeRaw.substr(6); - std::vector splitParts; - boost::algorithm::split(splitParts, range, boost::algorithm::is_any_of("-")); - if (splitParts.size() == 2) { - try { - size_t r1 = std::stol(splitParts[0]); - size_t r2 = std::stol(splitParts[1]); - if (r1 <= r2) { - rangeStart = r1; - rangeStop = std::min(r2, rangeStop); - } - } catch (...) { - ROS_ERROR_STREAM("Could not parse Range parameter \"" << rangeRaw << "\"."); - } - } - } - } + getRangeHeaderParameter(req, rangeStart, rangeStop, rangeStop); // Iterate over devices Variant result = Variant::List(); @@ -177,17 +146,15 @@ void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Ne // There are no devices and a range was given, probably out of range if (result.Size() == 0 && rangeStart > 0) { if (rangeStart > 0) { - handleErrorResponse(req, resp, "Out of range", Poco::Net::HTTPResponse::HTTP_NOT_FOUND); + handleErrorResponse(req, resp, form, "Out of range", Poco::Net::HTTPResponse::HTTP_NOT_FOUND); } else { - handleResponse(req, resp, result); + handleResponse(req, resp, form, result); } // Return devices array } else { - std::string rangeOut = (boost::format("items %1%-%2%/%3%") % rangeStart % rangeStop % devices.size()).str(); - resp.set("Content-Range", rangeOut); - resp.set("X-Content-Range", rangeOut); - handleResponse(req, resp, result); + setRangeHeaderParameter(resp, rangeStart, rangeStop, devices.size()); + handleResponse(req, resp, form, result); } // Single device mode @@ -206,17 +173,17 @@ void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Ne for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) { path << "/" << pathPieces[i]; } - if (form->has("pointer")) { - auto tmp = form->get("pointer"); - if (tmp.size() > 0 && tmp[0] != '/') { + std::string pointer; + if (getPointerParameter(pointer, form)) { + if (pointer.size() > 0 && pointer[0] != '/') { path << "/"; } - path << tmp; + path << pointer; } // Get device info Variant result = parent->getDeviceInfo(device.lock().get(), category, path.str()); // Return device info - handleResponse(req, resp, result); + handleResponse(req, resp, form, result); } else { throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); @@ -225,17 +192,11 @@ void DeviceRepoHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Ne } -void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method) { +void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method, Net::HTMLForm& form) { std::vector pathPieces, matchPieces; matchUri.getPathSegments(matchPieces); Poco::URI uri(req.getURI()); uri.getPathSegments(pathPieces); - std::unique_ptr form; - if (req.getMethod() == Net::HTTPRequest::HTTP_POST) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } // No device given if (pathPieces.size() <= matchPieces.size()) { @@ -245,8 +206,8 @@ void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco: // Get message std::string msg; - if (form->has("value")) { - msg = form->get("value"); + if (form.has("value")) { + msg = form.get("value"); } else { throw webserver::webresource::WebResourceHandlerException("API method requires a value.", Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); @@ -257,8 +218,9 @@ void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco: for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) { path.push_back(pathPieces[i]); } - if (form->has("pointer")) { - URI pointer(form->get("pointer")); + std::string pointer; + if (getPointerParameter(pointer, form)) { + URI pointer(pointer); pointer.getPathSegments(path); } @@ -269,7 +231,7 @@ void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco: switch (method) { case DeviceAPIMethod::COMMAND: sendDeviceCommand(device.lock().get(), req, msg, path); - handleResponse(req, resp, std::string("OK: Command send")); + handleResponse(req, resp, form, std::string("OK: Command send")); break; case DeviceAPIMethod::STATUSUPDATE: throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method."); @@ -285,17 +247,11 @@ void DeviceRepoHandler::__handleCommand(Poco::Net::HTTPServerRequest &req, Poco: -void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, bool websocket) { +void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form, bool websocket) { std::vector pathPieces, matchPieces; matchUri.getPathSegments(matchPieces); Poco::URI uri(req.getURI()); uri.getPathSegments(pathPieces); - std::unique_ptr form; - if (req.getMethod() == Net::HTTPRequest::HTTP_POST) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } bool singleDeviceMode = true; // No device given @@ -314,8 +270,8 @@ void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::N // Get event type std::string eventRaw; DeviceEventType event = DeviceEventType::STATE_CHANGED; - if (form->has("onevent")) { - eventRaw = form->get("onevent"); + if (form.has("onevent")) { + eventRaw = form.get("onevent"); } else if (req.has("On-Event")) { eventRaw = req.get("On-Event"); } else if (req.has("X-On-Event")) { @@ -343,8 +299,8 @@ void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::N } // Get client ID std::string clientID; - if (form->has("clientid")) { - clientID = form->get("clientid"); + if (form.has("clientid")) { + clientID = form.get("clientid"); } else if (req.has("Client-Id")) { clientID = req.get("Client-Id"); } else if (req.has("X-Client-Id")) { @@ -355,12 +311,12 @@ void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::N for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) { path << "/" << pathPieces[i]; } - if (form->has("pointer")) { - auto tmp = form->get("pointer"); - if (tmp.size() > 0 && tmp[0] != '/') { + std::string pointer; + if (getPointerParameter(pointer, form)) { + if (pointer.size() > 0 && pointer[0] != '/') { path << "/"; } - path << tmp; + path << pointer; } // Get event queue auto eventQueue = parent->getEventQueue(clientID, repoService); @@ -375,9 +331,9 @@ void DeviceRepoHandler::__handleEvent(Poco::Net::HTTPServerRequest &req, Poco::N } eventQueue->checkRegistration(event, devptr, path.str()); if (websocket) { - __handleEventWebSocket(req, resp, eventQueue.get(), parent); + __handleEventWebSocket(req, resp, form, eventQueue.get(), parent); } else { - __handleEventLongPolling(req, resp, eventQueue.get(), parent); + __handleEventLongPolling(req, resp, form, eventQueue.get(), parent); } eventQueue->unsetHandler(); } else { @@ -534,18 +490,11 @@ void DeviceRepoHandler::sendDeviceCommand(devicerepository::Device* device, Net: } -DeviceAPIMethod DeviceRepoHandler::getDeviceAPIMethod(Net::HTTPServerRequest &req) noexcept { - std::unique_ptr form; +DeviceAPIMethod DeviceRepoHandler::getDeviceAPIMethod(Net::HTTPServerRequest &req, Net::HTMLForm& form) noexcept { DeviceAPIMethod retval = DeviceAPIMethod::GET; // default - if (req.getMethod() == Net::HTTPRequest::HTTP_POST && req.has("Content-type") - && req.get("Content-type") == Net::HTMLForm::ENCODING_URL) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } std::string methodRaw; - if (form->has("method")) { - methodRaw = form->get("method"); + if (form.has("method")) { + methodRaw = form.get("method"); } else if (req.has("API-Method")) { methodRaw = req.get("API-Method"); } else if (req.has("X-API-Method")) { @@ -565,7 +514,7 @@ DeviceAPIMethod DeviceRepoHandler::getDeviceAPIMethod(Net::HTTPServerRequest &re (boost::format("Unknown device api method \"%1%\"") % methodRaw).str(), Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST ); } - } else if (form->has("onevent") || req.has("On-Event") || req.has("X-On-Event")) { + } else if (form.has("onevent") || req.has("On-Event") || req.has("X-On-Event")) { retval = DeviceAPIMethod::EVENT; } return retval; diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.h b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.h index 33a41db9..0d481e27 100644 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.h +++ b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/DeviceRepoHandler.h @@ -115,20 +115,20 @@ public: bool ssl = false ) : parent(parent), matchUri(matchUri), repoService(repoService), ssl(ssl) {} - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override; - void __handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp); + void __handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form); - void __handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method); + void __handleCommand(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, DeviceAPIMethod method, Poco::Net::HTMLForm& form); - void __handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, bool websocket); + void __handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form, bool websocket); void sendDeviceCommand( devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& cmd, const std::vector& path = std::vector() ); - static DeviceAPIMethod getDeviceAPIMethod(Poco::Net::HTTPServerRequest &req) noexcept; + static DeviceAPIMethod getDeviceAPIMethod(Poco::Net::HTTPServerRequest &req, Poco::Net::HTMLForm& form) noexcept; }; diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.cpp b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.cpp deleted file mode 100644 index 7ba34cd7..00000000 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.cpp +++ /dev/null @@ -1,306 +0,0 @@ - -#include "DeviceRepoHandler.h" -#include -#include -#include -#include -#include -#include - - -// Service Namespace -namespace hos { -namespace cosgi { -namespace service { -namespace devicerepository { -namespace webresource { - - - -using namespace Poco; -using namespace libvariant; - - -HOS_SERVICE_IMPL_INFO( DeviceRepoHandlerFactory, // Implementation - service::webserver::WebResourceHandlerFactory::SERVICE_NAME, // Service Name - 1, 0 ) // Version - - -DeviceRepoHandlerFactory::DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* repoService) - : repoService(repoService) { - infoCategoryFuncs.insert({"id", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - return device->getDeviceID(); - } }); - - infoCategoryFuncs.insert({"type", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - return device->getDeviceType(); - } }); - - infoCategoryFuncs.insert({"status_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - return device->getStatusTopic(); - } }); - - infoCategoryFuncs.insert({"command_topic", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - return device->getCmdTopic(); - } }); - - infoCategoryFuncs.insert({"current_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - auto retval = device->getCurrentState(); - if (retval.IsMap()) { - retval["timestamp"] = utils::timestamp_to_ISO8601(device->getCurrentStateTimestamp()); - } - return retval; - } }); - - infoCategoryFuncs.insert({"last_state", [](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - auto retval = device->getLastState(); - if (retval.IsMap()) { - retval["timestamp"] = utils::timestamp_to_ISO8601(device->getLastStateTimestamp()); - } - return retval; - } }); - - infoCategoryFuncs.insert({"uri", [=](Poco::URI& matchUri, devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, const std::string& category, bool ssl)->Variant { - std::stringstream ss; - if (ssl) { - ss << "https://"; - } else { - ss << "http://"; - } - ss << req.getHost() << matchUri.toString() << device->getDeviceID(); - return ss.str(); - } }); -} - - -DeviceAPIMethod DeviceRepoHandlerFactory::getDeviceAPIMethod() noexcept { - -} - - -/*std::shared_ptr DeviceRepoHandlerFactory::getDeviceEventQueue(std::string clientID) { - if (clientID.empty()) { - - } else { - - } -}*/ - - -Net::HTTPRequestHandler* DeviceRepoHandlerFactory::createRequestHandler( Poco::URI matchUri, const Net::HTTPServerRequest& request, bool ssl ) noexcept { - if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0) - return new DeviceRepoEventWebSocketHandler(matchUri, repoService, infoCategoryFuncs, ssl); - else { - DeviceAPIMethod method = getDeviceAPIMethod(); - if (method == DeviceAPIMethod::EVENT) { - return new DeviceRepoEventLongPollingHandler(matchUri, repoService, infoCategoryFuncs, ssl); - } else { - return new DeviceRepoInfoHandler(matchUri, method, repoService, infoCategoryFuncs, ssl); - } - } -} - - - - - - - -Variant DeviceRepoHandlerBase::getDeviceInfo(devicerepository::Device* device, Net::HTTPServerRequest &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(matchUri, device, req, category, ssl); - } - - // We want a single info category - } else { - auto c = infoCategoryFuncs.find(category); - if (c != infoCategoryFuncs.end()) { - retval = c->second(matchUri, device, req, category, ssl); - 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 webserver::webresource::WebResourceHandlerException((boost::format("Unknown path \"%1%\" for device info category \"%2%\".") % pathstr % category).str(), - Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); - } - } - } else { - throw webserver::webresource::WebResourceHandlerException((boost::format("Unknown device info category \"%1%\"") % category).str(), - Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); - } - } - - return retval; -} - - - -void DeviceRepoHandlerBase::sendDeviceCommand(devicerepository::Device* device, Net::HTTPServerRequest &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); -} - - - -void DeviceRepoInfoHandler::__handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { - - std::string methodValue; - if (method == DeviceAPIMethod::COMMAND || method == DeviceAPIMethod::STATUSUPDATE) { - if (form.has("value")) { - methodValue = form.get("value"); - } else { - throw webserver::webresource::WebResourceHandlerException("API method requires a value parameter.", - Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); - } - } - - std::vector pathPieces, matchPieces; - matchUri.getPathSegments(matchPieces); - Poco::URI uri(req.getURI()); - uri.getPathSegments(pathPieces); - - // No device given - if (pathPieces.size() <= matchPieces.size()) { - if (method != DeviceAPIMethod::GET) { - throw webserver::webresource::WebResourceHandlerException("Requested API method requires a device", - Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); - } - - auto devices = repoService->getDevices(); - - // Range header parameter - std::string rangeRaw; - size_t rangeStart = 0; - size_t rangeStop = devices.size() - 1; - if (req.has("Range")) { - rangeRaw = req.get("Range"); - } else if (req.has("X-Range")) { - rangeRaw = req.get("X-Range"); - } - if (!rangeRaw.empty()) { - if (boost::algorithm::starts_with(rangeRaw, "items=")) { - auto range = rangeRaw.substr(6); - std::vector splitParts; - boost::algorithm::split(splitParts, range, boost::algorithm::is_any_of("-")); - if (splitParts.size() == 2) { - try { - size_t r1 = std::stol(splitParts[0]); - size_t r2 = std::stol(splitParts[1]); - if (r1 <= r2) { - rangeStart = r1; - rangeStop = std::min(r2, rangeStop); - } - } catch (...) { - ROS_ERROR_STREAM("Could not parse Range parameter \"" << rangeRaw << "\"."); - } - } - } - } - - // - - Variant result = Variant::List(); - for (size_t t = rangeStart; t <= rangeStop; t++) { - auto dev = devices[t].lock(); - result.Append(getDeviceInfo(dev.get(), req)); - } - if (result.Size() == 0 && rangeStart > 0) { - if (rangeStart > 0) { - handleErrorResponse(req, resp, "Out of range", Poco::Net::HTTPResponse::HTTP_NOT_FOUND); - } else { - handleResponse(req, resp, result); - } - } else { - std::string rangeOut = (boost::format("items %1%-%2%/%3%") % rangeStart % rangeStop % devices.size()).str(); - resp.set("Content-Range", rangeOut); - resp.set("X-Content-Range", rangeOut); - handleResponse(req, resp, result); - } - - // Device given - } else { - std::string devid = pathPieces[matchPieces.size()]; - auto device = repoService->getDevice(devid); - if (!device.expired()) { - switch (method) { - case DeviceAPIMethod::GET: - { - std::string category; - if (pathPieces.size() > matchPieces.size() + 1) { - category = pathPieces[matchPieces.size() + 1]; - } - std::vector path; - for (int i = matchPieces.size() + 2; i < pathPieces.size(); i++) { - path.push_back(pathPieces[i]); - } - Variant result = getDeviceInfo(device.lock().get(), req, category, path); - handleResponse(req, resp, result); - } - break; - case DeviceAPIMethod::COMMAND: - { - std::vector path; - for (int i = matchPieces.size()+1; i < pathPieces.size(); i++) { - path.push_back(pathPieces[i]); - } - sendDeviceCommand(device.lock().get(), req, methodValue, path); - handleResponse(req, resp, std::string("OK: Command send")); - } - break; - case DeviceAPIMethod::STATUSUPDATE: - throw webserver::webresource::WebResourceHandlerException("ToDo: Implement method."); - default: - throw webserver::webresource::WebResourceHandlerException("Internal Server Error"); - } - } else { - throw webserver::webresource::WebResourceHandlerException((boost::format("Could not find device \"%1%\"") % devid).str(), - Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); - } - } -} - - - -void DeviceRepoInfoHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { - Net::HTMLForm form(req); - __handle(req, resp, form); -} - -void DeviceRepoInfoHandler::_handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { - Net::HTMLForm form(req, req.stream()); - __handle(req, resp, form); -} - - - -} // namespace resources -} // namespace webserver -} // namespace service -} // namespace cosgi -} // namespace hos - - - diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.h b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.h deleted file mode 100644 index 3e9bcfca..00000000 --- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/devicerepository/webresource/tmp/DeviceRepoHandler.h +++ /dev/null @@ -1,250 +0,0 @@ - -#pragma once - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - - -// Service Namespace -namespace hos { -namespace cosgi { -namespace service { -namespace devicerepository { -namespace webresource { - - - -enum class DeviceAPIMethod { - GET, - COMMAND, - STATUSUPDATE, - EVENT -}; - - -class DeviceEventQueue - : public virtual interface::Observer { -public: - const std::string clientID; - DeviceRepositoryService* const repoService; - DeviceEventType eventType; - Device* device; - std::string pointer; - std::queue eventQueue; - size_t openConnectionCount = 0; - - DeviceEventQueue( const std::string& clientID, - const DeviceEventType eventType, - DeviceRepositoryService* repoService, - Device* device, - std::string pointer ) - : clientID(clientID), eventType(eventType), repoService(repoService), device(device), pointer(pointer) {} - - virtual void notificationReceived( interface::Observer::observable_type* o, - const devicerepository::DeviceEventType& key, - const devicerepository::DeviceEvent* e) noexcept override { - if (eventType == DeviceEventType::ALL_EVENTS || eventType == key) { - switch (key) { - case devicerepository::DeviceEventType::COMMAND_RECEIVED: - break; - case devicerepository::DeviceEventType::STATUS_UPDATE_RECEIVED: - break; - case devicerepository::DeviceEventType::STATE_CHANGED: - break; - case devicerepository::DeviceEventType::DEVICE_ADDED: - break; - case devicerepository::DeviceEventType::DEVICE_REMOVED: - break; - default: - break; - } - } - } - -}; - - - -class DeviceRepoHandlerFactory - : public virtual webserver::WebResourceHandlerFactory, - public implementation::Service { -public: - typedef std::function infoCategoryFunc_t; -private: - webserver::WebServerService* service = nullptr; - DeviceRepositoryService* repoService; - std::map infoCategoryFuncs; - std::map> deviceEventQueues; - -public: - DeviceRepoHandlerFactory(devicerepository::DeviceRepositoryService* repoService); - - virtual Poco::Net::HTTPRequestHandler* createRequestHandler(Poco::URI matchUri, const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept override; - - virtual void setWebServerService(webserver::WebServerService* service) noexcept override { - this->service = service; - } - - virtual bool excludeFromAuthentication() noexcept override { - return false; - } - - static DeviceAPIMethod getDeviceAPIMethod() noexcept; - - std::shared_ptr getDeviceEventQueue( const std::string& clientID, - const DeviceEventType eventType, - DeviceRepositoryService* repoService, - Device* device, - std::string pointer ); - -}; - - - -/** - * 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 DeviceRepoHandlerBase - : public webserver::webresource::WebResourceHandlerImpl { - -protected: - Poco::URI matchUri; - devicerepository::DeviceRepositoryService* repoService; - const std::map& infoCategoryFuncs; - bool ssl; - - libvariant::Variant getDeviceInfo( devicerepository::Device* device, - Poco::Net::HTTPServerRequest &req, - const std::string& category = std::string(), - const std::vector& path = std::vector() ); - - void sendDeviceCommand( devicerepository::Device* device, - Poco::Net::HTTPServerRequest &req, - const std::string& cmd, - const std::vector& path = std::vector() ); - - -public: - - DeviceRepoHandlerBase( Poco::URI matchUri, - devicerepository::DeviceRepositoryService* repoService, - const std::map& infoCategoryFuncs, - bool ssl = false ) - : matchUri(matchUri), repoService(repoService), infoCategoryFuncs(infoCategoryFuncs), ssl(ssl) {} - -}; - - - -class DeviceRepoInfoHandler - : public DeviceRepoHandlerBase { - -protected: - DeviceAPIMethod method; - -public: - - DeviceRepoInfoHandler( Poco::URI matchUri, - DeviceAPIMethod method, - devicerepository::DeviceRepositoryService* repoService, - const std::map& infoCategoryFuncs, - bool ssl = false ) - : DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl), method(method) {} - - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - - virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - - virtual void __handle(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form); - -}; - - - -class DeviceRepoEventLongPollingHandler - : public DeviceRepoHandlerBase { - -protected: - - -public: - - DeviceRepoEventLongPollingHandler( Poco::URI matchUri, - devicerepository::DeviceRepositoryService* repoService, - const std::map& infoCategoryFuncs, - bool ssl = false ) - : DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl) {} - - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - - virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - -}; - - - -class DeviceRepoEventWebSocketHandler - : public DeviceRepoHandlerBase { - -protected: - - -public: - - DeviceRepoEventWebSocketHandler( Poco::URI matchUri, - devicerepository::DeviceRepositoryService* repoService, - const std::map& infoCategoryFuncs, - bool ssl = false ) - : DeviceRepoHandlerBase(matchUri, repoService, infoCategoryFuncs, ssl) {} - - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - - virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; - -}; - - - -} // namespace webresource -} // namespace devicerepository -} // namespace service -} // namespace cosgi -} // namespace hos - diff --git a/src/hos_cosgi_ruleengine/API-Notes.txt b/src/hos_cosgi_ruleengine/API-Notes.txt new file mode 100644 index 00000000..4587df75 --- /dev/null +++ b/src/hos_cosgi_ruleengine/API-Notes.txt @@ -0,0 +1,16 @@ + +x) Rule engine infos: +====================== +http[s]://hos/api// + +x) Rule package infos: +====================== +http[s]://hos/api//packages +http[s]://hos/api//packages/ + +x) Rule infos: +====================== +http[s]://hos/api//packages//rules +http[s]://hos/api//packages//rules/ + + diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h index 8b8bd26c..e58c9584 100644 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/RuleEngineService.h @@ -129,6 +129,8 @@ public: */ virtual size_t getRulePackageCount() noexcept = 0; + virtual std::string getName() noexcept = 0; + /** * Registers a rule condition factory under the given type identifier. * diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h index 55cf5135..0e5d2c7c 100644 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/Rule.h @@ -132,6 +132,8 @@ public: virtual void conditionStateChanged(bool newState, const conditions::ConditionTriggerType& trigger, const libvariant::Variant& lcontext = libvariant::Variant()) noexcept = 0; + virtual libvariant::Variant getConditionJSONRepresentation() = 0; + }; diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h index 65dbc158..5991ef93 100644 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleCondition.h @@ -116,6 +116,8 @@ public: */ virtual void setCompositeParent(RuleCompositeCondition* compositeParent) noexcept = 0; + virtual libvariant::Variant getJSONRepresentation() = 0; + }; diff --git a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h index ae1f0f1d..53c78dc1 100644 --- a/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h +++ b/src/hos_cosgi_ruleengine/include/hos_cosgi_ruleengine/hos/cosgi/service/ruleengine/rules/conditions/RuleConditionImpl.h @@ -55,12 +55,14 @@ protected: RuleCompositeCondition* compositeParent = nullptr; bool enabled = false; + libvariant::Variant jsonRepresentation; + void notifyStateChangeToParent(bool newState, ConditionTriggerType category, const libvariant::Variant& lcontext = libvariant::Variant()); public: - RuleConditionImpl(rules::Rule* parent, ConditionTriggerType triggerType, const std::string& type) : - parent(parent), triggerType(triggerType), type(type) {} + RuleConditionImpl(rules::Rule* parent, ConditionTriggerType triggerType, const std::string& type, const libvariant::Variant& jsonRepresentation) : + parent(parent), triggerType(triggerType), type(type), jsonRepresentation(jsonRepresentation) {} virtual rules::Rule* getParent() noexcept override { return parent; @@ -94,6 +96,10 @@ public: this->enabled; } + virtual libvariant::Variant getJSONRepresentation() override { + return jsonRepresentation; + } + }; @@ -110,8 +116,10 @@ protected: public: - RuleCompositeConditionImpl(Rule* parent, ConditionTriggerType category, std::string type) : - RuleConditionImpl(parent, category, type) {} + RuleCompositeConditionImpl(Rule* parent, ConditionTriggerType category, std::string type, const libvariant::Variant& jsonRepresentation) : + RuleConditionImpl(parent, category, type, jsonRepresentation) { + this->jsonRepresentation.Erase("conditions"); + } virtual void childStateChanged( RuleCondition* child, bool newState, @@ -133,6 +141,15 @@ public: this->enabled = enabled; } } + + virtual libvariant::Variant getJSONRepresentation() override { + libvariant::Variant tmp = jsonRepresentation.Copy(); + tmp["conditions"] = libvariant::Variant::List(); + for (auto c : children) { + tmp["conditions"].Append(c->getJSONRepresentation()); + } + return tmp; + } }; diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp index e090e549..669c475e 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.cpp @@ -66,7 +66,8 @@ void RuleEngineServiceImpl::setRepoService(devicerepository::DeviceRepositorySer void RuleEngineServiceImpl::init() noexcept { // Get public parameters - nodehandle->getParam("ruleengine_prefix", ruleEnginePrefix); + nodehandle->getParam("hos_param_prefix", hosParamPrefix); + nodehandle->getParam("ruleengine_name", engineName); // Register condition factories registerRuleConditionFactory("logical_or", std::make_shared()); @@ -77,7 +78,9 @@ void RuleEngineServiceImpl::init() noexcept { // Create rule packages XmlRpc::XmlRpcValue rawValue; - nodehandle->getParam(ruleEnginePrefix, rawValue); + std::stringstream ss; + ss << hosParamPrefix << "/" << engineName; + nodehandle->getParam(ss.str(), rawValue); if (rawValue.getType() == XmlRpc::XmlRpcValue::TypeStruct) { for (auto pkg : rawValue) { std::string pname = pkg.first; @@ -98,7 +101,7 @@ void RuleEngineServiceImpl::init() noexcept { void RuleEngineServiceImpl::addRulePkg(const std::string& name) { std::stringstream ss; - ss << ruleEnginePrefix << "/" << name; + ss << hosParamPrefix << "/" << engineName << "/" << name; XmlRpc::XmlRpcValue rawValue; nodehandle->getParam(ss.str(), rawValue); addRulePkg(name, static_cast(rawValue)); diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h index fe3c3665..9b6100e6 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/RuleEngineServiceImpl.h @@ -74,7 +74,8 @@ private: devicerepository::DeviceRepositoryService* repoService = nullptr; ros::NodeHandle* nodehandle = nullptr; - std::string ruleEnginePrefix = "/hos/rules/ruleengine0"; + std::string hosParamPrefix = "/hos/rules"; + std::string engineName = "ruleengine0"; rulePkgMap_t rulePkgs; rulePkgMap_t noExecRulePkgs; @@ -110,6 +111,10 @@ public: virtual size_t getRulePackageCount() noexcept override; + virtual std::string getName() noexcept override { + return engineName; + } + virtual devicerepository::DeviceRepositoryService* getDeviceRepository() noexcept override { return repoService; } diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h index c0950b2d..6a70f790 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/RuleImpl.h @@ -93,6 +93,10 @@ public: std::shared_ptr createRuleCondition(const Variant& raw) override; + virtual Variant getConditionJSONRepresentation() override { + return condition->getJSONRepresentation(); + } + }; diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp index 835b4649..5ee91750 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.cpp @@ -95,7 +95,8 @@ std::shared_ptr DeviceStatusMsgConditionFactory::createCondition( if (raw.HasPath("msgvalue")) { msgValue = raw["msgvalue"]; } - retval = std::make_shared(parent, deviceID, msgPointer, msgValue); + Variant rep = raw.Copy(); + retval = std::make_shared(parent, deviceID, msgPointer, msgValue, rep); return retval; } @@ -154,7 +155,8 @@ std::shared_ptr DeviceCommandMsgConditionFactory::createCondition if (raw.HasPath("msgvalue")) { msgValue = raw["msgvalue"]; } - retval = std::make_shared(parent, deviceID, msgPointer, msgValue); + Variant rep = raw.Copy(); + retval = std::make_shared(parent, deviceID, msgPointer, msgValue, rep); return retval; } @@ -217,7 +219,8 @@ std::shared_ptr DeviceStateChangeConditionFactory::createConditio if (raw.HasPath("tovalue")) { toValue = raw["tovalue"]; } - retval = std::make_shared(parent, deviceID, statePointer, fromValue, toValue); + Variant rep = raw.Copy(); + retval = std::make_shared(parent, deviceID, statePointer, fromValue, toValue, rep); return retval; } diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h index f40daa88..bc2472ea 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/DeviceMsgCondition.h @@ -41,8 +41,9 @@ public: const std::string& deviceID, const std::string& msgPointer, const Variant& msgValue, - const std::set _listenerKeys ) - : RuleConditionImpl(parent, category, type), deviceID(deviceID), + const std::set _listenerKeys, + const libvariant::Variant& jsonRepresentation) + : RuleConditionImpl(parent, category, type, jsonRepresentation), deviceID(deviceID), msgPointer(msgPointer), msgValue(msgValue), _listenerKeys(_listenerKeys) {} virtual void notificationReceived( interface::Observer::observable_type* o, @@ -86,9 +88,9 @@ class DeviceCommandMsgCondition : public DeviceMsgCondition { public: - DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue) + DeviceCommandMsgCondition(Rule* parent, const std::string deviceID, const std::string msgPointer, const Variant msgValue, const libvariant::Variant& jsonRepresentation) : DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicecommandmsg", deviceID, - msgPointer, msgValue, {devicerepository::DeviceEventType::COMMAND_RECEIVED}) {} + msgPointer, msgValue, {devicerepository::DeviceEventType::COMMAND_RECEIVED}, jsonRepresentation) {} virtual void notificationReceived( interface::Observer::observable_type* o, @@ -116,9 +118,10 @@ public: const std::string deviceID, const std::string statePointer, const Variant fromValue, - const Variant toValue ) + const Variant toValue, + const libvariant::Variant& jsonRepresentation ) : DeviceMsgCondition(parent, ConditionTriggerType::Event, "devicestatechange", deviceID, statePointer, - toValue, {devicerepository::DeviceEventType::STATE_CHANGED}), fromValue(fromValue) {} + toValue, {devicerepository::DeviceEventType::STATE_CHANGED}, jsonRepresentation), fromValue(fromValue) {} virtual void notificationReceived(interface::Observer::observable_type* o, const devicerepository::DeviceEventType& key, const devicerepository::DeviceEvent* e) noexcept override; }; diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp index e9c928be..3766b601 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.cpp @@ -16,7 +16,8 @@ namespace conditions { std::shared_ptr LogicalAndConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { std::shared_ptr retval; if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { - retval = std::make_shared(parent); + Variant rep = raw.Copy(); + retval = std::make_shared(parent, rep); for (auto c : raw["conditions"].AsList()) { auto cond = parent->createRuleCondition(c); if (cond) { diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h index 33b5537f..9f15b1d1 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalAndCondition.h @@ -28,8 +28,8 @@ protected: unsigned trueChildrenCount = 0; public: - LogicalAndCondition(Rule* parent) : - RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_and") {} + LogicalAndCondition(Rule* parent, const libvariant::Variant& jsonRepresentation) : + RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_and", jsonRepresentation) {} virtual void addChild(std::shared_ptr child) override; diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp index 1f0e4cf1..ebf0a619 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.cpp @@ -15,7 +15,8 @@ namespace conditions { std::shared_ptr LogicalOrConditionFactory::createCondition(rules::Rule* parent, const Variant& raw) { std::shared_ptr retval; if (raw.HasPath("conditions") && raw["conditions"].IsList() && raw["conditions"].Size() > 0) { - retval = std::make_shared(parent); + Variant rep = raw.Copy(); + retval = std::make_shared(parent, rep); for (auto c : raw["conditions"].AsList()) { auto cond = parent->createRuleCondition(c); if (cond) { diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h index 5c0fd866..87e54e84 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/rules/conditions/LogicalOrCondition.h @@ -28,8 +28,8 @@ protected: unsigned trueChildrenCount = 0; public: - LogicalOrCondition(Rule* parent) : - RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_or") {} + LogicalOrCondition(Rule* parent, const libvariant::Variant& jsonRepresentation) : + RuleCompositeConditionImpl(parent, ConditionTriggerType::Both, "logical_or", jsonRepresentation) {} virtual void addChild(std::shared_ptr child) override; virtual void childStateChanged(RuleCondition* child, bool newState, ConditionTriggerType category, diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.cpp b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.cpp index fecfbf91..6e886ce6 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.cpp +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.cpp @@ -52,6 +52,10 @@ RuleEngineHandlerFactory::RuleEngineHandlerFactory(RuleEngineService* ruleServic Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); }) { + engineInfoFuncs.add("id", [](RuleEngineService* engine, const std::string& category)->Variant { + return engine->getName(); + }); + engineInfoFuncs.add("languages", [](RuleEngineService* engine, const std::string& category)->Variant { Variant retval = libvariant::Variant::List(); for (auto e : engine->getExecutionBackends()) { @@ -74,6 +78,10 @@ RuleEngineHandlerFactory::RuleEngineHandlerFactory(RuleEngineService* ruleServic return pkg->getName(); }); + pkgInfoFuncs.add("parent", [](rules::RulePackage* pkg, const std::string& category)->Variant { + return pkg->getParent()->getName(); + }); + pkgInfoFuncs.add("language", [](rules::RulePackage* pkg, const std::string& category)->Variant { return pkg->getLanguage(); }); @@ -106,6 +114,10 @@ RuleEngineHandlerFactory::RuleEngineHandlerFactory(RuleEngineService* ruleServic return rule->getName(); }); + ruleInfoFuncs.add("parent", [](rules::Rule* rule, const std::string& category)->Variant { + return rule->getParent()->getName(); + }); + ruleInfoFuncs.add("enabled", [](rules::Rule* rule, const std::string& category)->Variant { return rule->isEnabled(); }); @@ -129,7 +141,7 @@ RuleEngineHandlerFactory::RuleEngineHandlerFactory(RuleEngineService* ruleServic }); ruleInfoFuncs.add("condition", [](rules::Rule* rule, const std::string& category)->Variant { - return "ToDo"; + return rule->getConditionJSONRepresentation(); }); } @@ -215,24 +227,24 @@ Variant RuleEngineHandlerFactory::getRuleInfo( rules::Rule* rule, const std::str -void RuleEngineHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { +void RuleEngineHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { if(req.find("Upgrade") != req.end() && Poco::icompare(req["Upgrade"], "websocket") == 0) { //__handleEvent(req, resp, true); - /*throw webserver::webresource::WebResourceHandlerException("ToDo", - Net::HTTPResponse::HTTP_NOT_IMPLEMENTED);*/ + throw webserver::webresource::WebResourceHandlerException("ToDo", + Net::HTTPResponse::HTTP_NOT_IMPLEMENTED); } else { if (req.getMethod() == Net::HTTPRequest::HTTP_PUT) { // Put Command throw webserver::webresource::WebResourceHandlerException("Still a ToDo.", Net::HTTPResponse::HTTP_NOT_IMPLEMENTED); } else if (req.getMethod() == Net::HTTPRequest::HTTP_GET || req.getMethod() == Net::HTTPRequest::HTTP_POST) { - RuleEngineAPIMethod method = getRuleEngineAPIMethod(req); + RuleEngineAPIMethod method = getRuleEngineAPIMethod(req, form); if (method == RuleEngineAPIMethod::GET) { - __handleInfo(req, resp); + __handleInfo(req, resp, form); } else if (method == RuleEngineAPIMethod::SET) { - //__handleCommand(req, resp, method); + //__handleCommand(req, resp, method, form.get()); } else if (method == RuleEngineAPIMethod::EVENT) { - //__handleEvent(req, resp, false); + //__handleEvent(req, resp, false, form.get()); } else { throw webserver::webresource::WebResourceHandlerException("Could not understood request", Net::HTTPResponse::HTTP_BAD_REQUEST); @@ -246,78 +258,16 @@ void RuleEngineHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco:: -void RuleEngineHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { +void RuleEngineHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { std::vector pathPieces, matchPieces; matchUri.getPathSegments(matchPieces); Poco::URI uri(req.getURI()); uri.getPathSegments(pathPieces); - std::unique_ptr form; - if (req.getMethod() == Net::HTTPRequest::HTTP_POST) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } // Nothing given, return engine info if (pathPieces.size() <= matchPieces.size()) { - handleResponse(req, resp, parent->getEngineInfo(ruleService, "", "")); - - /*auto pkgs = ruleService->getRulePackages(); - - // Parse HTTP header "Range" parameter (to support dojo/store) - size_t rangeStart = 0; - size_t rangeStop = pkgs.size() - 1; - std::string rangeRaw; - if (req.has("Range")) { - rangeRaw = req.get("Range"); - } else if (req.has("X-Range")) { - rangeRaw = req.get("X-Range"); - } - if (!rangeRaw.empty()) { - if (boost::algorithm::starts_with(rangeRaw, "items=")) { - auto range = rangeRaw.substr(6); - std::vector splitParts; - boost::algorithm::split(splitParts, range, boost::algorithm::is_any_of("-")); - if (splitParts.size() == 2) { - try { - size_t r1 = std::stol(splitParts[0]); - size_t r2 = std::stol(splitParts[1]); - if (r1 <= r2) { - rangeStart = r1; - rangeStop = std::min(r2, rangeStop); - } - } catch (...) { - ROS_ERROR_STREAM("Could not parse Range parameter \"" << rangeRaw << "\"."); - } - } - } - } - - // Iterate over packages - Variant result = Variant::List(); - if (pkgs.size() > 0) { - for (size_t t = rangeStart; t <= rangeStop; t++) { - auto pkg = pkgs[t].lock(); - result.Append(parent->getRulePkgInfo(pkg.get())); - } - } - - // There are no devices and a range was given, probably out of range - if (result.Size() == 0 && rangeStart > 0) { - if (rangeStart > 0) { - handleErrorResponse(req, resp, "Out of range", Poco::Net::HTTPResponse::HTTP_NOT_FOUND); - } else { - handleResponse(req, resp, result); - } - - // Return devices array - } else { - std::string rangeOut = (boost::format("items %1%-%2%/%3%") % rangeStart % rangeStop % pkgs.size()).str(); - resp.set("Content-Range", rangeOut); - resp.set("X-Content-Range", rangeOut); - handleResponse(req, resp, result); - }*/ + handleResponse(req, resp, form, parent->getEngineInfo(ruleService, "", "")); // rule package given } else if (pathPieces.size() == matchPieces.size() + 1) { @@ -330,18 +280,11 @@ void RuleEngineHandler::__handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Ne -RuleEngineAPIMethod RuleEngineHandler::getRuleEngineAPIMethod(Net::HTTPServerRequest &req) noexcept { - std::unique_ptr form; +RuleEngineAPIMethod RuleEngineHandler::getRuleEngineAPIMethod(Net::HTTPServerRequest &req, Net::HTMLForm& form) noexcept { RuleEngineAPIMethod retval = RuleEngineAPIMethod::GET; // default - if (req.getMethod() == Net::HTTPRequest::HTTP_POST && req.has("Content-type") - && req.get("Content-type") == Net::HTMLForm::ENCODING_URL) { - form.reset(new Net::HTMLForm(req, req.stream())); - } else { - form.reset(new Net::HTMLForm(req)); - } std::string methodRaw; - if (form->has("method")) { - methodRaw = form->get("method"); + if (form.has("method")) { + methodRaw = form.get("method"); } else if (req.has("API-Method")) { methodRaw = req.get("API-Method"); } else if (req.has("X-API-Method")) { @@ -359,7 +302,7 @@ RuleEngineAPIMethod RuleEngineHandler::getRuleEngineAPIMethod(Net::HTTPServerReq (boost::format("Unknown rule engine api method \"%1%\"") % methodRaw).str(), Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST ); } - } else if (form->has("onevent") || req.has("On-Event") || req.has("X-On-Event")) { + } else if (form.has("onevent") || req.has("On-Event") || req.has("X-On-Event")) { retval = RuleEngineAPIMethod::EVENT; } return retval; diff --git a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.h b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.h index cee52ff9..321a84ae 100644 --- a/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.h +++ b/src/hos_cosgi_ruleengine/src/hos/cosgi/service/ruleengine/webresource/RuleEngineHandler.h @@ -119,15 +119,15 @@ public: bool ssl = false ) : parent(parent), matchUri(matchUri), ruleService(ruleService), ssl(ssl) {} - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override; - void __handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp); + void __handleInfo(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form); - void __handleSet(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, RuleEngineAPIMethod method); + void __handleSet(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, RuleEngineAPIMethod method, Poco::Net::HTMLForm& form); - void __handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, bool websocket); + void __handleEvent(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form, bool websocket); - static RuleEngineAPIMethod getRuleEngineAPIMethod(Poco::Net::HTTPServerRequest &req) noexcept; + static RuleEngineAPIMethod getRuleEngineAPIMethod(Poco::Net::HTTPServerRequest &req, Poco::Net::HTMLForm& form) noexcept; /*void sendDeviceCommand( devicerepository::Device* device, Poco::Net::HTTPServerRequest &req, diff --git a/src/hos_cosgi_testwebapp/webroot/libs/dstore/.directory b/src/hos_cosgi_testwebapp/webroot/libs/dstore/.directory new file mode 100644 index 00000000..929d5cb9 --- /dev/null +++ b/src/hos_cosgi_testwebapp/webroot/libs/dstore/.directory @@ -0,0 +1,4 @@ +[Dolphin] +Timestamp=2015,6,9,18,8,49 +Version=3 +ViewMode=1 diff --git a/src/hos_cosgi_testwebapp/webroot/libs/hos/DeviceRepo.js b/src/hos_cosgi_testwebapp/webroot/libs/hos/DeviceRepo.js index 455aaad3..db50d500 100644 --- a/src/hos_cosgi_testwebapp/webroot/libs/hos/DeviceRepo.js +++ b/src/hos_cosgi_testwebapp/webroot/libs/hos/DeviceRepo.js @@ -5,8 +5,8 @@ define([ 'dstore/RequestMemory', 'dstore/SimpleQuery', 'dstore/Trackable', - "dojox/socket", - "dojo/_base/xhr", + "hos/Socket", + "dojo/request/xhr", "dojox/uuid/generateRandomUuid" ], function(config, declare, Rest, RequestMemory, SimpleQuery, Trackable, Socket, xhr, generateRandomUuid){ @@ -51,8 +51,9 @@ define([ }); _store.sendCommand = function(id, cmd) { console.log("ToDo: send command", cmd ,"to " + id); - var devurl = config.hos.deviceRepoUrl + "/" + id + "?method=command&value=" + encodeURIComponent(JSON.stringify(cmd)); - xhr.get({url: devurl}).then(function(data){ + //var devurl = config.hos.deviceRepoUrl + "/" + id + "?method=command&value=" + encodeURIComponent(JSON.stringify(cmd)); + var devurl = config.hos.deviceRepoUrl + "/" + id; + xhr.post(devurl, {handleAs: 'json', data: {method: 'command', value: JSON.stringify(cmd)}}).then(function(data){ // Do something with the handled data }, function(err){ // Handle the error condition diff --git a/src/hos_cosgi_testwebapp/webroot/libs/hos/Socket.js b/src/hos_cosgi_testwebapp/webroot/libs/hos/Socket.js new file mode 100644 index 00000000..59bb41ed --- /dev/null +++ b/src/hos_cosgi_testwebapp/webroot/libs/hos/Socket.js @@ -0,0 +1,228 @@ +define([ + "dojo/_base/array", + "dojo/_base/lang", + "dojo/request/xhr", + "dojo/aspect", + "dojo/on", + "dojo/Evented", + "dojo/_base/url" +], function(array, lang, xhr, aspect, on, Evented, dBaseUrl) { + +var WebSocket = window.WebSocket; + +var Socket = function(/*dojo.__XhrArgs*/ argsOrUrl){ + // summary: + // Provides a simple socket connection using WebSocket, or alternate + // communication mechanisms in legacy browsers for comet-style communication. This is based + // on the WebSocket API and returns an object that implements the WebSocket interface: + // http://dev.w3.org/html5/websockets/#websocket + // description: + // Provides socket connections. This can be used with virtually any Comet protocol. + // argsOrUrl: + // This uses the same arguments as the other I/O functions in Dojo, or a + // URL to connect to. The URL should be a relative URL in order to properly + // work with WebSockets (it can still be host relative, like //other-site.org/endpoint) + // returns: + // An object that implements the WebSocket API + // example: + // | require(["dojox/socket", "dojo/aspect"], function(socket, aspect) { + // | var sock = socket({"url://comet-server/comet"); + // | // we could also add auto-reconnect support + // | // now we can connect to standard HTML5 WebSocket-style events + // | aspect.after(socket, "onmessage", function(event){ + // | var message = event.data; + // | // do something with the message + // | }); + // | // send something + // | sock.send("hi there"); + // | ... + // | }); + // You can also use the Reconnect module: + // | require["dojox/socket", "dojox/socket/Reconnect"], function(dxSocket, reconnect){ + // | var socket = dxSocket({url:"/comet"}); + // | // add auto-reconnect support + // | socket = reconnect(socket); + if(typeof argsOrUrl == "string"){ + argsOrUrl = {url: argsOrUrl}; + } + return WebSocket ? Socket.WebSocket(argsOrUrl, true) : Socket.LongPoll(argsOrUrl); +}; + +Socket.WebSocket = function(args, fallback){ + // summary: + // A wrapper for WebSocket, than handles standard args and relative URLs + var baseURI = document.baseURI || window.location.href; + var ws = new WebSocket(new dBaseUrl(baseURI.replace(/^http/i,'ws'), args.url)); + ws.on = function(type, listener){ + ws.addEventListener(type, listener, true); + }; + var opened; + aspect.after(ws, "onopen", function(event){ + opened = true; + }, true); + aspect.after(ws, "onclose", function(event){ + if(opened){ + return; + } + if(fallback){ + Socket.replace(ws, Socket.LongPoll(args), true); + } + }, true); + return ws; +}; + +Socket.replace = function(socket, newSocket, listenForOpen){ + // make the original socket a proxy for the new socket + socket.send = lang.hitch(newSocket, "send"); + socket.close = lang.hitch(newSocket, "close"); + var proxyEvent = function(type){ + (newSocket.addEventListener || newSocket.on).call(newSocket, type, function(event){ + on.emit(socket, event.type, event); + }, true); + }; + + if(listenForOpen){ + proxyEvent("open"); + } + // redirect the events as well + array.forEach(["message", "close", "error"], proxyEvent); +}; + +Socket.LongPoll = function(/*dojo.__XhrArgs*/ args){ + // summary: + // Provides a simple long-poll based comet-style socket/connection to a server and returns an + // object implementing the WebSocket interface: + // http://dev.w3.org/html5/websockets/#websocket + // args: + // This uses the same arguments as the other I/O functions in Dojo, with this addition: + // args.interval: + // Indicates the amount of time (in milliseconds) after a response was received + // before another request is made. By default, a request is made immediately + // after getting a response. The interval can be increased to reduce load on the + // server or to do simple time-based polling where the server always responds + // immediately. + // args.transport: + // Provide an alternate transport like dojo.io.script.get + // returns: + // An object that implements the WebSocket API + // example: + // | dojo.require("dojox.socket.LongPoll"); + // | var socket = dojox.socket.LongPoll({url:"/comet"}); + // or: + // | dojo.require("dojox.socket.LongPoll"); + // | dojox.socket.LongPoll.add(); + // | var socket = dojox.socket({url:"/comet"}); + + var cancelled = false, + first = true, + timeoutId, + connections = []; + + // create the socket object + var fire, connect; + var socket = { + send: function(data){ + // summary: + // Send some data using XHR or provided transport + var sendArgs = lang.delegate(args); + sendArgs.rawBody = data; + clearTimeout(timeoutId); + var deferred = first ? (first = false) || socket.firstRequest(sendArgs) : + socket.transport(sendArgs.url, sendArgs); + connections.push(deferred); + deferred.then(function(response){ + // got a response + socket.readyState = 1; + // remove the current connection + connections.splice(array.indexOf(connections, deferred), 1); + // reconnect to listen for the next message if there are no active connections, + // we queue it up in case one of the onmessage handlers has a message to send + if(!connections.length){ + timeoutId = setTimeout(connect, args.interval); + } + if(response){ + // now send the message along to listeners + fire("message", {data: response}, deferred); + } + }, function(error){ + connections.splice(array.indexOf(connections, deferred), 1); + // an error occurred, fire the appropriate event listeners + if(!cancelled){ + fire("error", {error:error}, deferred); + if(!connections.length){ + socket.readyState = 3; + fire("close", {wasClean:false}, deferred); + } + } + }); + return deferred; + }, + close: function(){ + // summary: + // Close the connection + socket.readyState = 2; + cancelled = true; + var i; + for(i = 0; i < connections.length; i++){ + connections[i].cancel(); + } + socket.readyState = 3; + fire("close", {wasClean:true}); + }, + transport: args.transport || xhr.post, + args: args, + url: args.url, + readyState: 0, + CONNECTING: 0, + OPEN: 1, + CLOSING: 2, + CLOSED: 3, + on: Evented.prototype.on, + firstRequest: function(args){ + // summary: + // This allows for special handling for the first request. This is useful for + // providing information to disambiguate between the first request and + // subsequent long-poll requests so the server can properly setup a + // connection on the first connection or reject a request for an expired + // connection if the request is not expecting to be the first for a connection. + // This method can be overriden. The default behavior is to include a Pragma + // header with a value of "start-long-poll" + var headers = (args.headers || (args.headers = {})); + headers.Pragma = "start-long-poll"; + try{ + return this.transport(args.url, args); + }finally{ + // cleanup the header so it is not used on subsequent requests + delete headers.Pragma; + } + } + }; + fire = function(type, object, deferred){ + if(socket["on" + type]){ + object.ioArgs = deferred && deferred.ioArgs; + object.type = type; + on.emit(socket, type, object); + } + }; + connect = function(){ + if(socket.readyState == 0){ + // we fire the open event now because we really don't know when the "socket" + // is truly open, and this gives us a to do a send() and get it included in the + // HTTP request + fire("open",{}); + } + // make the long-poll connection, to wait for response from the server + if(!connections.length){ + socket.send(); + } + }; + // provide an alias for Dojo's connect method + socket.connect = socket.on; + // do the initial connection + setTimeout(connect); + return socket; +}; + +return Socket; + +}); diff --git a/src/hos_cosgi_testwebapp/webroot/libs/hos/stores/DeviceRepoStore.js b/src/hos_cosgi_testwebapp/webroot/libs/hos/stores/DeviceRepoStore.js new file mode 100644 index 00000000..63341b4b --- /dev/null +++ b/src/hos_cosgi_testwebapp/webroot/libs/hos/stores/DeviceRepoStore.js @@ -0,0 +1,29 @@ +define([ + 'dojo/_base/declare', + 'dstore/Rest', + 'dstore/RequestMemory', + 'dstore/SimpleQuery', + 'dstore/Trackable', + 'hos/Socket', + 'dojo/_base/xhr', + 'dojox/uuid/generateRandomUuid' +], function(declare, Rest, RequestMemory, SimpleQuery, Trackable, Socket, xhr, generateRandomUuid){ + + return declare([Rest, RequestMemory, SimpleQuery, Trackable], { + + sendDeviceCommand: function(id, cmd) { + console.log("ToDo: send command", cmd ,"to " + id); + var devurl = config.hos.deviceRepoUrl + "/" + id + "?method=command&value=" + encodeURIComponent(JSON.stringify(cmd)); + xhr.get({url: devurl}).then(function(data){ + // Do something with the handled data + }, function(err){ + // Handle the error condition + }, function(evt){ + // Handle a progress event from the request if the + // browser supports XHR2 + }); + }; + + }); + +}); diff --git a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h index 5324e6c0..d1e5b4aa 100644 --- a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h +++ b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -308,9 +309,8 @@ protected: class NotImplementedException : public std::exception {}; - DataFormat getDataFormat(const Poco::Net::HTTPServerRequest& request, bool incoming = false) { + DataFormat getDataFormat(const Poco::Net::HTTPServerRequest& request, Poco::Net::HTMLForm& form, bool incoming = false) { DataFormat retval = DataFormat::JSON; // defaults to json - Poco::Net::HTMLForm form(request); if (form.has("format")) { std::string formatRaw = form.get("format"); if (formatRaw.compare("json") == 0) { @@ -375,16 +375,16 @@ protected: std::string encodeVariant( const libvariant::Variant& msg, const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTMLForm& form, DataFormat format = DataFormat::AUTO_GUESS ) { if (format == DataFormat::AUTO_GUESS) { - format = getDataFormat(request); + format = getDataFormat(request, form); } switch (format) { case DataFormat::JSON: return libvariant::SerializeJSON(msg, true); case DataFormat::JSONP: { - Poco::Net::HTMLForm form(request); std::string padding("callback"); if (form.has("callback")) { padding = form.get("callback"); @@ -436,24 +436,26 @@ protected: void handleResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const libvariant::Variant& msg, Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK, const std::string& mime = "" ) { resp.setStatusAndReason(status); - auto format = getDataFormat(request); + auto format = getDataFormat(request, form); if (mime.empty()) { setContentType(resp, format); } else { resp.setContentType(mime); } std::stringstream ss; - ss << encodeVariant(msg, request, format); + ss << encodeVariant(msg, request, form, format); std::string buffer = ss.str(); resp.sendBuffer(buffer.c_str(), buffer.size()); } void handleResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK, const std::string& mime = "text/plain" ) { @@ -464,6 +466,7 @@ protected: void handleErrorResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR, const std::string& mime = "text/plain" ) { @@ -478,6 +481,7 @@ protected: void handleErrorResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) { resp.setStatusAndReason(status); resp.setContentType("text/plain"); @@ -490,6 +494,7 @@ protected: void handleErrorResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const WebResourceHandlerException& e ) { resp.setStatusAndReason(e.getStatusCode()); resp.setContentType("text/plain"); @@ -502,6 +507,7 @@ protected: void handleErrorResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const std::exception& e, Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) { resp.setStatusAndReason(status); @@ -515,6 +521,7 @@ protected: void handleRequireAuthenticationResponse( Poco::Net::HTTPServerRequest& request, Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, const std::string& realm, const std::string& body = "", Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_UNAUTHORIZED, @@ -544,56 +551,67 @@ protected: << "======== DEBUG END ========" << std::endl ; } - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) { throw NotImplementedException(); } - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) { throw NotImplementedException(); } - virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) { throw NotImplementedException(); } - virtual void _handlePUT(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + virtual void _handlePUT(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) { throw NotImplementedException(); } virtual void handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override { + std::unique_ptr form; + if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_POST && req.has("Content-type") + && boost::starts_with(req.get("Content-type"), Poco::Net::HTMLForm::ENCODING_URL)) { + form.reset(new Poco::Net::HTMLForm(req, req.stream())); + } else { + form.reset(new Poco::Net::HTMLForm(req)); + } try { try { - _handleRequest(req, resp); + _handleRequest(req, resp, *form.get()); } catch (NotImplementedException& e) { try { if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) { - _handleGET(req, resp); + _handleGET(req, resp, *form.get()); } else if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) { - _handlePOST(req, resp); + _handlePOST(req, resp, *form.get()); } else if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_PUT) { - _handlePUT(req, resp); + _handlePUT(req, resp, *form.get()); } else { std::cout << "Unsupported HTTP Request Method" << std::endl; - handleErrorResponse(req, resp, (boost::format("HTTP method %1% not supported") % req.getMethod()).str(), + handleErrorResponse(req, resp, *form.get(), (boost::format("HTTP method %1% not supported") % req.getMethod()).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED); } } catch (NotImplementedException& e) { std::cout << "Unsupported HTTP Request Method" << std::endl; - handleErrorResponse(req, resp, (boost::format("HTTP method %1% not supported") % req.getMethod()).str(), + handleErrorResponse(req, resp, *form.get(), (boost::format("HTTP method %1% not supported") % req.getMethod()).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED); } } } catch (WebResourceHandlerException& e) { - handleErrorResponse(req, resp, e); + handleErrorResponse(req, resp, *form.get(), e); } catch (std::exception& e) { - handleErrorResponse(req, resp, e); + handleErrorResponse(req, resp, *form.get(), e); } catch (...) { - handleErrorResponse(req, resp, "Unknown exception"); + handleErrorResponse(req, resp, *form.get(), "Unknown exception"); } } template - void __handleEventWebSocket(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, EventQueue* eventQueue, HandlerFactory* factory) { + void __handleEventWebSocket( Poco::Net::HTTPServerRequest &req, + Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, + EventQueue* eventQueue, + HandlerFactory* factory ) { try { Poco::Net::WebSocket ws(req, resp); std::cout << "WebSocket connection established." << std::endl; @@ -627,7 +645,7 @@ protected: while (!breakLoop) { libvariant::Variant msg; msg = eventQueue->popEvent(); - std::string encoded = encodeVariant(msg, req); + std::string encoded = encodeVariant(msg, req, form); ws.sendFrame(encoded.c_str(), encoded.size(), Poco::Net::WebSocket::FRAME_FLAG_FIN | Poco::Net::WebSocket::FRAME_OP_TEXT); } std::cout << "WebSocket connection closed." << std::endl; @@ -651,7 +669,11 @@ protected: } template - void __handleEventLongPolling(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, EventQueue* eventQueue, HandlerFactory* factory) { + void __handleEventLongPolling( Poco::Net::HTTPServerRequest &req, + Poco::Net::HTTPServerResponse &resp, + Poco::Net::HTMLForm& form, + EventQueue* eventQueue, + HandlerFactory* factory ) { libvariant::Variant msg; bool breakLoop = false; boost::asio::deadline_timer connCheckTimer(factory->getIOService()); @@ -690,8 +712,55 @@ protected: breakLoop = true; connCheckTimer.cancel(); } - handleResponse(req, resp, msg); + handleResponse(req, resp, form, msg); } + + bool getRangeHeaderParameter(Poco::Net::HTTPServerRequest &req, size_t& start, size_t& end, size_t end_max) { + std::string rangeRaw; + if (req.has("Range")) { + rangeRaw = req.get("Range"); + } else if (req.has("X-Range")) { + rangeRaw = req.get("X-Range"); + } else { + return false; + } + if (!rangeRaw.empty()) { + if (boost::algorithm::starts_with(rangeRaw, "items=")) { + auto range = rangeRaw.substr(6); + std::vector splitParts; + boost::algorithm::split(splitParts, range, boost::algorithm::is_any_of("-")); + if (splitParts.size() == 2) { + try { + size_t r1 = std::stol(splitParts[0]); + size_t r2 = std::stol(splitParts[1]); + if (r1 <= r2) { + start = r1; + end = std::min(r2, end_max); + } + } catch (...) { + ROS_ERROR_STREAM("Could not parse Range parameter \"" << rangeRaw << "\"."); + } + } + } + } + return true; + } + + void setRangeHeaderParameter(Poco::Net::HTTPServerResponse &resp, size_t start, size_t end, size_t max) { + std::string rangeOut = (boost::format("items %1%-%2%/%3%") % start % end % max).str(); + resp.set("Content-Range", rangeOut); + resp.set("X-Content-Range", rangeOut); + } + + bool getPointerParameter(std::string& pointer, Poco::Net::HTMLForm& form) { + if (form.has("pointer")) { + pointer = form.get("pointer"); + return true; + } else { + return false; + } + } + }; diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.cpp index 17e22421..04ea8f09 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.cpp @@ -97,9 +97,17 @@ void WebServerServiceImpl::init() noexcept { webServerParams->setSoftwareVersion("Home Operating System alpha"); webServerParams->setMaxThreads(10); - Net::ServerSocket webServerSocket(8080); - webServer.reset(new Poco::Net::HTTPServer(&handlerFactory, webServerSocket, webServerParams)); - webServer->start(); + try { + Net::ServerSocket webServerSocket(8080); + webServer.reset(new Poco::Net::HTTPServer(&handlerFactory, webServerSocket, webServerParams)); + webServer->start(); + } catch(Poco::Net::NetException& e) { + ROS_ERROR_STREAM("Could not start HTTP server: " << e.displayText()); + webServer.reset(); + } catch(...) { + ROS_ERROR("Unknown exception while starting HTTP Server"); + webServer.reset(); + } try { @@ -143,6 +151,9 @@ void WebServerServiceImpl::init() noexcept { } catch(Poco::Net::SSLException& e) { ROS_ERROR_STREAM("Could not start HTTPS server: " << e.displayText()); webServerSSL.reset(); + } catch(Poco::Net::NetException& e) { + ROS_ERROR_STREAM("Could not start HTTPS server: " << e.displayText()); + webServerSSL.reset(); } catch(...) { ROS_ERROR("Unknown exception while starting HTTPS Server"); webServerSSL.reset(); diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.h index 7ad76b65..a0b33405 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/WebServerServiceImpl.h @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -54,15 +53,15 @@ private: class DefaultRequestHandler : public webresource::WebResourceHandlerImpl { public: - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override { - handleErrorResponse(req, resp, Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN); + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override { + handleErrorResponse(req, resp, form, Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN); } }; class RequireAuthenticationRequestHandler : public webresource::WebResourceHandlerImpl { public: - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override { - handleRequireAuthenticationResponse(req, resp, "Home Operating System Login"); + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override { + handleRequireAuthenticationResponse(req, resp, form, "Home Operating System Login"); } }; class ErrorRequestHandler @@ -72,8 +71,8 @@ private: Poco::Net::HTTPResponse::HTTPStatus status; ErrorRequestHandler(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus status) : msg(msg), status(status) {} - virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override { - handleErrorResponse(req, resp, msg, status); + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override { + handleErrorResponse(req, resp, form, msg, status); } }; public: diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp index 9aed6ff0..20a7ac5c 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp @@ -79,7 +79,7 @@ Poco::Net::HTTPRequestHandler* DirectoryResourceHandlerFactory::createRequestHan -void DirectoryResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { +void DirectoryResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { // Get "absolute" file path std::vector matchPieces, pathPieces; @@ -95,45 +95,46 @@ void DirectoryResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poc if (filesystem::exists(resourcePath)) { // Directory if (filesystem::is_directory(resourcePath)) { - serveDirectoy(req, resp, resourcePath); + serveDirectoy(req, resp, form, resourcePath); // Regular file } else if (filesystem::is_regular_file(resourcePath)) { // php script if (hasFileExtension(resourcePath, parent->phpExtensions)) { - servePhpFile(req, resp, resourcePath); + servePhpFile(req, resp, form, resourcePath); // other } else { - serveRegularFile(req, resp, resourcePath); + serveRegularFile(req, resp, form, resourcePath); } } else { - handleErrorResponse(req, resp, (boost::format("\"%1%\" has unknown type") % resourcePath).str(), + handleErrorResponse(req, resp, form, (boost::format("\"%1%\" has unknown type") % resourcePath).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR); } } else { - handleErrorResponse(req, resp, (boost::format("Could not find \"%1%\"") % resourcePath).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); + handleErrorResponse(req, resp, form, (boost::format("Could not find \"%1%\"") % resourcePath).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); } } -void DirectoryResourceHandler::serveDirectoy(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& dir) { +void DirectoryResourceHandler::serveDirectoy(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form, const boost::filesystem::path& dir) { auto index = dirGetFile(dir, parent->dirIndices); if (!index.empty()) { if (hasFileExtension(index, parent->phpExtensions)) { - servePhpFile(req, resp, index); + servePhpFile(req, resp, form, index); } else { - serveRegularFile(req, resp, index); + serveRegularFile(req, resp, form, index); } } else { - handleErrorResponse(req, resp, (boost::format("Listing of directory \"%1%\" not supported") % dir).str(), + handleErrorResponse(req, resp, form, (boost::format("Listing of directory \"%1%\" not supported") % dir).str(), Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); } } -void DirectoryResourceHandler::serveRegularFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& file) { +void DirectoryResourceHandler::serveRegularFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form, const boost::filesystem::path& file) { auto type = parent->contentTypes.find(file.extension().string()); if (type != parent->contentTypes.end()) { resp.setContentType(type->second); @@ -148,7 +149,7 @@ void DirectoryResourceHandler::serveRegularFile(Poco::Net::HTTPServerRequest &re -void DirectoryResourceHandler::servePhpFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& file) { +void DirectoryResourceHandler::servePhpFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form, const boost::filesystem::path& file) { // Compile file (ToDo: implement a cache) ph7_vm* pVm; int rc = ph7_compile_file(parent->getPH7Engine(), file.string().c_str(), &pVm, 0); diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h index 6e424b8c..ea997814 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h @@ -95,7 +95,7 @@ public: DirectoryResourceHandler(Poco::URI matchUri, DirectoryResourceHandlerFactory* parent, webserver::WebServerService* service, const DirectoryResourceEntry& resource, bool ssl) : matchUri(matchUri), parent(parent), service(service), resource(resource), ssl(ssl) {} - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override; static int ph7OutputConsumer(const void *pOutput, unsigned int nOutputLen, void *pUserData) { std::ostream& out = *(std::ostream*)pUserData; @@ -106,11 +106,11 @@ public: return PH7_OK; } - void serveDirectoy(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& dir); + void serveDirectoy(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form, const boost::filesystem::path& dir); - void serveRegularFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& file); + void serveRegularFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form, const boost::filesystem::path& file); - void servePhpFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& file); + void servePhpFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form, const boost::filesystem::path& file); }; diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.cpp index 6300f022..b2ae29c4 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.cpp @@ -27,44 +27,48 @@ HOS_SERVICE_IMPL_INFO( GenericWebResourceHandlerFactory, // Implementation -void SchemaResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { - Poco::URI uri(req.getURI()); - std::vector pathPieces, matchPieces; - uri.getPathSegments(pathPieces); - matchUri.getPathSegments(matchPieces); +void SchemaResourceHandler::_handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { + if (req.getMethod() == Net::HTTPRequest::HTTP_GET || req.getMethod() == Net::HTTPRequest::HTTP_POST) { + Poco::URI uri(req.getURI()); + std::vector pathPieces, matchPieces; + uri.getPathSegments(pathPieces); + matchUri.getPathSegments(matchPieces); - // Requested URL is too short to contain any schema - if (pathPieces.size() <= matchPieces.size()) { - throw WebResourceHandlerException("No schema given.", Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); + // Requested URL is too short to contain any schema + if (pathPieces.size() <= matchPieces.size()) { + throw WebResourceHandlerException("No schema given.", Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); - // Requested URL may contain a valid schema - } else { - // Get requested schema - std::stringstream ss; - for (int i = matchPieces.size(); i < pathPieces.size(); i++) { - ss << "/" << pathPieces[i]; - } - auto schemaPath = (boost::format("hos:schema:/%1%") % ss.str()).str(); - // Try to retrieve schema - try { - auto schema = schemaLoader->GetSchema(schemaPath); - Net::HTMLForm form(req); - if (form.has("pointer")) { - std::string pointer = form.get("pointer"); - if (schema.HasPath(pointer)) { - schema = schema.GetPath(pointer); - } else { - throw WebResourceHandlerException((boost::format("Could not resolve pointer \"%1%\"") % pointer ).str(), - Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); + // Requested URL may contain a valid schema + } else { + // Get requested schema + std::stringstream ss; + for (int i = matchPieces.size(); i < pathPieces.size(); i++) { + ss << "/" << pathPieces[i]; + } + auto schemaPath = (boost::format("hos:schema:/%1%") % ss.str()).str(); + // Try to retrieve schema + try { + auto schema = schemaLoader->GetSchema(schemaPath); + std::string pointer; + if (getPointerParameter(pointer, form)) { + if (schema.HasPath(pointer)) { + schema = schema.GetPath(pointer); + } else { + throw WebResourceHandlerException((boost::format("Could not resolve pointer \"%1%\"") % pointer ).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); + } } + handleResponse(req, resp, form, schema); + } catch(WebResourceHandlerException& e) { + throw e; + } catch(std::exception& e) { + throw WebResourceHandlerException((boost::format("Could not find schema \"%1%\": %2%") % schemaPath % e.what()).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); } - handleResponse(req, resp, schema); - } catch(WebResourceHandlerException& e) { - throw e; - } catch(std::exception& e) { - throw WebResourceHandlerException((boost::format("Could not find schema \"%1%\": %2%") % schemaPath % e.what()).str(), - Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_FOUND); } + } else { + handleErrorResponse(req, resp, form, (boost::format("HTTP method %1% not supported") % req.getMethod()).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_NOT_IMPLEMENTED); } } diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.h index 49a22016..fc3a7376 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/SchemaResource.h @@ -33,7 +33,7 @@ public: SchemaResourceHandler(Poco::URI matchUri, ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader, webserver::WebServerService* service) : matchUri(matchUri), nodehandle(nodehandle), schemaLoader(schemaLoader), service(service) {} - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override; }; diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp index 18f863ec..a19de0b2 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp @@ -44,7 +44,7 @@ Poco::Net::HTTPRequestHandler* WebSocketTestFactory::createRequestHandler(Poco:: -void WebSocketTestHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { +void WebSocketTestHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Net::HTMLForm& form) { try { Net::WebSocket ws(req, resp); std::cout << "WebSocket connection established." << std::endl; diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h index 9d06d2fb..4e1c2f19 100644 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h @@ -58,7 +58,7 @@ public: WebSocketTestHandler(Poco::URI matchUri, WebSocketTestFactory* parent, webserver::WebServerService* service, bool ssl) : matchUri(matchUri), parent(parent), service(service), ssl(ssl) {} - virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, Poco::Net::HTMLForm& form) override; };