diff -r 37a86904145c -r 121981e6bd54 src/HTTPDHandler.h --- 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 #include #include +#include + +#include #include #include @@ -43,8 +46,9 @@ class HttpdHandler : public relpipe::reader::handlers::RelationalReaderStringHandler { private: - std::wstring_convert> convertor; // XML is in UTF-8 + std::wstring_convert> convertor; // TODO: support also other encoding than UTF-8 shared_ptr relationalWriter; + std::mutex relationalWriterMutex; Configuration configuration; std::shared_ptr httpServer; relpipe::common::type::StringX currentRelationName; @@ -100,12 +104,108 @@ class RequestHandler : public HTTPServer::RequestHandler { private: + shared_ptr relationalWriter; + std::mutex* relationalWriterMutex; std::vector* responseTemplates; std::vector* headerTemplates; std::mutex* templatesMutex; + + void writeExchange(const HTTPServer::Request& request, const HTTPServer::Response& response) { + std::wstring_convert < codecvt_utf8> convertor; // TODO: support also other encoding than UTF-8 + std::lock_guard 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>&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>&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* responseTemplates, std::vector* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) { + RequestHandler(shared_ptr relationalWriter, std::mutex* relationalWriterMutex, std::vector* responseTemplates, std::vector* 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 = "

HTTP 404: Not Found

(no response template matched)

"; + std::lock_guard 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 = "

HTTP 404: Not Found

(no response template matched)

"; + writeExchange(request, response); return response; } }; - std::shared_ptr requestHandler = std::make_shared(&templatesMutex, &responseTemplates, &headerTemplates); + std::shared_ptr requestHandler = std::make_shared(relationalWriter, &relationalWriterMutex, &responseTemplates, &headerTemplates, &templatesMutex); relpipe::common::type::StringX getHeaderAttributePrefix() { // might be configurable - parametrized