From 52ae80523c14832ea042345bbf79dcc18ddd4e1d Mon Sep 17 00:00:00 2001 From: matzman Date: Wed, 27 May 2015 02:29:05 +0200 Subject: [PATCH] . --- src/hos_cosgi_webserver/CMakeLists.txt | 12 +- .../service/webserver/WebServerService.h | 23 +- .../resources/WebResourceHandlerImpl.h | 233 ++++++++++++++++++ .../webserver/WebServerServiceImpl.cpp | 103 ++++++-- .../service/webserver/WebServerServiceImpl.h | 79 +++++- .../resources/SchemaHTTPResource.cpp | 63 ----- .../webserver/resources/SchemaHTTPResource.h | 62 ----- .../webserver/resources/SchemaResource.cpp | 61 +++++ .../webserver/resources/SchemaResource.h | 44 ++++ 9 files changed, 517 insertions(+), 163 deletions(-) create mode 100644 src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h delete mode 100644 src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp delete mode 100644 src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h create mode 100644 src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.cpp create mode 100644 src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.h diff --git a/src/hos_cosgi_webserver/CMakeLists.txt b/src/hos_cosgi_webserver/CMakeLists.txt index b75a0f09..62744b68 100644 --- a/src/hos_cosgi_webserver/CMakeLists.txt +++ b/src/hos_cosgi_webserver/CMakeLists.txt @@ -8,6 +8,7 @@ find_package(catkin REQUIRED COMPONENTS hos_cosgi_core hos_cpp_utils hos_deps + cmake_modules roscpp ) @@ -17,6 +18,7 @@ find_package(CELIX REQUIRED) find_package(Boost REQUIRED) find_package(libvariant REQUIRED) find_package(libhttpserver REQUIRED) +find_package(Poco REQUIRED COMPONENTS Net NetSSL) ################################### @@ -44,6 +46,7 @@ include_directories( ${APR_INCLUDE_DIR}/apr-1.0 ${libvariant_INCLUDE_DIRS} ${libhttpserver_INCLUDE_DIRS} + ${Poco_INCLUDE_DIRS} ) set(BUNDLE_SYMBOLICNAME "hos.hos_webserver") @@ -54,15 +57,16 @@ bundle(hos_cosgi_webserver SOURCES ${PROJECT_SOURCE_DIR}/src/BundleActivator ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/WebServerServiceImpl - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource - ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource +# ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/DevicesHTTPResource + ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/SchemaResource ) target_link_libraries(hos_cosgi_webserver ${catkin_LIBRARIES} ${APR_LIBRARIES} - ${libvariant_LIBRARY} - ${libhttpserver_LIBRARY} + ${libvariant_LIBRARIES} + ${libhttpserver_LIBRARIES} + ${Poco_LIBRARIES} ) diff --git a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/WebServerService.h b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/WebServerService.h index 59e79e42..9dff833e 100644 --- a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/WebServerService.h +++ b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/WebServerService.h @@ -6,10 +6,18 @@ #include #include #include +#include // forward declarations namespace httpserver { class webserver;} +namespace libvariant { class Variant; } +namespace Poco { +class URI; +namespace Net { +class HTTPRequestHandler; +class HTTPServerRequest; +}} // service namespace @@ -23,16 +31,11 @@ namespace webserver { class WebServerService; - -class WebResource +class WebResourceHandlerFactory : public virtual interface::Object { public: - virtual void init( const std::vector& prefixPath, - const std::string& registeredPath, - const std::string& absolutePath, - httpserver::webserver& webserver, - WebServerService& service ) = 0; + virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl = false ) noexcept = 0; }; @@ -44,7 +47,11 @@ public: static constexpr const char* SERVICE_NAME = "hos_webserver_service"; - virtual void registerWebResource(const std::string& path, std::shared_ptr resource) = 0; + virtual bool registerWebResource(const std::string& path, std::shared_ptr factory) noexcept = 0; + + virtual const Poco::URI& getPrefixPath() noexcept = 0; + + virtual const std::vector& getPrefixPathPieces() noexcept = 0; }; diff --git a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h new file mode 100644 index 00000000..c0358292 --- /dev/null +++ b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h @@ -0,0 +1,233 @@ + +#pragma once + + +#include +#include +#include +#include +#include +#include +#include +#include + + + +// service namespace +namespace hos { +namespace cosgi { +namespace service { +namespace webserver { +namespace resources { + + + +/** + * Used for internal error handling. + */ +class WebResourceHandlerException + : public std::runtime_error { +protected: + Poco::Net::HTTPResponse::HTTPStatus statusCode; +public: + WebResourceHandlerException(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus statusCode) + : std::runtime_error(msg), statusCode(statusCode) {} + Poco::Net::HTTPResponse::HTTPStatus getStatusCode() const { + return statusCode; + } +}; + + + +class GenericWebResourceHandlerFactory + : public virtual WebResourceHandlerFactory { +public: + typedef std::function factory_t; +private: + factory_t _factory; +public: + GenericWebResourceHandlerFactory(factory_t factory) + : _factory(factory) {} + virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl = false ) noexcept override { + return _factory(request, ssl); + } +}; + + + +enum class ResponseDataFormat : int { + JSON, + JSONP, + YAML, + XMLPLIST, + BUNDLEHDR, + MSGPACK, + AUTO_GUESS +}; + + +class WebResourceHandlerImpl + : public Poco::Net::HTTPRequestHandler { + +protected: + + class NotImplementedException + : public std::exception {}; + + ResponseDataFormat getResponseDataFormat(const Poco::Net::HTTPServerRequest& request) { + ResponseDataFormat retval = ResponseDataFormat::JSON; + Poco::Net::HTMLForm form(request); + if (form.has("format")) { + std::string formatRaw = form.get("format"); + if (formatRaw.compare("json") == 0) { + // do nothing + } else if (formatRaw.compare("jsonp") == 0) { + retval = ResponseDataFormat::JSONP; + } else if (formatRaw.compare("yaml") == 0) { + retval = ResponseDataFormat::YAML; + } else if (formatRaw.compare("xmlplist") == 0) { + retval = ResponseDataFormat::XMLPLIST; + } else if (formatRaw.compare("bundlehdr") == 0) { + retval = ResponseDataFormat::BUNDLEHDR; + } else if (formatRaw.compare("msgpack") == 0) { + retval = ResponseDataFormat::MSGPACK; + } else { + throw WebResourceHandlerException((boost::format("Unknown format parameter \"%1%\"") % formatRaw).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); + } + } + return retval; + } + + std::string encodeVariant( const libvariant::Variant& msg, + const Poco::Net::HTTPServerRequest& request, + ResponseDataFormat format = ResponseDataFormat::AUTO_GUESS ) { + if (format == ResponseDataFormat::AUTO_GUESS) { + format = getResponseDataFormat(request); + } + switch (format) { + case ResponseDataFormat::JSON: + return libvariant::SerializeJSON(msg, true); + case ResponseDataFormat::JSONP: + { + Poco::Net::HTMLForm form(request); + std::string padding; + if (form.has("callback")) { + padding = form.get("callback"); + } else if (form.has("jsonp")) { + padding = form.get("jsonp"); + } + if (padding.empty()) { + throw WebResourceHandlerException("No callback parameter specified for JSONP.", + Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); + } + std::stringstream ss; + ss << padding << "(" << libvariant::SerializeJSON(msg, false) << ")"; + return ss.str(); + } + case ResponseDataFormat::YAML: + return libvariant::SerializeYAML(msg); + case ResponseDataFormat::XMLPLIST: + return libvariant::SerializeXMLPLIST(msg, true); + case ResponseDataFormat::BUNDLEHDR: + return libvariant::SerializeBundleHdr(msg); + case ResponseDataFormat::MSGPACK: + return libvariant::Serialize(msg, libvariant::SERIALIZE_MSGPACK); + default: + throw WebResourceHandlerException("Internal Server Error", + Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR); + } + } + + void handleResponse( const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTTPServerResponse &resp, + const libvariant::Variant& msg, + Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK ) { + resp.setStatusAndReason(status); + auto format = getResponseDataFormat(request); + resp.send() << encodeVariant(msg, request, format); + } + + void handleResponse( const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTTPServerResponse &resp, + const std::string& msg, + Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK ) { + resp.setStatusAndReason(status); + resp.send() << msg; + } + + void handleErrorResponse( const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTTPServerResponse &resp, + const std::string& msg = "Internal Server Error", + Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) { + resp.setStatusAndReason(status); + resp.send() << "ERROR: " << msg; + } + + void handleErrorResponse( const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTTPServerResponse &resp, + const WebResourceHandlerException& e ) { + resp.setStatusAndReason(e.getStatusCode()); + resp.send() << "ERROR: " << e.what(); + } + + void handleErrorResponse( const Poco::Net::HTTPServerRequest& request, + Poco::Net::HTTPServerResponse &resp, + const std::exception& e, + Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_INTERNAL_SERVER_ERROR ) { + resp.setStatusAndReason(status); + resp.send() << "ERROR: " << e.what(); + } + + virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + throw NotImplementedException(); + } + + virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + throw NotImplementedException(); + } + + virtual void _handlePOST(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + throw NotImplementedException(); + } + + virtual void handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override { + try { + try { + _handleRequest(req, resp); + } catch (NotImplementedException& e) { + try { + if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_GET) { + _handleGET(req, resp); + } else if (req.getMethod() == Poco::Net::HTTPRequest::HTTP_POST) { + _handlePOST(req, resp); + } else { + std::cout << "Unsupported HTTP Request Method" << std::endl; + handleErrorResponse(req, resp, (boost::format("Unsupported HTTP Request Method %1%") % req.getMethod()).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); + } + } catch (NotImplementedException& e) { + std::cout << "Unsupported HTTP Request Method" << std::endl; + handleErrorResponse(req, resp, (boost::format("Unsupported HTTP Request Method %1%") % req.getMethod()).str(), + Poco::Net::HTTPResponse::HTTPStatus::HTTP_BAD_REQUEST); + } + } + } catch (WebResourceHandlerException& e) { + handleErrorResponse(req, resp, e); + } catch (std::exception& e) { + handleErrorResponse(req, resp, e); + } catch (...) { + handleErrorResponse(req, resp, "Unknown exception"); + } + } + +}; + + + +} // namespace resources +} // namespace webserver +} // namespace service +} // namespace cosgi +} // namespace hos + 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 73646cc9..57895826 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 @@ -2,6 +2,7 @@ #include "WebServerServiceImpl.h" #include #include +#include "resources/SchemaResource.h" @@ -13,6 +14,7 @@ namespace webserver { using namespace libvariant; using namespace httpserver; +using namespace Poco; @@ -23,7 +25,12 @@ template<> const int implementation::ServiceInfo::minor = WebServerServiceImpl::~WebServerServiceImpl() { - webServer->stop(); + if (webServer) { + webServer->stop(); + } + if (webServerSSL) { + webServerSSL->stop(); + } } @@ -54,46 +61,104 @@ void WebServerServiceImpl::setRepoService(devicerepository::DeviceRepositoryServ } + +void WebServerServiceImpl::RequestHandlerFactory::DefaultRequestHandler::handleRequest( + Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp ) { + resp.setStatusAndReason(Poco::Net::HTTPResponse::HTTP_FORBIDDEN); + resp.setContentType("text/plain"); + resp.send() + << "ERROR: " << resp.getReason() << std::endl + << "========== DEBUG ==========" << std::endl + << "getURI: " << req.getURI() << std::endl + << "getHost: " << req.getHost() << std::endl + << "getMethod: " << req.getMethod() << std::endl + << "getTransferEncoding: " << req.getTransferEncoding() << std::endl + << "getVersion: " << req.getVersion() << std::endl + << "getContentType: " << req.getContentType() << std::endl + << "getContentLength: " << req.getContentLength() << std::endl + << "======== DEBUG END ========" << std::endl ; +} + + +Poco::Net::HTTPRequestHandler* WebServerServiceImpl::RequestHandlerFactory::createRequestHandler( const Poco::Net::HTTPServerRequest& request) { + // We want a longest prefix match. + // Since we have an ordered map, we just need to iterate it in reverse order and return the first match. + for (auto i = parent->webResourceFactories.rbegin(); i != parent->webResourceFactories.rend(); i++) { + if (boost::algorithm::starts_with(request.getURI(), i->first)) { + return i->second->createRequestHandler(request, ssl); + } + } + // return default handler + return new DefaultRequestHandler(); +} + + + void WebServerServiceImpl::init() noexcept { // Create and start webserver - create_webserver serverParams(8888); - serverParams.max_threads(5); - webServer.reset(new webserver::webserver(serverParams)); + //create_webserver serverParams(8888); + //serverParams.max_threads(5); + //webServer.reset(new webserver::webserver(serverParams)); - // Register web resources - registerWebResource( "/schemas", + Net::ServerSocket webServerSocket(8080); + Net::HTTPServerParams::Ptr webServerParams = new Net::HTTPServerParams(); + webServerParams->setServerName("Home Operating System"); + webServerParams->setSoftwareVersion("Home Operating System alpha"); + webServer.reset(new Poco::Net::HTTPServer(new RequestHandlerFactory(this), webServerSocket, webServerParams)); + webServer->start(); + /*auto& sslManager = Poco::Net::SSLManager::instance(); + sslManager.defaultServerContext(); + Poco::Net::SecureServerSocket webServerSSLSocket(8090); + webServerSSL.reset(new Poco::Net::HTTPServer(new RequestHandlerFactory(), webServerSocket, webServerParams)); + webServerSSL->start();*/ + + // Register web resources ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader, webserver::WebServerService* service + registerWebResource("/schemas", + std::make_shared([=](const Net::HTTPServerRequest&, bool)->Net::HTTPRequestHandler* { + return new resources::SchemaResourceHandler(nodehandle, &coreService->getSchemaLoader(), this); + }) + ); + registerWebResource("/default", + std::make_shared([=](const Net::HTTPServerRequest&, bool)->Net::HTTPRequestHandler* { + return new resources::WebResourceHandlerImpl(); + }) + ); + + + /*registerWebResource( "/schemas", std::make_shared(nodehandle, &coreService->getSchemaLoader()) ); registerWebResource( "/devices", std::make_shared(repoService) - ); + );*/ - webServer->start(false); + //webServer->start(false); initialized = true; ROS_INFO_STREAM("WebServerService: Initialized and Ready."); } -void WebServerServiceImpl::registerWebResource(const std::string& path, std::shared_ptr resource) { - std::string apath = getAbsolutePath(path); - if (webResources.find(apath) == webResources.end()) { - resource->init(prefixPath, path, apath, *webServer, *this); - webResources[apath] = resource; +bool WebServerServiceImpl::registerWebResource(const std::string& path, std::shared_ptr factory) noexcept { + auto uri = getAbsoluteURI(path); + if (webResourceFactories.find(uri.getPath()) == webResourceFactories.end()) { + webResourceFactories[uri.getPath()] = factory; + return true; } + return false; } -std::string WebServerServiceImpl::getAbsolutePath(const std::string& path) { +Poco::URI WebServerServiceImpl::getAbsoluteURI(const std::string& relpath) { std::stringstream ss; - for (auto p : prefixPath) { - ss << "/" << p; - } - std::string tpath = boost::algorithm::trim_copy(path); + ss << prefixPath.getPath(); + std::string tpath = boost::algorithm::trim_copy(relpath); if (tpath[0] != '/') { ss << "/"; } ss << tpath; - return ss.str(); + Poco::URI uri(ss.str()); + uri.normalize(); + return uri; } 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 f09ff42e..ef180672 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 @@ -7,13 +7,33 @@ #include #include #include +#include #include #include #include #include +#include + + + #include -#include "resources/SchemaHTTPResource.h" -#include "resources/DevicesHTTPResource.h" +//#include "resources/SchemaHTTPResource.h" +//#include "resources/DevicesHTTPResource.h" + + + + +#include + #include +#include +#include +#include +#include +#include +#include +#include + + @@ -24,6 +44,22 @@ namespace service { namespace webserver { +/** + * Used for internal error handling. + */ +class WebResourceHandlerException + : public std::runtime_error { +protected: + Poco::Net::HTTPResponse::HTTPStatus statusCode; +public: + WebResourceHandlerException(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus statusCode) + : std::runtime_error(msg), statusCode(statusCode) {} + Poco::Net::HTTPResponse::HTTPStatus getStatusCode() { + return statusCode; + } +}; + + class WebServerServiceImpl : public virtual WebServerService, @@ -31,19 +67,40 @@ class WebServerServiceImpl private: + class RequestHandlerFactory + : public Poco::Net::HTTPRequestHandlerFactory { + private: + WebServerServiceImpl* parent; + bool ssl; + class DefaultRequestHandler + : public Poco::Net::HTTPRequestHandler { + public: + virtual void handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; + }; + public: + RequestHandlerFactory(WebServerServiceImpl* parent, bool ssl = false) + : parent(parent), ssl(ssl) {} + virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request) override; + }; + service::core::CoreService* coreService = nullptr; devicerepository::DeviceRepositoryService* repoService = nullptr; ros::NodeHandle* nodehandle = nullptr; - std::vector prefixPath = { "hos" }; bool initialized = false; - std::unique_ptr webServer; - std::map> webResources; + Poco::URI prefixPath = Poco::URI("/hos"); + std::vector prefixPathPieces; + std::unique_ptr webServer; + std::unique_ptr webServerSSL; + std::map> webResourceFactories; void init() noexcept; public: + WebServerServiceImpl() { + prefixPath.getPathSegments(prefixPathPieces); + } ~WebServerServiceImpl(); @@ -56,7 +113,15 @@ public: /* Public Service Implementations */ - virtual void registerWebResource(const std::string& path, std::shared_ptr resource); + virtual bool registerWebResource(const std::string& path, std::shared_ptr factory) noexcept override; + + virtual const Poco::URI& getPrefixPath() noexcept override { + return prefixPath; + } + + virtual const std::vector& getPrefixPathPieces() noexcept override { + return prefixPathPieces; + } /* Internal */ @@ -65,7 +130,7 @@ public: return initialized; } - std::string getAbsolutePath(const std::string& path); + Poco::URI getAbsoluteURI(const std::string& relpath); }; diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp deleted file mode 100644 index c095ec5d..00000000 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.cpp +++ /dev/null @@ -1,63 +0,0 @@ - -#include "SchemaHTTPResource.h" -#include - - - -// Service Namespace -namespace hos { -namespace cosgi { -namespace service { -namespace webserver { -namespace resources { - - - -using namespace httpserver; -using namespace libvariant; - - - -void SchemaHTTPResource::init( const std::vector& prefixPath, - const std::string& registeredPath, - const std::string& absolutePath, - httpserver::webserver& webserver, - WebServerService& service ) { - webResource.prefixPath = prefixPath; - webResource.disallow_all(); - webResource.set_allowing(MHD_HTTP_METHOD_GET, true); - webserver.register_resource(absolutePath, &webResource, true); -} - - -void SchemaHTTPResource::WebResource::render_GET(const httpserver::http_request& req, httpserver::http_response** res) { - // Requested URL is too short to contain any schema - if (req.get_path_pieces_size() <= prefixPath.size() + 1) { - *res = new http_response(http_response_builder("ERROR: No schema given", 400).string_response()); - // Requested URL may contain a valid schema - } else { - // Get requested schema - std::stringstream ss; - for (int i = prefixPath.size() + 1; i < req.get_path_pieces_size(); i++) { - ss << "/" << req.get_path_piece(i); - } - auto schemaPath = (boost::format("hos:schema:/%1%") % ss.str()).str(); - // Try to retrieve schema - try { - auto schema = schemaLoader->GetSchema(schemaPath); - *res = new http_response(http_response_builder(SerializeJSON(schema, true), 200).string_response()); - } catch(std::exception& e) { - *res = new http_response(http_response_builder((boost::format("ERROR: Could not find schema \"%1%\": %2%") % schemaPath % e.what()).str(), 404).string_response()); - } - } -} - - -} // namespace resources -} // namespace webserver -} // namespace service -} // namespace cosgi -} // namespace hos - - - diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h deleted file mode 100644 index 24391638..00000000 --- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource.h +++ /dev/null @@ -1,62 +0,0 @@ - -#pragma once - - -#include -#include -#include -#include -#include - - - -// Service Namespace -namespace hos { -namespace cosgi { -namespace service { -namespace webserver { -namespace resources { - - - -class SchemaHTTPResource - : public virtual webserver::WebResource { - -private: - - /** - * libhttpserver does a static_cast with any class registered as http resource. Unfortunately this breaks classes - * using virtual inheritance. Therefore we register this inner class which does not use virtual inheritance. - */ - class WebResource - : public httpserver::http_resource { - - public: - - ros::NodeHandle* nodehandle = nullptr; - utils::SchemaLoader* schemaLoader = nullptr; - std::vector prefixPath; - - WebResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader) - : nodehandle(nodehandle), schemaLoader(schemaLoader) {} - - void render_GET(const httpserver::http_request& req, httpserver::http_response** res); - - } webResource; - -public: - SchemaHTTPResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader) - : webResource(nodehandle, schemaLoader) {} - - virtual void init( const std::vector& prefixPath, - const std::string& registeredPath, - const std::string& absolutePath, - httpserver::webserver& webserver, - WebServerService& service ); -}; - -} // namespace resources -} // namespace webserver -} // namespace service -} // namespace cosgi -} // namespace hos diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.cpp new file mode 100644 index 00000000..4285db16 --- /dev/null +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.cpp @@ -0,0 +1,61 @@ + +#include "SchemaResource.h" + +#include +#include +#include +#include +#include "../WebServerServiceImpl.h" + + + +// Service Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace webserver { +namespace resources { + + + +using namespace httpserver; +using namespace libvariant; + + + +void SchemaResourceHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) { + // Requested URL is too short to contain any schema + Poco::URI uri(req.getURI()); + std::vector pathPieces; + uri.getPathSegments(pathPieces); + size_t prefixPathSize = service->getPrefixPathPieces().size(); + if (pathPieces.size() <= prefixPathSize + 1) { + 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 = prefixPathSize + 1; 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); + handleResponse(req, resp, schema); + } 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); + } + } +} + + + +} // namespace resources +} // namespace webserver +} // namespace service +} // namespace cosgi +} // namespace hos + diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.h new file mode 100644 index 00000000..cc238e43 --- /dev/null +++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/resources/SchemaResource.h @@ -0,0 +1,44 @@ + +#pragma once + + +#include +#include +#include +#include +#include +#include + + + +// Service Namespace +namespace hos { +namespace cosgi { +namespace service { +namespace webserver { +namespace resources { + + + +class SchemaResourceHandler + : public virtual WebResourceHandlerImpl { + +private: + ros::NodeHandle* nodehandle; + utils::SchemaLoader* schemaLoader; + WebServerService* service; + +public: + SchemaResourceHandler(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader, webserver::WebServerService* service) + : nodehandle(nodehandle), schemaLoader(schemaLoader), service(service) {} + + virtual void _handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override; +}; + + + +} // namespace resources +} // namespace webserver +} // namespace service +} // namespace cosgi +} // namespace hos