.
This commit is contained in:
@@ -8,6 +8,7 @@ find_package(catkin REQUIRED COMPONENTS
|
|||||||
hos_cosgi_core
|
hos_cosgi_core
|
||||||
hos_cpp_utils
|
hos_cpp_utils
|
||||||
hos_deps
|
hos_deps
|
||||||
|
cmake_modules
|
||||||
roscpp
|
roscpp
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@ find_package(CELIX REQUIRED)
|
|||||||
find_package(Boost REQUIRED)
|
find_package(Boost REQUIRED)
|
||||||
find_package(libvariant REQUIRED)
|
find_package(libvariant REQUIRED)
|
||||||
find_package(libhttpserver REQUIRED)
|
find_package(libhttpserver REQUIRED)
|
||||||
|
find_package(Poco REQUIRED COMPONENTS Net NetSSL)
|
||||||
|
|
||||||
|
|
||||||
###################################
|
###################################
|
||||||
@@ -44,6 +46,7 @@ include_directories(
|
|||||||
${APR_INCLUDE_DIR}/apr-1.0
|
${APR_INCLUDE_DIR}/apr-1.0
|
||||||
${libvariant_INCLUDE_DIRS}
|
${libvariant_INCLUDE_DIRS}
|
||||||
${libhttpserver_INCLUDE_DIRS}
|
${libhttpserver_INCLUDE_DIRS}
|
||||||
|
${Poco_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(BUNDLE_SYMBOLICNAME "hos.hos_webserver")
|
set(BUNDLE_SYMBOLICNAME "hos.hos_webserver")
|
||||||
@@ -54,15 +57,16 @@ bundle(hos_cosgi_webserver
|
|||||||
SOURCES
|
SOURCES
|
||||||
${PROJECT_SOURCE_DIR}/src/BundleActivator
|
${PROJECT_SOURCE_DIR}/src/BundleActivator
|
||||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/WebServerServiceImpl
|
${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/DevicesHTTPResource
|
||||||
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/SchemaHTTPResource
|
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/resources/SchemaResource
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(hos_cosgi_webserver
|
target_link_libraries(hos_cosgi_webserver
|
||||||
${catkin_LIBRARIES}
|
${catkin_LIBRARIES}
|
||||||
${APR_LIBRARIES}
|
${APR_LIBRARIES}
|
||||||
${libvariant_LIBRARY}
|
${libvariant_LIBRARIES}
|
||||||
${libhttpserver_LIBRARY}
|
${libhttpserver_LIBRARIES}
|
||||||
|
${Poco_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+15
-8
@@ -6,10 +6,18 @@
|
|||||||
#include <Variant/Variant.h>
|
#include <Variant/Variant.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
|
||||||
// forward declarations
|
// forward declarations
|
||||||
namespace httpserver { class webserver;}
|
namespace httpserver { class webserver;}
|
||||||
|
namespace libvariant { class Variant; }
|
||||||
|
namespace Poco {
|
||||||
|
class URI;
|
||||||
|
namespace Net {
|
||||||
|
class HTTPRequestHandler;
|
||||||
|
class HTTPServerRequest;
|
||||||
|
}}
|
||||||
|
|
||||||
|
|
||||||
// service namespace
|
// service namespace
|
||||||
@@ -23,16 +31,11 @@ namespace webserver {
|
|||||||
class WebServerService;
|
class WebServerService;
|
||||||
|
|
||||||
|
|
||||||
|
class WebResourceHandlerFactory
|
||||||
class WebResource
|
|
||||||
: public virtual interface::Object {
|
: public virtual interface::Object {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void init( const std::vector<std::string>& prefixPath,
|
virtual Poco::Net::HTTPRequestHandler* createRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl = false ) noexcept = 0;
|
||||||
const std::string& registeredPath,
|
|
||||||
const std::string& absolutePath,
|
|
||||||
httpserver::webserver& webserver,
|
|
||||||
WebServerService& service ) = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -44,7 +47,11 @@ public:
|
|||||||
|
|
||||||
static constexpr const char* SERVICE_NAME = "hos_webserver_service";
|
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 "WebServerServiceImpl.h"
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <ros/ros.h>
|
#include <ros/ros.h>
|
||||||
|
#include "resources/SchemaResource.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -13,6 +14,7 @@ namespace webserver {
|
|||||||
|
|
||||||
using namespace libvariant;
|
using namespace libvariant;
|
||||||
using namespace httpserver;
|
using namespace httpserver;
|
||||||
|
using namespace Poco;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +25,12 @@ template<> const int implementation::ServiceInfo<WebServerServiceImpl>::minor =
|
|||||||
|
|
||||||
|
|
||||||
WebServerServiceImpl::~WebServerServiceImpl() {
|
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 {
|
void WebServerServiceImpl::init() noexcept {
|
||||||
// Create and start webserver
|
// Create and start webserver
|
||||||
create_webserver serverParams(8888);
|
//create_webserver serverParams(8888);
|
||||||
serverParams.max_threads(5);
|
//serverParams.max_threads(5);
|
||||||
webServer.reset(new webserver::webserver(serverParams));
|
//webServer.reset(new webserver::webserver(serverParams));
|
||||||
|
|
||||||
// Register web resources
|
Net::ServerSocket webServerSocket(8080);
|
||||||
registerWebResource( "/schemas",
|
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())
|
std::make_shared<resources::SchemaHTTPResource>(nodehandle, &coreService->getSchemaLoader())
|
||||||
);
|
);
|
||||||
registerWebResource( "/devices",
|
registerWebResource( "/devices",
|
||||||
std::make_shared<resources::DevicesHTTPResource>(repoService)
|
std::make_shared<resources::DevicesHTTPResource>(repoService)
|
||||||
);
|
);*/
|
||||||
|
|
||||||
webServer->start(false);
|
//webServer->start(false);
|
||||||
initialized = true;
|
initialized = true;
|
||||||
ROS_INFO_STREAM("WebServerService: Initialized and Ready.");
|
ROS_INFO_STREAM("WebServerService: Initialized and Ready.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void WebServerServiceImpl::registerWebResource(const std::string& path, std::shared_ptr<WebResource> resource) {
|
bool WebServerServiceImpl::registerWebResource(const std::string& path, std::shared_ptr<WebResourceHandlerFactory> factory) noexcept {
|
||||||
std::string apath = getAbsolutePath(path);
|
auto uri = getAbsoluteURI(path);
|
||||||
if (webResources.find(apath) == webResources.end()) {
|
if (webResourceFactories.find(uri.getPath()) == webResourceFactories.end()) {
|
||||||
resource->init(prefixPath, path, apath, *webServer, *this);
|
webResourceFactories[uri.getPath()] = factory;
|
||||||
webResources[apath] = resource;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::string WebServerServiceImpl::getAbsolutePath(const std::string& path) {
|
Poco::URI WebServerServiceImpl::getAbsoluteURI(const std::string& relpath) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
for (auto p : prefixPath) {
|
ss << prefixPath.getPath();
|
||||||
ss << "/" << p;
|
std::string tpath = boost::algorithm::trim_copy(relpath);
|
||||||
}
|
|
||||||
std::string tpath = boost::algorithm::trim_copy(path);
|
|
||||||
if (tpath[0] != '/') {
|
if (tpath[0] != '/') {
|
||||||
ss << "/";
|
ss << "/";
|
||||||
}
|
}
|
||||||
ss << tpath;
|
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/core/CoreService.h>
|
||||||
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
#include <hos/cosgi/service/devicerepository/DeviceRepositoryService.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include <httpserver.hpp>
|
#include <httpserver.hpp>
|
||||||
#include "resources/SchemaHTTPResource.h"
|
//#include "resources/SchemaHTTPResource.h"
|
||||||
#include "resources/DevicesHTTPResource.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 {
|
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
|
class WebServerServiceImpl
|
||||||
: public virtual WebServerService,
|
: public virtual WebServerService,
|
||||||
@@ -31,19 +67,40 @@ class WebServerServiceImpl
|
|||||||
|
|
||||||
private:
|
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;
|
service::core::CoreService* coreService = nullptr;
|
||||||
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
devicerepository::DeviceRepositoryService* repoService = nullptr;
|
||||||
ros::NodeHandle* nodehandle = nullptr;
|
ros::NodeHandle* nodehandle = nullptr;
|
||||||
|
|
||||||
std::vector<std::string> prefixPath = { "hos" };
|
|
||||||
bool initialized = false;
|
bool initialized = false;
|
||||||
|
|
||||||
std::unique_ptr<httpserver::webserver> webServer;
|
Poco::URI prefixPath = Poco::URI("/hos");
|
||||||
std::map<std::string, std::shared_ptr<WebResource>> webResources;
|
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;
|
void init() noexcept;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
WebServerServiceImpl() {
|
||||||
|
prefixPath.getPathSegments(prefixPathPieces);
|
||||||
|
}
|
||||||
~WebServerServiceImpl();
|
~WebServerServiceImpl();
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +113,15 @@ public:
|
|||||||
|
|
||||||
/* Public Service Implementations */
|
/* 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 */
|
/* Internal */
|
||||||
@@ -65,7 +130,7 @@ public:
|
|||||||
return initialized;
|
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