--- 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