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