src/HTTPDHandler.h
branchv_0
changeset 4 37a86904145c
parent 3 1184f3de5533
child 5 121981e6bd54
--- a/src/HTTPDHandler.h	Fri Apr 08 22:38:45 2022 +0200
+++ b/src/HTTPDHandler.h	Sat Apr 09 17:50:46 2022 +0200
@@ -22,6 +22,8 @@
 #include <codecvt>
 #include <regex>
 #include <stdexcept>
+#include <mutex>
+#include <shared_mutex>
 
 #include <relpipe/common/type/typedefs.h>
 #include <relpipe/reader/TypeId.h>
@@ -45,27 +47,146 @@
 	shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
 	Configuration configuration;
 	std::shared_ptr<HTTPServer> httpServer;
+	relpipe::common::type::StringX currentRelationName;
 	std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata;
 	std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata;
 	size_t currentAttributeIndex = 0;
 	size_t currentRecordNumber = 1;
 
+	class RequestMatcher {
+	public:
+		std::regex method = std::regex(".*");
+		std::regex url = std::regex(".*");
+
+		virtual ~RequestMatcher() = default;
+
+		bool matches(const std::string& method, const std::string& url) const {
+			bool result = true;
+			result &= std::regex_match(method, this->method);
+			result &= std::regex_match(url, this->url);
+			return result;
+		}
+	};
+
+	class HeaderTemplate {
+	public:
+		std::string name;
+		std::string value;
+
+		HeaderTemplate() {
+		}
+
+		HeaderTemplate(std::string name, std::string value) : name(name), value(value) {
+		}
+
+		virtual ~HeaderTemplate() = default;
+	};
+
+	class GlobalHeaderTemplate : public HeaderTemplate, public RequestMatcher {
+	} headerTemplate;
+
+	std::vector<GlobalHeaderTemplate> headerTemplates;
+
+	class ResponseTemplate : public RequestMatcher {
+	public:
+		std::string body;
+		uint16_t code = 200;
+		std::vector<HeaderTemplate> headers;
+	} responseTemplate;
+
+	std::vector<ResponseTemplate> responseTemplates;
+
+	std::mutex templatesMutex; // TODO: read-write lock (for responseTemplates and headerTemplates)
+
 	class RequestHandler : public HTTPServer::RequestHandler {
+	private:
+		std::vector<ResponseTemplate>* responseTemplates;
+		std::vector<GlobalHeaderTemplate>* headerTemplates;
+		std::mutex* templatesMutex;
 	public:
 
+		RequestHandler(std::mutex* templatesMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) {
+		}
+
+		virtual ~RequestHandler() = default;
+
 		const HTTPServer::Response handle(const HTTPServer::Request& request) override {
 			HTTPServer::Response response;
 
-			// TODO: return real responses
-			response.code = 200;
-			response.body = "<h1>greetings and salutations</h1>";
+			std::lock_guard<std::mutex> lock(*templatesMutex);
+			for (ResponseTemplate t : *responseTemplates) {
+				if (t.matches(request.method, request.url)) {
+					response.code = t.code;
+					response.body = t.body;
+					// TODO: replace global header values with request-specific ones instead of appending?
+					for (const GlobalHeaderTemplate& h : *headerTemplates) if (h.matches(request.method, request.url)) response.header.push_back(HTTPServer::Header(h.name, h.value));
+					for (const HeaderTemplate& h : t.headers) response.header.push_back(HTTPServer::Header(h.name, h.value));
+					return response;
+				}
+			}
 
+			response.code = 404;
+			response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>";
 			return response;
 		}
 
 	};
 
-	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>();
+	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(&templatesMutex, &responseTemplates, &headerTemplates);
+
+	relpipe::common::type::StringX getHeaderAttributePrefix() {
+		// might be configurable - parametrized
+		return L"header.";
+	}
+
+	bool isHeaderAttribute(const relpipe::common::type::StringX& attributeName) {
+		return attributeName.rfind(getHeaderAttributePrefix(), 0) == 0;
+	}
+
+	relpipe::common::type::StringX fetchHeaderName(const relpipe::common::type::StringX& attributeName) {
+		// TODO: recognize several modes: header.set.*, header.add.*, header.remove.*
+		return attributeName.substr(getHeaderAttributePrefix().size());
+	}
+
+	void headerTemplateAttribute(const relpipe::common::type::StringX& value) {
+		auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName();
+
+		if (attributeName == L"url") headerTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*");
+		else if (attributeName == L"method") headerTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*");
+		else if (attributeName == L"name") headerTemplate.name = convertor.to_bytes(value);
+		else if (attributeName == L"value") headerTemplate.value = convertor.to_bytes(value); // TODO: header encoding?
+		else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value));
+
+		currentAttributeIndex++;
+
+		if (currentAttributeIndex % currentReaderMetadata.size() == 0) {
+			std::lock_guard<std::mutex> lock(templatesMutex);
+			currentAttributeIndex = 0;
+			headerTemplates.push_back(headerTemplate);
+			headerTemplate = GlobalHeaderTemplate();
+		}
+	}
+
+	void responseTemplateAttribute(const relpipe::common::type::StringX& value) {
+		auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName();
+
+		if (attributeName == L"url") responseTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*");
+		else if (attributeName == L"method") responseTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*");
+		else if (attributeName == L"code") responseTemplate.code = std::stoi(value);
+		else if (attributeName == L"text") responseTemplate.body = convertor.to_bytes(value);
+		else if (attributeName == L"data") responseTemplate.body = "TODO: read binary data: " + convertor.to_bytes(value); // TODO: read hex/binary request body
+		else if (isHeaderAttribute(attributeName)) responseTemplate.headers.push_back(HeaderTemplate(convertor.to_bytes(fetchHeaderName(attributeName)), convertor.to_bytes(value))); // TODO: header encoding?
+		else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value));
+
+		currentAttributeIndex++;
+
+		if (currentAttributeIndex % currentReaderMetadata.size() == 0) {
+			std::lock_guard<std::mutex> lock(templatesMutex);
+			currentAttributeIndex = 0;
+			responseTemplates.push_back(responseTemplate);
+			responseTemplate = ResponseTemplate();
+		}
+	}
 
 public:
 
@@ -77,15 +198,18 @@
 	}
 
 	void startRelation(relpipe::common::type::StringX name, std::vector<relpipe::reader::handlers::AttributeMetadata> attributes) override {
-
+		currentRelationName = name;
+		currentReaderMetadata = attributes;
 	}
 
 	void attribute(const relpipe::common::type::StringX& value) override {
-
+		if (currentRelationName == L"header_template") headerTemplateAttribute(value);
+		else if (currentRelationName == L"response_template") responseTemplateAttribute(value);
+		else throw std::invalid_argument("Unsupported relation: " + convertor.to_bytes(currentRelationName));
 	}
 
 	void endOfPipe() {
-
+		sleep(60); // FIXME: run for configured interval or number of requests or forever (until some stop signal)
 	}
 
 };