configurable TCP listener port + RequestHandler interface v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 07 Apr 2022 23:04:12 +0200
branchv_0
changeset 2 4b05b16b97e6
parent 1 23c516259cc5
child 3 1184f3de5533
configurable TCP listener port + RequestHandler interface
bash-completion.sh
src/CLIParser.h
src/Configuration.h
src/HTTPDHandler.h
src/HTTPServer.cpp
src/HTTPServer.h
src/relpipe-tr-httpd.cpp
--- a/bash-completion.sh	Thu Apr 07 21:06:37 2022 +0200
+++ b/bash-completion.sh	Thu Apr 07 23:04:12 2022 +0200
@@ -22,54 +22,10 @@
 	w2=${COMP_WORDS[COMP_CWORD-2]}
 	w3=${COMP_WORDS[COMP_CWORD-3]}
 
-	DATA_TYPE=(
-		"string"
-		"integer"
-		"boolean"
-	)
-
-	INPUT_ATTRIBUTE_POLICY=(
-		"append"
-		"prepend"
-		"auto"
-	)
-
-	# FIXME: user must type " and then press TAB otherwise the completion is broken due to the : colon
-	#
-	# can be fixed by global modification of environment variable:
-	#	COMP_WORDBREAKS=${COMP_WORDBREAKS//:}
-	# but it will affect other completions (where : is a separator)
-	#
-	# these functions should help:
-	#	_get_comp_words_by_ref -n : cur
-	#	__ltrim_colon_completions "$cur"
-	# but was not working (despite w0 renamed to cur)
-	XMLNS=(
-		"tag:globalcode.info,2018:relpipe"
-		"http://www.w3.org/1999/xhtml"
-		"http://www.w3.org/2000/svg"
-		"http://www.w3.org/2005/Atom"
-		"http://docbook.org/ns/docbook"
-	)
-
-
-	if   [[ "$w1" == "--relation"                      && "x$w0" == "x" ]];    then COMPREPLY=("'.*'")
-	elif [[ "$w1" == "--where"                         && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w1" == "--xml-attribute"                 && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w1" == "--output-attribute"              && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w2" == "--output-attribute"                               ]];    then COMPREPLY=($(compgen -W "${DATA_TYPE[*]}" -- "$w0"))
-	elif [[ "$w3" == "--output-attribute"              && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w1" == "--namespace"                     && "x$w0" == "x" ]];    then COMPREPLY=("''")
-	elif [[ "$w2" == "--namespace"                                      ]];    then COMPREPLY=($(compgen -W "${XMLNS[*]}" -- "$w0"))
-	elif [[ "$w1" == "--input-attributes"                               ]];    then COMPREPLY=($(compgen -W "${INPUT_ATTRIBUTE_POLICY[*]}" -- "$w0"))
+	if   [[ "$w1" == "--tcp-port"                      && "x$w0" == "x" ]];    then COMPREPLY=("8080")
 	else
 		OPTIONS=(
-			"--namespace"
-			"--relation"
-			"--where"
-			"--output-attribute"
-			"--input-attributes"
-			"--xml-attribute"
+			"--tcp-port"
 		)
 		COMPREPLY=($(compgen -W "${OPTIONS[*]}" -- "$w0"))
 	fi
--- a/src/CLIParser.h	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/CLIParser.h	Thu Apr 07 23:04:12 2022 +0200
@@ -36,76 +36,20 @@
 		else throw relpipe::cli::RelpipeCLIException(L"Missing CLI argument" + (i > 0 ? (L" after " + arguments[i - 1]) : L""), relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 	}
 
-	/**
-	 * TODO: use a common method
-	 */
-	bool parseBoolean(const relpipe::common::type::StringX& value) {
-		if (value == L"true") return true;
-		else if (value == L"false") return false;
-		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse boolean value: " + value + L" (expecting true or false)", relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
-	}
-
-	void addRelation(Configuration& c, RelationConfiguration& currentRelation) {
-		if (currentRelation.relation.size()) {
-			c.relationConfigurations.push_back(currentRelation);
-			currentRelation = RelationConfiguration();
-		}
-	}
-
-	/**
-	 * TODO: use a common method
-	 */
-	relpipe::writer::TypeId parseTypeId(const relpipe::common::type::StringX& value) {
-		using t = relpipe::writer::TypeId;
-		if (value == L"string") return t::STRING;
-		else if (value == L"integer") return t::INTEGER;
-		else if (value == L"boolean") return t::BOOLEAN;
-		else throw relpipe::cli::RelpipeCLIException(L"Unable to parse TypeId: " + value, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
-	}
-
 public:
 
-	static const relpipe::common::type::StringX OPTION_NAMESPACE;
-	static const relpipe::common::type::StringX OPTION_RELATION;
-	static const relpipe::common::type::StringX OPTION_WHERE;
-	static const relpipe::common::type::StringX OPTION_INPUT_ATTRIBUTES;
-	static const relpipe::common::type::StringX OPTION_OUTPUT_ATTRIBUTE;
-	static const relpipe::common::type::StringX OPTION_XML_ATTRIBUTE;
+	static const relpipe::common::type::StringX OPTION_TCP_PORT;
 
 	Configuration parse(const std::vector<relpipe::common::type::StringX>& arguments) {
 		Configuration c;
-		RelationConfiguration currentRelation;
 
 		for (int i = 0; i < arguments.size();) {
 			relpipe::common::type::StringX option = readNext(arguments, i);
 
-			if (option == OPTION_NAMESPACE) {
-				c.namespaceMappings.push_back(readNext(arguments, i));
-				c.namespaceMappings.push_back(readNext(arguments, i));
-			} else if (option == OPTION_RELATION) {
-				addRelation(c, currentRelation); // previous relation
-				currentRelation.relation = readNext(arguments, i);
-			} else if (option == OPTION_WHERE) {
-				currentRelation.where = readNext(arguments, i);
-			} else if (option == OPTION_XML_ATTRIBUTE) {
-				currentRelation.xmlAttributes.push_back(readNext(arguments, i));
-			} else if (option == OPTION_OUTPUT_ATTRIBUTE) {
-				AttributeRecipe attribute;
-				attribute.name = readNext(arguments, i);
-				attribute.type = parseTypeId(readNext(arguments, i));
-				attribute.httpd = readNext(arguments, i);
-				currentRelation.outputAttributes.push_back(attribute);
-			} else if (option == OPTION_INPUT_ATTRIBUTES) {
-				relpipe::common::type::StringX policyName = readNext(arguments, i);
-				InputAttributePolicy policy;
-				if (policyName == L"append") policy = InputAttributePolicy::Append;
-				else if (policyName == L"prepend") policy = InputAttributePolicy::Prepend;
-				else if (policyName == L"auto") policy = InputAttributePolicy::Auto;
-				else throw relpipe::cli::RelpipeCLIException(L"Unsupported input attributes policy: " + policyName, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
-				currentRelation.inputAttributePolicy = policy;
+			if (option == OPTION_TCP_PORT) {
+				c.tcpPort = stoi(readNext(arguments, i));
 			} else throw relpipe::cli::RelpipeCLIException(L"Unsupported CLI option: " + option, relpipe::cli::CLI::EXIT_CODE_BAD_CLI_ARGUMENTS);
 		}
-		addRelation(c, currentRelation); // last relation
 
 		return c;
 	}
@@ -114,12 +58,7 @@
 	}
 };
 
-const relpipe::common::type::StringX CLIParser::OPTION_NAMESPACE = L"--namespace";
-const relpipe::common::type::StringX CLIParser::OPTION_RELATION = L"--relation";
-const relpipe::common::type::StringX CLIParser::OPTION_WHERE = L"--where";
-const relpipe::common::type::StringX CLIParser::OPTION_INPUT_ATTRIBUTES = L"--input-attributes";
-const relpipe::common::type::StringX CLIParser::OPTION_OUTPUT_ATTRIBUTE = L"--output-attribute";
-const relpipe::common::type::StringX CLIParser::OPTION_XML_ATTRIBUTE = L"--xml-attribute";
+const relpipe::common::type::StringX CLIParser::OPTION_TCP_PORT = L"--tcp-port";
 
 }
 }
--- a/src/Configuration.h	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/Configuration.h	Thu Apr 07 23:04:12 2022 +0200
@@ -25,40 +25,9 @@
 namespace tr {
 namespace httpd {
 
-enum class InputAttributePolicy {
-	Prepend,
-	Append,
-	Auto
-};
-
-class AttributeRecipe {
-public:
-
-	virtual ~AttributeRecipe() {
-	}
-
-	relpipe::common::type::StringX name;
-	relpipe::writer::TypeId type;
-	relpipe::common::type::StringX httpd;
-};
-
-class RelationConfiguration {
-public:
-
-	virtual ~RelationConfiguration() {
-	}
-
-	relpipe::common::type::StringX relation;
-	relpipe::common::type::StringX where;
-	InputAttributePolicy inputAttributePolicy = InputAttributePolicy::Auto;
-	std::vector<AttributeRecipe> outputAttributes;
-	std::vector<relpipe::common::type::StringX> xmlAttributes;
-};
-
 class Configuration {
 public:
-	std::vector<RelationConfiguration> relationConfigurations;
-	std::vector<relpipe::common::type::StringX> namespaceMappings;
+	uint16_t tcpPort = 8080;
 
 	virtual ~Configuration() {
 	}
--- a/src/HTTPDHandler.h	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/HTTPDHandler.h	Thu Apr 07 23:04:12 2022 +0200
@@ -45,7 +45,6 @@
 	shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
 	Configuration configuration;
 	std::shared_ptr<HTTPServer> httpServer;
-	RelationConfiguration* currentRelationConfiguration = nullptr;
 	std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata;
 	std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata;
 	size_t currentAttributeIndex = 0;
--- a/src/HTTPServer.cpp	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/HTTPServer.cpp	Thu Apr 07 23:04:12 2022 +0200
@@ -27,9 +27,14 @@
 class HTTPServer::HTTPServerImpl {
 public:
 	MHD_Daemon* mhd = nullptr;
+	std::shared_ptr<RequestHandler> requestHandler;
 };
 
-HTTPServer* HTTPServer::create() {
+void HTTPServer::setRequestHandler(std::shared_ptr<RequestHandler> handler) {
+	impl->requestHandler = handler;
+}
+
+HTTPServer* HTTPServer::create(HTTPServer::Options options) {
 	HTTPServer::HTTPServerImpl* impl = new HTTPServer::HTTPServerImpl();
 
 	void* acceptCallbackData = nullptr;
@@ -48,7 +53,7 @@
 
 
 	impl->mhd = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD,
-			8080,
+			options.tcpPort,
 			acceptCallback, acceptCallbackData,
 			accessCallback, accessCallbackData,
 			MHD_OPTION_THREAD_POOL_SIZE, 10,
--- a/src/HTTPServer.h	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/HTTPServer.h	Thu Apr 07 23:04:12 2022 +0200
@@ -17,6 +17,11 @@
 
 #pragma once
 
+#include <vector>
+#include <string>
+#include <ctype.h>
+#include <memory>
+
 namespace relpipe {
 namespace tr {
 namespace httpd {
@@ -31,11 +36,47 @@
 
 
 public:
+
+	class Header {
+	public:
+		std::string name;
+		std::string value;
+	};
+
+	class Request {
+	public:
+		std::vector<Header> header;
+		std::string host;
+		std::string method;
+		std::string url;
+		std::string body;
+	};
+
+	class Response {
+	public:
+		std::vector<Header> header;
+		uint16_t code;
+		std::string body;
+	};
+
+	class RequestHandler {
+	public:
+
+		virtual const Response handle(const Request& request);
+	};
+
+	class Options {
+	public:
+		uint16_t tcpPort = 8080;
+	};
+
 	virtual ~HTTPServer();
 	HTTPServer(const HTTPServer&) = delete;
 	HTTPServer& operator=(const HTTPServer&) = delete;
 
-	static HTTPServer* create();
+	void setRequestHandler(std::shared_ptr<RequestHandler> handler);
+
+	static HTTPServer* create(Options options);
 
 };
 
--- a/src/relpipe-tr-httpd.cpp	Thu Apr 07 21:06:37 2022 +0200
+++ b/src/relpipe-tr-httpd.cpp	Thu Apr 07 23:04:12 2022 +0200
@@ -51,7 +51,9 @@
 		Configuration configuration = cliParser.parse(cli.arguments());
 		std::shared_ptr<RelationalWriter> writer(relpipe::writer::Factory::create(std::cout));
 		std::shared_ptr<RelationalReader> reader(relpipe::reader::Factory::create(std::cin));
-		std::shared_ptr<HTTPServer> httpServer(HTTPServer::create());
+		HTTPServer::Options serverOptions;
+		serverOptions.tcpPort = configuration.tcpPort;
+		std::shared_ptr<HTTPServer> httpServer(HTTPServer::create(serverOptions));
 		HttpdHandler handler(writer, configuration, httpServer);
 		reader->addHandler(&handler);
 		reader->process();