src/HTTPDHandler.h
branchv_0
changeset 5 121981e6bd54
parent 4 37a86904145c
child 6 b04bde9083d4
equal deleted inserted replaced
4:37a86904145c 5:121981e6bd54
    22 #include <codecvt>
    22 #include <codecvt>
    23 #include <regex>
    23 #include <regex>
    24 #include <stdexcept>
    24 #include <stdexcept>
    25 #include <mutex>
    25 #include <mutex>
    26 #include <shared_mutex>
    26 #include <shared_mutex>
       
    27 #include <iomanip>
       
    28 
       
    29 #include <uuid/uuid.h>
    27 
    30 
    28 #include <relpipe/common/type/typedefs.h>
    31 #include <relpipe/common/type/typedefs.h>
    29 #include <relpipe/reader/TypeId.h>
    32 #include <relpipe/reader/TypeId.h>
    30 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
    33 #include <relpipe/reader/handlers/RelationalReaderStringHandler.h>
    31 #include <relpipe/reader/handlers/AttributeMetadata.h>
    34 #include <relpipe/reader/handlers/AttributeMetadata.h>
    41 namespace tr {
    44 namespace tr {
    42 namespace httpd {
    45 namespace httpd {
    43 
    46 
    44 class HttpdHandler : public relpipe::reader::handlers::RelationalReaderStringHandler {
    47 class HttpdHandler : public relpipe::reader::handlers::RelationalReaderStringHandler {
    45 private:
    48 private:
    46 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML is in UTF-8
    49 	std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // TODO: support also other encoding than UTF-8
    47 	shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
    50 	shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
       
    51 	std::mutex relationalWriterMutex;
    48 	Configuration configuration;
    52 	Configuration configuration;
    49 	std::shared_ptr<HTTPServer> httpServer;
    53 	std::shared_ptr<HTTPServer> httpServer;
    50 	relpipe::common::type::StringX currentRelationName;
    54 	relpipe::common::type::StringX currentRelationName;
    51 	std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata;
    55 	std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata;
    52 	std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata;
    56 	std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata;
    98 
   102 
    99 	std::mutex templatesMutex; // TODO: read-write lock (for responseTemplates and headerTemplates)
   103 	std::mutex templatesMutex; // TODO: read-write lock (for responseTemplates and headerTemplates)
   100 
   104 
   101 	class RequestHandler : public HTTPServer::RequestHandler {
   105 	class RequestHandler : public HTTPServer::RequestHandler {
   102 	private:
   106 	private:
       
   107 		shared_ptr<relpipe::writer::RelationalWriter> relationalWriter;
       
   108 		std::mutex* relationalWriterMutex;
   103 		std::vector<ResponseTemplate>* responseTemplates;
   109 		std::vector<ResponseTemplate>* responseTemplates;
   104 		std::vector<GlobalHeaderTemplate>* headerTemplates;
   110 		std::vector<GlobalHeaderTemplate>* headerTemplates;
   105 		std::mutex* templatesMutex;
   111 		std::mutex* templatesMutex;
       
   112 
       
   113 		void writeExchange(const HTTPServer::Request& request, const HTTPServer::Response& response) {
       
   114 			std::wstring_convert < codecvt_utf8<wchar_t>> convertor; // TODO: support also other encoding than UTF-8
       
   115 			std::lock_guard<std::mutex> lock(*relationalWriterMutex);
       
   116 			
       
   117 			auto exchangeId = convertor.from_bytes(generateExchangeId());
       
   118 
       
   119 
       
   120 			// TODO: multiple modes:
       
   121 			// a) interleaved (current)
       
   122 			// b) write exchanges immediatelly + cache headers and flush them at the end
       
   123 
       
   124 			// TODO: data types
       
   125 			// TODO: support multiple encodings
       
   126 
       
   127 			relationalWriter->startRelation(L"exchange",{
       
   128 				{L"id", relpipe::writer::TypeId::STRING},
       
   129 				{L"url", relpipe::writer::TypeId::STRING},
       
   130 				{L"method", relpipe::writer::TypeId::STRING},
       
   131 				{L"request_text", relpipe::writer::TypeId::STRING},
       
   132 				{L"request_data", relpipe::writer::TypeId::STRING},
       
   133 				{L"response_text", relpipe::writer::TypeId::STRING},
       
   134 				{L"response_data", relpipe::writer::TypeId::STRING},
       
   135 				{L"response_code", relpipe::writer::TypeId::INTEGER},
       
   136 			}, true);
       
   137 
       
   138 			relationalWriter->writeAttribute(exchangeId);
       
   139 			relationalWriter->writeAttribute(convertor.from_bytes(request.url));
       
   140 			relationalWriter->writeAttribute(convertor.from_bytes(request.method));
       
   141 			relationalWriter->writeAttribute(bodyToText(convertor, request.body));
       
   142 			relationalWriter->writeAttribute(bodyToHex(convertor, request.body));
       
   143 			relationalWriter->writeAttribute(bodyToText(convertor, response.body));
       
   144 			relationalWriter->writeAttribute(bodyToHex(convertor, response.body));
       
   145 			relationalWriter->writeAttribute(std::to_wstring(response.code));
       
   146 
       
   147 
       
   148 			relationalWriter->startRelation(L"header",{
       
   149 				{L"exchange", relpipe::writer::TypeId::STRING},
       
   150 				{L"url", relpipe::writer::TypeId::STRING},
       
   151 				{L"direction", relpipe::writer::TypeId::STRING},
       
   152 				{L"name", relpipe::writer::TypeId::STRING},
       
   153 				{L"value", relpipe::writer::TypeId::STRING},
       
   154 			}, true);
       
   155 
       
   156 			for (const HTTPServer::Header& h : request.header) {
       
   157 				relationalWriter->writeAttribute(exchangeId);
       
   158 				relationalWriter->writeAttribute(convertor.from_bytes(request.url));
       
   159 				relationalWriter->writeAttribute(L"request");
       
   160 				relationalWriter->writeAttribute(convertor.from_bytes(h.name));
       
   161 				relationalWriter->writeAttribute(convertor.from_bytes(h.value));
       
   162 			}
       
   163 
       
   164 			for (const HTTPServer::Header& h : response.header) {
       
   165 				relationalWriter->writeAttribute(exchangeId);
       
   166 				relationalWriter->writeAttribute(convertor.from_bytes(request.url));
       
   167 				relationalWriter->writeAttribute(L"response");
       
   168 				relationalWriter->writeAttribute(convertor.from_bytes(h.name));
       
   169 				relationalWriter->writeAttribute(convertor.from_bytes(h.value));
       
   170 			}
       
   171 
       
   172 
       
   173 
       
   174 		}
       
   175 
       
   176 		relpipe::common::type::StringX bodyToText(std::wstring_convert < codecvt_utf8<wchar_t>>&convertor, const std::string& body, bool* validEncoding = nullptr) {
       
   177 			try {
       
   178 				if (validEncoding) *validEncoding = true;
       
   179 				// TODO: use encoding from the HTTP response headers instead of the constant one?
       
   180 				return convertor.from_bytes(body);
       
   181 			} catch (...) {
       
   182 				if (validEncoding) *validEncoding = false;
       
   183 				std::stringstream filtered;
       
   184 				for (char ch : body) filtered << (ch >= ' ' && ch < 127 ? ch : '.');
       
   185 				return convertor.from_bytes(filtered.str());
       
   186 			}
       
   187 		}
       
   188 
       
   189 		relpipe::common::type::StringX bodyToHex(std::wstring_convert < codecvt_utf8<wchar_t>>&convertor, const std::string& body) {
       
   190 			std::stringstream hex;
       
   191 			hex << std::hex << std::setfill('0') << std::hex;
       
   192 			for (size_t i = 0, size = body.size(); i < size; i++) hex << std::setw(2) << (0xff & body[i]);
       
   193 			return convertor.from_bytes(hex.str());
       
   194 		}
       
   195 
       
   196 		std::string generateExchangeId() {
       
   197 			char buffer[37];
       
   198 			uuid_t uuid;
       
   199 			uuid_generate_random(uuid);
       
   200 			// uuid_generate_time(uuid);
       
   201 			uuid_unparse_lower(uuid, buffer);
       
   202 			return buffer;
       
   203 		}
       
   204 
   106 	public:
   205 	public:
   107 
   206 
   108 		RequestHandler(std::mutex* templatesMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) {
   207 		RequestHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, std::mutex* relationalWriterMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates, std::mutex* templatesMutex) :
       
   208 		relationalWriter(relationalWriter), relationalWriterMutex(relationalWriterMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates), templatesMutex(templatesMutex) {
   109 		}
   209 		}
   110 
   210 
   111 		virtual ~RequestHandler() = default;
   211 		virtual ~RequestHandler() = default;
   112 
   212 
   113 		const HTTPServer::Response handle(const HTTPServer::Request& request) override {
   213 		const HTTPServer::Response handle(const HTTPServer::Request& request) override {
   114 			HTTPServer::Response response;
   214 			HTTPServer::Response response;
       
   215 
       
   216 			response.code = 404;
       
   217 			response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>";
   115 
   218 
   116 			std::lock_guard<std::mutex> lock(*templatesMutex);
   219 			std::lock_guard<std::mutex> lock(*templatesMutex);
   117 			for (ResponseTemplate t : *responseTemplates) {
   220 			for (ResponseTemplate t : *responseTemplates) {
   118 				if (t.matches(request.method, request.url)) {
   221 				if (t.matches(request.method, request.url)) {
   119 					response.code = t.code;
   222 					response.code = t.code;
   120 					response.body = t.body;
   223 					response.body = t.body;
   121 					// TODO: replace global header values with request-specific ones instead of appending?
   224 					// TODO: replace global header values with request-specific ones instead of appending?
   122 					for (const GlobalHeaderTemplate& h : *headerTemplates) if (h.matches(request.method, request.url)) response.header.push_back(HTTPServer::Header(h.name, h.value));
   225 					for (const GlobalHeaderTemplate& h : *headerTemplates) if (h.matches(request.method, request.url)) response.header.push_back(HTTPServer::Header(h.name, h.value));
   123 					for (const HeaderTemplate& h : t.headers) response.header.push_back(HTTPServer::Header(h.name, h.value));
   226 					for (const HeaderTemplate& h : t.headers) response.header.push_back(HTTPServer::Header(h.name, h.value));
   124 					return response;
   227 					break;
   125 				}
   228 				}
   126 			}
   229 			}
   127 
   230 
   128 			response.code = 404;
   231 			writeExchange(request, response);
   129 			response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>";
       
   130 			return response;
   232 			return response;
   131 		}
   233 		}
   132 
   234 
   133 	};
   235 	};
   134 
   236 
   135 	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(&templatesMutex, &responseTemplates, &headerTemplates);
   237 	std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(relationalWriter, &relationalWriterMutex, &responseTemplates, &headerTemplates, &templatesMutex);
   136 
   238 
   137 	relpipe::common::type::StringX getHeaderAttributePrefix() {
   239 	relpipe::common::type::StringX getHeaderAttributePrefix() {
   138 		// might be configurable - parametrized
   240 		// might be configurable - parametrized
   139 		return L"header.";
   241 		return L"header.";
   140 	}
   242 	}