diff --git a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/webresource/DeviceRepoHandler.h b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/webresource/DeviceRepoHandler.h
index 80e69a7c..1882a850 100644
--- a/src/hos_cosgi_devicerepository/src/hos/cosgi/service/webresource/DeviceRepoHandler.h
+++ b/src/hos_cosgi_devicerepository/src/hos/cosgi/service/webresource/DeviceRepoHandler.h
@@ -53,6 +53,10 @@ public:
this->service = service;
}
+ virtual bool excludeFromAuthentication() noexcept override {
+ return false;
+ }
+
};
diff --git a/src/hos_cosgi_testwebapp/webroot/websocket.html b/src/hos_cosgi_testwebapp/webroot/websocket.html
index 860e5ce2..c19499b3 100644
--- a/src/hos_cosgi_testwebapp/webroot/websocket.html
+++ b/src/hos_cosgi_testwebapp/webroot/websocket.html
@@ -3,22 +3,27 @@
WebSocketServer
diff --git a/src/hos_cosgi_webserver/CMakeLists.txt b/src/hos_cosgi_webserver/CMakeLists.txt
index 596668ec..5edd5a20 100644
--- a/src/hos_cosgi_webserver/CMakeLists.txt
+++ b/src/hos_cosgi_webserver/CMakeLists.txt
@@ -57,6 +57,7 @@ bundle(hos_cosgi_webserver
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/WebServerServiceImpl
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/webresource/DirectoryResource
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/webresource/SchemaResource
+ ${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/webresource/WebSocketTest
${PROJECT_SOURCE_DIR}/src/hos/cosgi/service/webserver/ph7/ph7
DIRECTORIES
${PROJECT_SOURCE_DIR}/certs
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 6ac37414..8fe84642 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
@@ -3,6 +3,7 @@
#include
+#include
#include
#include
#include
@@ -44,6 +45,7 @@ struct DirectoryResourceEntry {
: dir(dir) {}
std::string dir;
bool enablePHP = false;
+ bool excludeFromAuthentication = false;
};
@@ -59,6 +61,8 @@ public:
virtual Poco::Net::HTTPRequestHandler* createRequestHandler( Poco::URI matchUri, const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept = 0;
virtual void setWebServerService(WebServerService* service) noexcept = 0;
+
+ virtual bool excludeFromAuthentication() noexcept = 0;
};
@@ -80,7 +84,18 @@ public:
virtual void addDirectoryResource(const std::string& baseUri, const DirectoryResourceEntry& entry) = 0;
- virtual Poco::Net::HTTPRequestHandler* createDefaultRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl ) = 0;
+ virtual bool isAuthenticationRequired() noexcept = 0;
+
+ virtual bool checkAuthentication( const Poco::Net::HTTPServerRequest& request ) = 0;
+
+ virtual Poco::Net::HTTPRequestHandler* createDefaultRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept = 0;
+
+ virtual Poco::Net::HTTPRequestHandler* createRequireAuthenticationRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept = 0;
+
+ virtual Poco::Net::HTTPRequestHandler* createErrorHandler( const Poco::Net::HTTPServerRequest& request,
+ bool ssl,
+ const std::string& msg = std::string(),
+ Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR ) noexcept = 0;
};
diff --git a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h
index 4b68d350..2b13c4d3 100644
--- a/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h
+++ b/src/hos_cosgi_webserver/include/hos_cosgi_webserver/hos/cosgi/service/webserver/webresource/WebResourceHandlerImpl.h
@@ -66,6 +66,9 @@ public:
virtual void setWebServerService(webserver::WebServerService* service) noexcept override {
this->service = service;
}
+ virtual bool excludeFromAuthentication() noexcept override {
+ return false;
+ }
};
@@ -220,6 +223,19 @@ protected:
addDebugInfo(out, request, resp);
}
+ void handleRequireAuthenticationResponse( const Poco::Net::HTTPServerRequest& request,
+ Poco::Net::HTTPServerResponse &resp,
+ const std::string& realm,
+ const std::string& body = std::string(),
+ Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTPStatus::HTTP_UNAUTHORIZED ) {
+ resp.requireAuthentication(realm);
+ if (body.empty()) {
+ resp.send() << "ERROR: " << resp.getReason();
+ } else {
+ resp.send() << body;
+ }
+ }
+
void addDebugInfo(std::ostream& out, const Poco::Net::HTTPServerRequest& req, Poco::Net::HTTPServerResponse &resp) {
out << std::endl << std::endl
<<"========== DEBUG ==========" << std::endl
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 45d3bbb3..aefb13e9 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
@@ -7,7 +7,9 @@
#include
#include
#include
+#include
+#include "webresource/WebSocketTest.h"
namespace hos {
namespace cosgi {
@@ -53,16 +55,33 @@ void WebServerServiceImpl::setCoreService(service::core::CoreService* coreServic
Poco::Net::HTTPRequestHandler* WebServerServiceImpl::RequestHandlerFactory::createRequestHandler( const Poco::Net::HTTPServerRequest& request) {
+ WebResourceHandlerFactory* factory = nullptr;
+ std::string matchUri;
// 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)
&& ( request.getURI().size() == i->first.size() || request.getURI()[i->first.size()] == '/' ) ) {
- return i->second->createRequestHandler(Poco::URI(i->first), request, ssl);
+ factory = i->second;
+ matchUri = i->first;
+ break;
+ }
+ }
+ if (factory) {
+ if ( parent->requireAuthentication
+ && !factory->excludeFromAuthentication()
+ && !parent->checkAuthentication(request) ) {
+ return new RequireAuthenticationRequestHandler();
+ } else {
+ return factory->createRequestHandler(Poco::URI(matchUri), request, ssl);
+ }
+ } else {
+ if ( parent->requireAuthentication && !parent->checkAuthentication(request) ) {
+ return new RequireAuthenticationRequestHandler();
+ } else {
+ return new DefaultRequestHandler();
}
}
- // return default handler
- return new DefaultRequestHandler();
}
@@ -132,6 +151,9 @@ void WebServerServiceImpl::init() noexcept {
return new webresource::SchemaResourceHandler(matchUri, nodehandle, &coreService->getSchemaLoader(), this);
})
);
+ registerWebResource("/websocket",
+ new webresource::WebSocketTestFactory()
+ );
initialized = true;
ROS_INFO_STREAM("WebServerService: Initialized and Ready.");
@@ -176,6 +198,28 @@ Poco::URI WebServerServiceImpl::getAbsoluteURI(const std::string& relpath) {
}
+bool WebServerServiceImpl::checkAuthentication( const Poco::Net::HTTPServerRequest& request ) {
+ if (request.hasCredentials()) {
+ std::string scheme;
+ std::string authInfo;
+ request.getCredentials(scheme, authInfo);
+ if (scheme == Poco::Net::HTTPBasicCredentials::SCHEME) {
+ Poco::Net::HTTPBasicCredentials cred(authInfo);
+ auto user = authorizedUsers.find(cred.getUsername());
+ if (user != authorizedUsers.end()) {
+ if (user->second == cred.getPassword()) {
+ return true;
+ }
+ }
+ } else {
+ std::stringstream ss;
+ ss << "Unsupported authentication scheme \"" << scheme << "\"." << std::endl;
+ throw std::runtime_error(ss.str());
+ }
+ }
+ return false;
+}
+
} // namespace webserver
} // namespace service
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 d770a190..7ad76b65 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
@@ -58,6 +58,24 @@ private:
handleErrorResponse(req, resp, Poco::Net::HTTPResponse::HTTPStatus::HTTP_FORBIDDEN);
}
};
+ class RequireAuthenticationRequestHandler
+ : public webresource::WebResourceHandlerImpl {
+ public:
+ virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override {
+ handleRequireAuthenticationResponse(req, resp, "Home Operating System Login");
+ }
+ };
+ class ErrorRequestHandler
+ : public webresource::WebResourceHandlerImpl {
+ public:
+ std::string msg;
+ Poco::Net::HTTPResponse::HTTPStatus status;
+ ErrorRequestHandler(const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus status)
+ : msg(msg), status(status) {}
+ virtual void _handleRequest(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) override {
+ handleErrorResponse(req, resp, msg, status);
+ }
+ };
public:
RequestHandlerFactory(WebServerServiceImpl* parent, bool ssl = false)
: parent(parent), ssl(ssl) {}
@@ -65,6 +83,12 @@ private:
Poco::Net::HTTPRequestHandler* createDefaultRequestHandler( const Poco::Net::HTTPServerRequest& request) {
return new DefaultRequestHandler();
}
+ Poco::Net::HTTPRequestHandler* createRequireAuthenticationRequestHandler( const Poco::Net::HTTPServerRequest& request) {
+ return new RequireAuthenticationRequestHandler();
+ }
+ Poco::Net::HTTPRequestHandler* createErrorHandler( const Poco::Net::HTTPServerRequest& request, const std::string& msg, Poco::Net::HTTPResponse::HTTPStatus status) {
+ return new ErrorRequestHandler(msg, status);
+ }
} handlerFactory, handlerFactorySSL;
std::string installDir;
@@ -79,6 +103,11 @@ private:
std::unique_ptr webServer;
std::unique_ptr webServerSSL;
std::map webResourceFactories;
+ bool requireAuthentication = false;
+ std::map authorizedUsers = {
+ {"user", "secret"},
+ {"root", "pass"},
+ };
void init() noexcept;
@@ -109,6 +138,29 @@ public:
}
+ virtual Poco::Net::HTTPRequestHandler* createRequireAuthenticationRequestHandler( const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept override {
+ if (ssl) {
+ return handlerFactorySSL.createRequireAuthenticationRequestHandler(request);
+ } else {
+ return handlerFactory.createRequireAuthenticationRequestHandler(request);
+ }
+ }
+
+ virtual Poco::Net::HTTPRequestHandler* createErrorHandler( const Poco::Net::HTTPServerRequest& request,
+ bool ssl,
+ const std::string& msg = std::string(),
+ Poco::Net::HTTPResponse::HTTPStatus status = Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR ) noexcept {
+ if (ssl) {
+ return handlerFactorySSL.createErrorHandler(request, msg, status);
+ } else {
+ return handlerFactory.createErrorHandler(request, msg, status);
+ }
+ }
+
+ virtual bool isAuthenticationRequired() noexcept {
+ return requireAuthentication;
+ }
+
/* Internal */
@@ -118,6 +170,8 @@ public:
Poco::URI getAbsoluteURI(const std::string& relpath);
+ bool checkAuthentication( const Poco::Net::HTTPServerRequest& request ) override;
+
};
diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp
index 606fa747..afaa416b 100644
--- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp
+++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.cpp
@@ -56,9 +56,23 @@ DirectoryResourceHandlerFactory::~DirectoryResourceHandlerFactory() {
Poco::Net::HTTPRequestHandler* DirectoryResourceHandlerFactory::createRequestHandler(Poco::URI matchUri, const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept {
auto e = dirMap.find(matchUri.getPath());
if (e != dirMap.end()) {
- return new DirectoryResourceHandler(matchUri, this, service, e->second, ssl);
+ try {
+ if ( service->isAuthenticationRequired()
+ && !e->second.excludeFromAuthentication
+ && !service->checkAuthentication(request) ) {
+ return service->createRequireAuthenticationRequestHandler(request, ssl);
+ } else {
+ return new DirectoryResourceHandler(matchUri, this, service, e->second, ssl);
+ }
+ } catch (std::exception& e) {
+ return service->createErrorHandler(request, ssl, e.what(), Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
+ }
} else {
- return service->createDefaultRequestHandler(request, ssl);
+ if ( service->isAuthenticationRequired() && !service->checkAuthentication(request) ) {
+ return service->createRequireAuthenticationRequestHandler(request, ssl);
+ } else {
+ return service->createDefaultRequestHandler(request, ssl);
+ }
}
}
@@ -120,46 +134,6 @@ void DirectoryResourceHandler::serveDirectoy(Poco::Net::HTTPServerRequest &req,
void DirectoryResourceHandler::serveRegularFile(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp, const boost::filesystem::path& file) {
-
-
- std::ostream& ostr = std::cout;
- ostr << "";
- ostr << "";
- ostr << "WebSocketServer";
- ostr << "";
- ostr << "";
- ostr << "";
- ostr << " WebSocket Server
";
- ostr << " Run WebSocket Script
";
- ostr << "";
- ostr << "";
-
auto type = parent->contentTypes.find(file.extension().string());
if (type != parent->contentTypes.end()) {
resp.setContentType(type->second);
diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h
index 87c70b2f..6e424b8c 100644
--- a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h
+++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/DirectoryResource.h
@@ -69,6 +69,10 @@ public:
this->service = service;
}
+ virtual bool excludeFromAuthentication() noexcept override {
+ return false;
+ }
+
ph7* getPH7Engine() {
return pEngine;
}
diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp
new file mode 100644
index 00000000..18f863ec
--- /dev/null
+++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.cpp
@@ -0,0 +1,85 @@
+
+#include "WebSocketTest.h"
+#include "../WebServerServiceImpl.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include "Poco/Net/NetException.h"
+#include
+#include "Poco/Format.h"
+
+
+
+// Service Namespace
+namespace hos {
+namespace cosgi {
+namespace service {
+namespace webserver {
+namespace webresource {
+
+
+using namespace Poco;
+
+
+
+
+HOS_SERVICE_IMPL_INFO( WebSocketTestFactory, // Implementation
+ service::webserver::WebResourceHandlerFactory::SERVICE_NAME, // Service Name
+ 1, 0 ) // Version
+
+
+
+
+
+Poco::Net::HTTPRequestHandler* WebSocketTestFactory::createRequestHandler(Poco::URI matchUri, const Poco::Net::HTTPServerRequest& request, bool ssl ) noexcept {
+
+ if(request.find("Upgrade") != request.end() && Poco::icompare(request["Upgrade"], "websocket") == 0)
+ return new WebSocketTestHandler(matchUri, this, service, ssl);
+ else
+ return service->createErrorHandler(request, ssl, "Non-Websocket Requests are unsupported.");
+}
+
+
+
+void WebSocketTestHandler::_handleGET(Poco::Net::HTTPServerRequest &req, Poco::Net::HTTPServerResponse &resp) {
+ try {
+ Net::WebSocket ws(req, resp);
+ std::cout << "WebSocket connection established." << std::endl;
+ char buffer[1024];
+ int flags;
+ int n;
+ do {
+ n = ws.receiveFrame(buffer, sizeof(buffer), flags);
+ std::cout << Poco::format("Frame received (length=%d, flags=0x%x).", n, unsigned(flags)) << std::endl;
+ ws.sendFrame(buffer, n, flags);
+ //} while (n > 0 || (flags & Net::WebSocket::FRAME_OP_BITMASK) != Net::WebSocket::FRAME_OP_CLOSE);
+ } while (n > 0 || ((flags & Net::WebSocket::FRAME_OP_BITMASK) != Net::WebSocket::FRAME_OP_CLOSE && (flags & Net::WebSocket::FRAME_OP_BITMASK) != 0));
+ std::cout << "WebSocket connection closed." << std::endl;
+ } catch (Net::WebSocketException& exc) {
+ std::cout << "WebSocketException caught: " << exc.what() << std::endl;
+ switch (exc.code()) {
+ case Net::WebSocket::WS_ERR_HANDSHAKE_UNSUPPORTED_VERSION:
+ resp.set("Sec-WebSocket-Version", Net::WebSocket::WEBSOCKET_VERSION);
+ // fallthrough
+ case Net::WebSocket::WS_ERR_NO_HANDSHAKE:
+ case Net::WebSocket::WS_ERR_HANDSHAKE_NO_VERSION:
+ case Net::WebSocket::WS_ERR_HANDSHAKE_NO_KEY:
+ resp.setStatusAndReason(Net::HTTPResponse::HTTP_BAD_REQUEST);
+ resp.setContentLength(0);
+ resp.send();
+ break;
+ }
+ }
+}
+
+
+
+} // namespace resources
+} // namespace webserver
+} // namespace service
+} // namespace cosgi
+} // namespace hos
+
diff --git a/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h
new file mode 100644
index 00000000..9d06d2fb
--- /dev/null
+++ b/src/hos_cosgi_webserver/src/hos/cosgi/service/webserver/webresource/WebSocketTest.h
@@ -0,0 +1,70 @@
+#pragma once
+
+
+#include
+#include
+#include
+#include
+#include
+#include