generate some output relations v_0
authorFrantišek Kučera <franta-hg@frantovo.cz>
Sat, 16 Apr 2022 02:43:50 +0200
branchv_0
changeset 5 121981e6bd54
parent 4 37a86904145c
child 6 b04bde9083d4
generate some output relations
nbproject/configurations.xml
src/CMakeLists.txt
src/HTTPDHandler.h
src/HTTPServer.cpp
--- a/nbproject/configurations.xml	Sat Apr 09 17:50:46 2022 +0200
+++ b/nbproject/configurations.xml	Sat Apr 16 02:43:50 2022 +0200
@@ -86,6 +86,7 @@
               <pElem>../relpipe-lib-common.cpp/include</pElem>
               <pElem>../relpipe-lib-cli.cpp/include</pElem>
               <pElem>/usr/include/p11-kit-1</pElem>
+              <pElem>/usr/include/uuid</pElem>
               <pElem>build/Debug/src</pElem>
             </incDir>
           </ccTool>
@@ -97,8 +98,8 @@
         </preBuild>
       </makefileType>
       <item path="src/HTTPServer.cpp" ex="false" tool="1" flavor2="0">
-      </item>
-      <item path="src/HTTPServer.h" ex="false" tool="3" flavor2="0">
+        <ccTool flags="0">
+        </ccTool>
       </item>
       <item path="src/relpipe-tr-httpd.cpp" ex="false" tool="1" flavor2="0">
         <ccTool flags="0">
--- a/src/CMakeLists.txt	Sat Apr 09 17:50:46 2022 +0200
+++ b/src/CMakeLists.txt	Sat Apr 16 02:43:50 2022 +0200
@@ -17,7 +17,7 @@
 
 # Relpipe libraries:
 INCLUDE(FindPkgConfig)
-pkg_check_modules (RELPIPE_LIBS relpipe-lib-reader.cpp relpipe-lib-writer.cpp relpipe-lib-cli.cpp libmicrohttpd)
+pkg_check_modules (RELPIPE_LIBS relpipe-lib-reader.cpp relpipe-lib-writer.cpp relpipe-lib-cli.cpp libmicrohttpd uuid)
 include_directories(${RELPIPE_LIBS_INCLUDE_DIRS})
 link_directories(${RELPIPE_LIBS_LIBRARY_DIRS})
 
--- a/src/HTTPDHandler.h	Sat Apr 09 17:50:46 2022 +0200
+++ b/src/HTTPDHandler.h	Sat Apr 16 02:43:50 2022 +0200
@@ -24,6 +24,9 @@
 #include <stdexcept>
 #include <mutex>
 #include <shared_mutex>
+#include <iomanip>
+
+#include <uuid/uuid.h>
 
 #include <relpipe/common/type/typedefs.h>
 #include <relpipe/reader/TypeId.h>
@@ -43,8 +46,9 @@
 
 class HttpdHandler : public relpipe::reader::handlers::RelationalReaderStringHandler {
 private:
-	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML is in UTF-8
+	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encoding than UTF-8
 	shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
+	std::mutex relationalWriterMutex;
 	Configuration configuration;
 	std::shared_ptr<HTTPServer> httpServer;
 	relpipe::common::type::StringX currentRelationName;
@@ -100,12 +104,108 @@
 
 	class RequestHandler : public HTTPServer::RequestHandler {
 	private:
+		shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
+		std::mutex* relationalWriterMutex;
 		std::vector<ResponseTemplate>* responseTemplates;
 		std::vector<GlobalHeaderTemplate>* headerTemplates;
 		std::mutex* templatesMutex;
+
+		void writeExchange(const HTTPServer::Request& request, const HTTPServer::Response& response) {
+			std::wstring_convert < codecvt_utf8<wchar_t>> convertor; // TODO: support also other encoding than UTF-8
+			std::lock_guard<std::mutex> lock(*relationalWriterMutex);
+			
+			auto exchangeId = convertor.from_bytes(generateExchangeId());
+
+
+			// TODO: multiple modes:
+			// a) interleaved (current)
+			// b) write exchanges immediatelly + cache headers and flush them at the end
+
+			// TODO: data types
+			// TODO: support multiple encodings
+
+			relationalWriter->startRelation(L"exchange",{
+				{L"id", relpipe::writer::TypeId::STRING},
+				{L"url", relpipe::writer::TypeId::STRING},
+				{L"method", relpipe::writer::TypeId::STRING},
+				{L"request_text", relpipe::writer::TypeId::STRING},
+				{L"request_data", relpipe::writer::TypeId::STRING},
+				{L"response_text", relpipe::writer::TypeId::STRING},
+				{L"response_data", relpipe::writer::TypeId::STRING},
+				{L"response_code", relpipe::writer::TypeId::INTEGER},
+			}, true);
+
+			relationalWriter->writeAttribute(exchangeId);
+			relationalWriter->writeAttribute(convertor.from_bytes(request.url));
+			relationalWriter->writeAttribute(convertor.from_bytes(request.method));
+			relationalWriter->writeAttribute(bodyToText(convertor, request.body));
+			relationalWriter->writeAttribute(bodyToHex(convertor, request.body));
+			relationalWriter->writeAttribute(bodyToText(convertor, response.body));
+			relationalWriter->writeAttribute(bodyToHex(convertor, response.body));
+			relationalWriter->writeAttribute(std::to_wstring(response.code));
+
+
+			relationalWriter->startRelation(L"header",{
+				{L"exchange", relpipe::writer::TypeId::STRING},
+				{L"url", relpipe::writer::TypeId::STRING},
+				{L"direction", relpipe::writer::TypeId::STRING},
+				{L"name", relpipe::writer::TypeId::STRING},
+				{L"value", relpipe::writer::TypeId::STRING},
+			}, true);
+
+			for (const HTTPServer::Header& h : request.header) {
+				relationalWriter->writeAttribute(exchangeId);
+				relationalWriter->writeAttribute(convertor.from_bytes(request.url));
+				relationalWriter->writeAttribute(L"request");
+				relationalWriter->writeAttribute(convertor.from_bytes(h.name));
+				relationalWriter->writeAttribute(convertor.from_bytes(h.value));
+			}
+
+			for (const HTTPServer::Header& h : response.header) {
+				relationalWriter->writeAttribute(exchangeId);
+				relationalWriter->writeAttribute(convertor.from_bytes(request.url));
+				relationalWriter->writeAttribute(L"response");
+				relationalWriter->writeAttribute(convertor.from_bytes(h.name));
+				relationalWriter->writeAttribute(convertor.from_bytes(h.value));
+			}
+
+
+
+		}
+
+		relpipe::common::type::StringX bodyToText(std::wstring_convert < codecvt_utf8<wchar_t>>&convertor, const std::string& body, bool* validEncoding = nullptr) {
+			try {
+				if (validEncoding) *validEncoding = true;
+				// TODO: use encoding from the HTTP response headers instead of the constant one?
+				return convertor.from_bytes(body);
+			} catch (...) {
+				if (validEncoding) *validEncoding = false;
+				std::stringstream filtered;
+				for (char ch : body) filtered << (ch >= ' ' && ch < 127 ? ch : '.');
+				return convertor.from_bytes(filtered.str());
+			}
+		}
+
+		relpipe::common::type::StringX bodyToHex(std::wstring_convert < codecvt_utf8<wchar_t>>&convertor, const std::string& body) {
+			std::stringstream hex;
+			hex << std::hex << std::setfill('0') << std::hex;
+			for (size_t i = 0, size = body.size(); i < size; i++) hex << std::setw(2) << (0xff & body[i]);
+			return convertor.from_bytes(hex.str());
+		}
+
+		std::string generateExchangeId() {
+			char buffer[37];
+			uuid_t uuid;
+			uuid_generate_random(uuid);
+			// uuid_generate_time(uuid);
+			uuid_unparse_lower(uuid, buffer);
+			return buffer;
+		}
+
 	public:
 
-		RequestHandler(std::mutex* templatesMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) {
+		RequestHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, std::mutex* relationalWriterMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates, std::mutex* templatesMutex) :
+		relationalWriter(relationalWriter), relationalWriterMutex(relationalWriterMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates), templatesMutex(templatesMutex) {
 		}
 
 		virtual ~RequestHandler() = default;
@@ -113,6 +213,9 @@
 		const HTTPServer::Response handle(const HTTPServer::Request& request) override {
 			HTTPServer::Response response;
 
+			response.code = 404;
+			response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>";
+
 			std::lock_guard<std::mutex> lock(*templatesMutex);
 			for (ResponseTemplate t : *responseTemplates) {
 				if (t.matches(request.method, request.url)) {
@@ -121,18 +224,17 @@
 					// 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;
+					break;
 				}
 			}
 
-			response.code = 404;
-			response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>";
+			writeExchange(request, response);
 			return response;
 		}
 
 	};
 
-	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(&templatesMutex, &responseTemplates, &headerTemplates);
+	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(relationalWriter, &relationalWriterMutex, &responseTemplates, &headerTemplates, &templatesMutex);
 
 	relpipe::common::type::StringX getHeaderAttributePrefix() {
 		// might be configurable - parametrized
--- a/src/HTTPServer.cpp	Sat Apr 09 17:50:46 2022 +0200
+++ b/src/HTTPServer.cpp	Sat Apr 16 02:43:50 2022 +0200
@@ -56,6 +56,10 @@
 			HTTPServer::Request request;
 			request.method = method;
 			request.url = url;
+
+			// TODO: return also client IP address etc.
+			// const MHD_ConnectionInfo* connetionInfo = MHD_get_connection_info(connection, MHD_ConnectionInfoType::MHD_CONNECTION_INFO_CLIENT_ADDRESS);
+
 			// FIXME: request.body = ...
 			// FIXME: multiple calls for one request
 			const HTTPServer::Response response = impl->requestHandler->handle(request);