43 private: |
45 private: |
44 std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML is in UTF-8 |
46 std::wstring_convert<codecvt_utf8<wchar_t>> convertor; // XML is in UTF-8 |
45 shared_ptr<relpipe::writer::RelationalWriter> relationalWriter; |
47 shared_ptr<relpipe::writer::RelationalWriter> relationalWriter; |
46 Configuration configuration; |
48 Configuration configuration; |
47 std::shared_ptr<HTTPServer> httpServer; |
49 std::shared_ptr<HTTPServer> httpServer; |
|
50 relpipe::common::type::StringX currentRelationName; |
48 std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata; |
51 std::vector<relpipe::reader::handlers::AttributeMetadata> currentReaderMetadata; |
49 std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata; |
52 std::vector<relpipe::writer::AttributeMetadata> currentWriterMetadata; |
50 size_t currentAttributeIndex = 0; |
53 size_t currentAttributeIndex = 0; |
51 size_t currentRecordNumber = 1; |
54 size_t currentRecordNumber = 1; |
52 |
55 |
|
56 class RequestMatcher { |
|
57 public: |
|
58 std::regex method = std::regex(".*"); |
|
59 std::regex url = std::regex(".*"); |
|
60 |
|
61 virtual ~RequestMatcher() = default; |
|
62 |
|
63 bool matches(const std::string& method, const std::string& url) const { |
|
64 bool result = true; |
|
65 result &= std::regex_match(method, this->method); |
|
66 result &= std::regex_match(url, this->url); |
|
67 return result; |
|
68 } |
|
69 }; |
|
70 |
|
71 class HeaderTemplate { |
|
72 public: |
|
73 std::string name; |
|
74 std::string value; |
|
75 |
|
76 HeaderTemplate() { |
|
77 } |
|
78 |
|
79 HeaderTemplate(std::string name, std::string value) : name(name), value(value) { |
|
80 } |
|
81 |
|
82 virtual ~HeaderTemplate() = default; |
|
83 }; |
|
84 |
|
85 class GlobalHeaderTemplate : public HeaderTemplate, public RequestMatcher { |
|
86 } headerTemplate; |
|
87 |
|
88 std::vector<GlobalHeaderTemplate> headerTemplates; |
|
89 |
|
90 class ResponseTemplate : public RequestMatcher { |
|
91 public: |
|
92 std::string body; |
|
93 uint16_t code = 200; |
|
94 std::vector<HeaderTemplate> headers; |
|
95 } responseTemplate; |
|
96 |
|
97 std::vector<ResponseTemplate> responseTemplates; |
|
98 |
|
99 std::mutex templatesMutex; // TODO: read-write lock (for responseTemplates and headerTemplates) |
|
100 |
53 class RequestHandler : public HTTPServer::RequestHandler { |
101 class RequestHandler : public HTTPServer::RequestHandler { |
54 public: |
102 private: |
|
103 std::vector<ResponseTemplate>* responseTemplates; |
|
104 std::vector<GlobalHeaderTemplate>* headerTemplates; |
|
105 std::mutex* templatesMutex; |
|
106 public: |
|
107 |
|
108 RequestHandler(std::mutex* templatesMutex, std::vector<ResponseTemplate>* responseTemplates, std::vector<GlobalHeaderTemplate>* headerTemplates) : templatesMutex(templatesMutex), responseTemplates(responseTemplates), headerTemplates(headerTemplates) { |
|
109 } |
|
110 |
|
111 virtual ~RequestHandler() = default; |
55 |
112 |
56 const HTTPServer::Response handle(const HTTPServer::Request& request) override { |
113 const HTTPServer::Response handle(const HTTPServer::Request& request) override { |
57 HTTPServer::Response response; |
114 HTTPServer::Response response; |
58 |
115 |
59 // TODO: return real responses |
116 std::lock_guard<std::mutex> lock(*templatesMutex); |
60 response.code = 200; |
117 for (ResponseTemplate t : *responseTemplates) { |
61 response.body = "<h1>greetings and salutations</h1>"; |
118 if (t.matches(request.method, request.url)) { |
62 |
119 response.code = t.code; |
|
120 response.body = t.body; |
|
121 // 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)); |
|
123 for (const HeaderTemplate& h : t.headers) response.header.push_back(HTTPServer::Header(h.name, h.value)); |
|
124 return response; |
|
125 } |
|
126 } |
|
127 |
|
128 response.code = 404; |
|
129 response.body = "<h1>HTTP 404: Not Found</h1><p>(no response template matched)</p>"; |
63 return response; |
130 return response; |
64 } |
131 } |
65 |
132 |
66 }; |
133 }; |
67 |
134 |
68 std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(); |
135 std::shared_ptr<RequestHandler> requestHandler = std::make_shared<RequestHandler>(&templatesMutex, &responseTemplates, &headerTemplates); |
|
136 |
|
137 relpipe::common::type::StringX getHeaderAttributePrefix() { |
|
138 // might be configurable - parametrized |
|
139 return L"header."; |
|
140 } |
|
141 |
|
142 bool isHeaderAttribute(const relpipe::common::type::StringX& attributeName) { |
|
143 return attributeName.rfind(getHeaderAttributePrefix(), 0) == 0; |
|
144 } |
|
145 |
|
146 relpipe::common::type::StringX fetchHeaderName(const relpipe::common::type::StringX& attributeName) { |
|
147 // TODO: recognize several modes: header.set.*, header.add.*, header.remove.* |
|
148 return attributeName.substr(getHeaderAttributePrefix().size()); |
|
149 } |
|
150 |
|
151 void headerTemplateAttribute(const relpipe::common::type::StringX& value) { |
|
152 auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName(); |
|
153 |
|
154 if (attributeName == L"url") headerTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); |
|
155 else if (attributeName == L"method") headerTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); |
|
156 else if (attributeName == L"name") headerTemplate.name = convertor.to_bytes(value); |
|
157 else if (attributeName == L"value") headerTemplate.value = convertor.to_bytes(value); // TODO: header encoding? |
|
158 else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value)); |
|
159 |
|
160 currentAttributeIndex++; |
|
161 |
|
162 if (currentAttributeIndex % currentReaderMetadata.size() == 0) { |
|
163 std::lock_guard<std::mutex> lock(templatesMutex); |
|
164 currentAttributeIndex = 0; |
|
165 headerTemplates.push_back(headerTemplate); |
|
166 headerTemplate = GlobalHeaderTemplate(); |
|
167 } |
|
168 } |
|
169 |
|
170 void responseTemplateAttribute(const relpipe::common::type::StringX& value) { |
|
171 auto attributeName = currentReaderMetadata[currentAttributeIndex].getAttributeName(); |
|
172 |
|
173 if (attributeName == L"url") responseTemplate.url = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); |
|
174 else if (attributeName == L"method") responseTemplate.method = std::regex(value.size() ? convertor.to_bytes(value) : ".*"); |
|
175 else if (attributeName == L"code") responseTemplate.code = std::stoi(value); |
|
176 else if (attributeName == L"text") responseTemplate.body = convertor.to_bytes(value); |
|
177 else if (attributeName == L"data") responseTemplate.body = "TODO: read binary data: " + convertor.to_bytes(value); // TODO: read hex/binary request body |
|
178 else if (isHeaderAttribute(attributeName)) responseTemplate.headers.push_back(HeaderTemplate(convertor.to_bytes(fetchHeaderName(attributeName)), convertor.to_bytes(value))); // TODO: header encoding? |
|
179 else throw std::invalid_argument("Unsupported attribute in the request_template relation: " + convertor.to_bytes(attributeName + L" = " + value)); |
|
180 |
|
181 currentAttributeIndex++; |
|
182 |
|
183 if (currentAttributeIndex % currentReaderMetadata.size() == 0) { |
|
184 std::lock_guard<std::mutex> lock(templatesMutex); |
|
185 currentAttributeIndex = 0; |
|
186 responseTemplates.push_back(responseTemplate); |
|
187 responseTemplate = ResponseTemplate(); |
|
188 } |
|
189 } |
69 |
190 |
70 public: |
191 public: |
71 |
192 |
72 HttpdHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, Configuration configuration, std::shared_ptr<HTTPServer> httpServer) : relationalWriter(relationalWriter), configuration(configuration), httpServer(httpServer) { |
193 HttpdHandler(shared_ptr<relpipe::writer::RelationalWriter> relationalWriter, Configuration configuration, std::shared_ptr<HTTPServer> httpServer) : relationalWriter(relationalWriter), configuration(configuration), httpServer(httpServer) { |
73 httpServer->setRequestHandler(requestHandler); |
194 httpServer->setRequestHandler(requestHandler); |