diff -r 1184f3de5533 -r 37a86904145c src/HTTPDHandler.h --- a/src/HTTPDHandler.h Fri Apr 08 22:38:45 2022 +0200 +++ b/src/HTTPDHandler.h Sat Apr 09 17:50:46 2022 +0200 @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -45,27 +47,146 @@ shared_ptr relationalWriter; Configuration configuration; std::shared_ptr httpServer; + relpipe::common::type::StringX currentRelationName; std::vector currentReaderMetadata; std::vector currentWriterMetadata; size_t currentAttributeIndex = 0; size_t currentRecordNumber = 1; + class RequestMatcher { + public: + std::regex method = std::regex(".*"); + std::regex url = std::regex(".*"); + + virtual ~RequestMatcher() = default; + + bool matches(const std::string& method, const std::string& url) const { + bool result = true; + result &= std::regex_match(method, this->method); + result &= std::regex_match(url, this->url); + return result; + } + }; + + class HeaderTemplate { + public: + std::string name; + std::string value; + + HeaderTemplate() { + } + + HeaderTemplate(std::string name, std::string value) : name(name), value(value) { + } + + virtual ~HeaderTemplate() = default; + }; + + class GlobalHeaderTemplate : public HeaderTemplate, public RequestMatcher { + } headerTemplate; + + std::vector headerTemplates; + + class ResponseTemplate : public RequestMatcher { + public: + std::string body; + uint16_t code = 200; + std::vector headers; + } responseTemplate; + + std::vector responseTemplates; + + std::mutex templatesMutex; // TODO: read-write lock (for responseTemplates and headerTemplates) + class RequestHandler : public HTTPServer::RequestHandler { + private: + std::vector* responseTemplates; + std::vector* headerTemplates; + std::mutex* templatesMutex; public: + RequestHandler(std::mutex* templatesMutex, std::vector* responseTemplates, std::vector* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) { + } + + virtual ~RequestHandler() = default; + const HTTPServer::Response handle(const HTTPServer::Request& request) override { HTTPServer::Response response; - // TODO: return real responses - response.code = 200; - response.body = "

greetings and salutations

"; + std::lock_guard lock(*templatesMutex); + for (ResponseTemplate t : *responseTemplates) { + if (t.matches(request.method, request.url)) { + response.code = t.code; + response.body = t.body; + // 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; + } + } + response.code = 404; + response.body = "

HTTP 404: Not Found

(no response template matched)

"; return response; } }; - std::shared_ptr requestHandler = std::make_shared(); + std::shared_ptr requestHandler = std::make_shared(&templatesMutex, &responseTemplates, &headerTemplates); + + relpipe::common::type::StringX getHeaderAttributePrefix() { + // might be configurable - parametrized + return L"header."; + } + + bool isHeaderAttribute(const relpipe::common::type::StringX& attributeName) { + return attributeName.rfind(getHeaderAttributePrefix(), 0) == 0; + } + + relpipe::common::type::StringX fetchHeaderName(const relpipe::common::type::StringX& attributeName) { + // TODO: recognize several modes: header.set.*, header.add.*, header.remove.* + return attributeName.substr(getHeaderAttributePrefix().size()); + } + + void headerTemplateAttribute(const relpipe::common::type::StringX& value) { + auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName(); + + if (attributeName == L"url") headerTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); + else if (attributeName == L"method") headerTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); + else if (attributeName == L"name") headerTemplate.name = convertor.to_bytes(value); + else if (attributeName == L"value") headerTemplate.value = convertor.to_bytes(value); // TODO: header encoding? + else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value)); + + currentAttributeIndex++; + + if (currentAttributeIndex % currentReaderMetadata.size() == 0) { + std::lock_guard lock(templatesMutex); + currentAttributeIndex = 0; + headerTemplates.push_back(headerTemplate); + headerTemplate = GlobalHeaderTemplate(); + } + } + + void responseTemplateAttribute(const relpipe::common::type::StringX& value) { + auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName(); + + if (attributeName == L"url") responseTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); + else if (attributeName == L"method") responseTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); + else if (attributeName == L"code") responseTemplate.code = std::stoi(value); + else if (attributeName == L"text") responseTemplate.body = convertor.to_bytes(value); + else if (attributeName == L"data") responseTemplate.body = "TODO: read binary data: " + convertor.to_bytes(value); // TODO: read hex/binary request body + else if (isHeaderAttribute(attributeName)) responseTemplate.headers.push_back(HeaderTemplate(convertor.to_bytes(fetchHeaderName(attributeName)), convertor.to_bytes(value))); // TODO: header encoding? + else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value)); + + currentAttributeIndex++; + + if (currentAttributeIndex % currentReaderMetadata.size() == 0) { + std::lock_guard lock(templatesMutex); + currentAttributeIndex = 0; + responseTemplates.push_back(responseTemplate); + responseTemplate = ResponseTemplate(); + } + } public: @@ -77,15 +198,18 @@ } void startRelation(relpipe::common::type::StringX name, std::vector attributes) override { - + currentRelationName = name; + currentReaderMetadata = attributes; } void attribute(const relpipe::common::type::StringX& value) override { - + if (currentRelationName == L"header_template") headerTemplateAttribute(value); + else if (currentRelationName == L"response_template") responseTemplateAttribute(value); + else throw std::invalid_argument("Unsupported relation: " + convertor.to_bytes(currentRelationName)); } void endOfPipe() { - + sleep(60); // FIXME: run for configured interval or number of requests or forever (until some stop signal) } };