.
This commit is contained in:
@@ -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}
|
||||
)
|
||||
|
||||
|
||||
|
||||
+15
-8
@@ -6,10 +6,18 @@
|
||||
#include <Variant/Variant.h>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
|
||||
// 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<std::string>& 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<WebResource> resource) = 0;
|
||||
virtual bool registerWebResource(const std::string& path, std::shared_ptr<WebResourceHandlerFactory> factory) noexcept = 0;
|
||||
|
||||
virtual const Poco::URI& getPrefixPath() noexcept = 0;
|
||||
|
||||
virtual const std::vector<std::string>& getPrefixPathPieces() noexcept = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <Poco/Net/HTMLForm.h>
|
||||
#include <Variant/Variant.h>
|
||||
#include <boost/format.hpp>
|
||||
#include <functional>
|
||||
|
||||
|
||||
|
||||
// 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<Poco::Net::HTTPRequestHandler*(const Poco::Net::HTTPServerRequest&, bool)> 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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "WebServerServiceImpl.h"
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <ros/ros.h>
|
||||
#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<WebServerServiceImpl>::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<resources::GenericWebResourceHandlerFactory>([=](const Net::HTTPServerRequest&, bool)->Net::HTTPRequestHandler* {
|
||||
return new resources::SchemaResourceHandler(nodehandle, &coreService->getSchemaLoader(), this);
|
||||
})
|
||||
);
|
||||
registerWebResource("/default",
|
||||
std::make_shared<resources::GenericWebResourceHandlerFactory>([=](const Net::HTTPServerRequest&, bool)->Net::HTTPRequestHandler* {
|
||||
return new resources::WebResourceHandlerImpl();
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
/*registerWebResource( "/schemas",
|
||||
std::make_shared<resources::SchemaHTTPResource>(nodehandle, &coreService->getSchemaLoader())
|
||||
);
|
||||
registerWebResource( "/devices",
|
||||
std::make_shared<resources::DevicesHTTPResource>(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<WebResource> 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<WebResourceHandlerFactory> 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,33 @@
|
||||
#include <hos/cosgi/service/core/CoreService.h>
|
||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||
#include <mutex>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
|
||||
#include <httpserver.hpp>
|
||||
#include "resources/SchemaHTTPResource.h"
|
||||
#include "resources/DevicesHTTPResource.h"
|
||||
//#include "resources/SchemaHTTPResource.h"
|
||||
//#include "resources/DevicesHTTPResource.h"
|
||||
|
||||
|
||||
|
||||
|
||||
#include <Poco/Net/HTTPServer.h>
|
||||
#include <Poco/Net/HTTPRequestHandlerFactory.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <Poco/URI.h>
|
||||
#include <Poco/Net/HTMLForm.h>
|
||||
#include <Poco/Net/SecureServerSocket.h>
|
||||
#include <Poco/Net/SSLManager.h>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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<std::string> prefixPath = { "hos" };
|
||||
bool initialized = false;
|
||||
|
||||
std::unique_ptr<httpserver::webserver> webServer;
|
||||
std::map<std::string, std::shared_ptr<WebResource>> webResources;
|
||||
Poco::URI prefixPath = Poco::URI("/hos");
|
||||
std::vector<std::string> prefixPathPieces;
|
||||
std::unique_ptr<Poco::Net::HTTPServer> webServer;
|
||||
std::unique_ptr<Poco::Net::HTTPServer> webServerSSL;
|
||||
std::map<std::string, std::shared_ptr<WebResourceHandlerFactory>> 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<WebResource> resource);
|
||||
virtual bool registerWebResource(const std::string& path, std::shared_ptr<WebResourceHandlerFactory> factory) noexcept override;
|
||||
|
||||
virtual const Poco::URI& getPrefixPath() noexcept override {
|
||||
return prefixPath;
|
||||
}
|
||||
|
||||
virtual const std::vector<std::string>& 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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
-63
@@ -1,63 +0,0 @@
|
||||
|
||||
#include "SchemaHTTPResource.h"
|
||||
#include <boost/format.hpp>
|
||||
|
||||
|
||||
|
||||
// Service Namespace
|
||||
namespace hos {
|
||||
namespace cosgi {
|
||||
namespace service {
|
||||
namespace webserver {
|
||||
namespace resources {
|
||||
|
||||
|
||||
|
||||
using namespace httpserver;
|
||||
using namespace libvariant;
|
||||
|
||||
|
||||
|
||||
void SchemaHTTPResource::init( const std::vector<std::string>& 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
|
||||
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/utils/SchemaLoader.h>
|
||||
#include <ros/ros.h>
|
||||
#include <httpserver.hpp>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
// 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<WebResource> {
|
||||
|
||||
public:
|
||||
|
||||
ros::NodeHandle* nodehandle = nullptr;
|
||||
utils::SchemaLoader* schemaLoader = nullptr;
|
||||
std::vector<std::string> prefixPath;
|
||||
|
||||
WebResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader)
|
||||
: nodehandle(nodehandle), schemaLoader(schemaLoader) {}
|
||||
|
||||
void render_GET(const httpserver::http_request& req, httpserver::http_response** res);
|
||||
|
||||
} webResource;
|
||||
|
||||
public:
|
||||
SchemaHTTPResource(ros::NodeHandle* nodehandle, utils::SchemaLoader* schemaLoader)
|
||||
: webResource(nodehandle, schemaLoader) {}
|
||||
|
||||
virtual void init( const std::vector<std::string>& prefixPath,
|
||||
const std::string& registeredPath,
|
||||
const std::string& absolutePath,
|
||||
httpserver::webserver& webserver,
|
||||
WebServerService& service );
|
||||
};
|
||||
|
||||
} // namespace resources
|
||||
} // namespace webserver
|
||||
} // namespace service
|
||||
} // namespace cosgi
|
||||
} // namespace hos
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
#include "SchemaResource.h"
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <Poco/Net/HTTPServerRequest.h>
|
||||
#include <Poco/Net/HTTPServerResponse.h>
|
||||
#include <Poco/URI.h>
|
||||
#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<std::string> 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
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <hos/cosgi/service/webserver/WebServerService.h>
|
||||
#include <hos/cosgi/service/webserver/resources/WebResourceHandlerImpl.h>
|
||||
#include <hos/utils/SchemaLoader.h>
|
||||
#include <Poco/Net/HTTPRequestHandler.h>
|
||||
#include <ros/ros.h>
|
||||
#include <vector>
|
||||
|
||||
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user