Add second work package for REST API implementation
[anna.git] / example / diameter / launcher / MyHandler.cpp
1 // ANNA - Anna is Not Nothingness Anymore                                                         //
2 //                                                                                                //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo                         //
4 //                                                                                                //
5 // See project site at http://redmine.teslayout.com/projects/anna-suite                           //
6 // See accompanying file LICENSE or copy at http://www.teslayout.com/projects/public/anna.LICENSE //
7
8
9 // Standard
10 #include <string>
11 #include <sstream>
12 #include <iomanip>
13 #include <stdlib.h>
14
15 // Project
16 #include <anna/http/Request.hpp>
17 #include <anna/core/functions.hpp>
18
19 // Process
20 #include <MyHandler.hpp>
21 #include <Launcher.hpp>
22 #include <EventOperation.hpp>
23
24 void MyHandler::splitURI(const std::string &uri, std::string & operation, std::string & param1, std::string & param2) const {
25
26   std::string::size_type slash_pos = uri.find("/", 1u);
27   if (slash_pos == std::string::npos) {
28     operation = uri.substr(0);
29   }
30   else {
31     operation = uri.substr(0, slash_pos);
32     param1 = uri.substr(slash_pos + 1);
33
34     std::string::size_type slash2_pos = uri.find("/", slash_pos + 1);
35     if (slash2_pos != std::string::npos) {
36       param1 = uri.substr(slash_pos + 1, slash2_pos - slash_pos - 1);
37       param2 = uri.substr(slash2_pos + 1);
38     }
39   }
40 }
41
42 void MyHandler::sendResponse(anna::comm::ClientSocket& clientSocket, anna::http::Response *response)
43 {
44   try {
45     clientSocket.send(*response);
46   } catch(Exception& ex) {
47     ex.trace();
48   }
49 }
50
51 void MyHandler::evRequest(anna::comm::ClientSocket& clientSocket, const anna::http::Request& request)
52 throw(anna::RuntimeException) {
53
54   const anna::DataBlock& body = request.getBody();
55   anna::http::Method::Type::_v method = request.getMethod();
56   std::string uri = request.getURI();
57   bool isGET = (method == anna::http::Method::Type::Get);
58   bool isPOST = (method == anna::http::Method::Type::Post);
59   std::string body_content;
60   nlohmann::json json_body;
61   std::stringstream ss;
62
63   anna::http::Response* response = allocateResponse();
64   response->setStatusCode(200);
65
66   if (isGET) {
67     if(body.getSize() != 0)
68        response->setStatusCode(400); // bad request
69   }
70   else if (isPOST) {
71     if(body.getSize() != 0) {
72       body_content.assign(body.getData(), body.getSize()); // assign body content:
73
74       try {
75         json_body = nlohmann::json::parse(body_content);
76         LOGINFORMATION(
77           std::string msg("Json body received:\n\n");
78           msg += json_body.dump(4); // pretty print json body
79           anna::Logger::information(msg, ANNA_FILE_LOCATION);
80         );
81       }
82       catch (nlohmann::json::parse_error& e)
83       {
84         ss << "Json body parse error: " << e.what() << '\n'
85            << "exception id: " << e.id << '\n'
86            << "byte position of error: " << e.byte << std::endl;
87         anna::Logger::error(ss.str(), ANNA_FILE_LOCATION);
88       }
89     }
90   }
91   else {
92        response->setStatusCode(405); // method not allowed
93   }
94
95   //////////////////////
96   // Prepare response //
97   //////////////////////
98
99   // Headers:
100   http::Header* contentType =response->find(http::Header::Type::ContentType);
101   if(contentType == NULL)
102     contentType = response->createHeader(http::Header::Type::ContentType);
103   contentType->setValue("application/json");
104
105   // Body:
106   std::string response_content = "Internal error (check ADML traces): ";
107   bool success = false;
108   if (isGET) {
109     success = doGET(uri, response_content);
110   }
111   else if (isPOST) {
112     try {
113       success = doPOST(uri, json_body, response_content);
114     }
115     catch(anna::RuntimeException &ex) {
116       ex.trace();
117       LOGINFORMATION(anna::Logger::information("doPOST exception: TODO control in inner method. Check traces", ANNA_FILE_LOCATION));
118     }
119   }
120
121   ss << R"({ "success":")" << (success ? "true":"false")
122      << R"(", "response": )" << std::quoted(response_content);
123   ss << R"( })";
124
125   anna::DataBlock db_content(true);
126   db_content = ss.str();
127   response->setBody(db_content);
128
129   sendResponse(clientSocket, response);
130 }
131
132 bool MyHandler::doGET(const std::string &uri, std::string &response) {
133
134   bool result = false;
135
136   EventOperation eop(true /* is HTTP */);
137
138   // Snapshots
139   if (uri == "/show-oam") {
140     result = eop.show_oam(response);
141   }
142   else if (uri == "/show-stats") {
143     result = eop.show_stats(response);
144   }
145
146   return result;
147 }
148
149 bool MyHandler::doPOST(const std::string &uri, const nlohmann::json &j, std::string &response) {
150
151   bool result = false;
152
153   EventOperation eop(true /* is HTTP */);
154
155   std::string opType{}, param1{}, param2{};
156   splitURI(uri, opType, param1, param2);
157
158   // Node management
159   if (opType == "/node") {
160     auto it = j.find("name");
161     if (it != j.end())
162       result = eop.node(response, *it);
163     else
164       response += "missing 'name' string field";
165   }
166   else if (opType == "/node-auto") {
167     result = eop.node_auto(response);
168   }
169
170   // Parsing operations
171   else if (opType == "/code") {
172     auto it = j.find("diameterJson");
173     if (it != j.end())
174       result = eop.code(response, it->dump(4)); // get the object as string (always indentation = 4)
175     else
176       response += "missing 'diameterJson' object field";
177   }
178   else if (opType == "/decode") {
179     auto it = j.find("diameterHex");
180     if (it != j.end())
181       result = eop.decode(response, *it);
182     else
183       response += "missing 'diameterHex' string field";
184   }
185   else if (opType == "/loadmsg") {
186     auto it = j.find("diameterJson");
187     if (it != j.end())
188       result = eop.loadmsg(response, it->dump(4)); // get the object as string (always indentation = 4)
189     else
190       response += "missing 'diameterJson' object field";
191   }
192
193   // Hot changes
194   else if (opType == "/services") {
195     auto it = j.find("servicesJson");
196     if (it != j.end()) {
197       result = eop.services(response, it->dump(4)); // get the object as string (always indentation = 4)
198     }
199     else
200       response += "missing 'servicesJson' object field";
201   }
202   else if (opType == "/diameterServerSessions") {
203     auto it = j.find("sessions");
204     if (it != j.end())
205       result = eop.diameterServerSessions(response, it->get<int>());
206     else
207       response += "missing 'session' integer field";
208   }
209   else if (opType == "/change-dir") {
210     auto it = j.find("directory");
211     std::string directory = (it != j.end()) ? *it : ""; // default is: restore initial directory
212     result = eop.change_dir(response, directory);
213   }
214
215   // Client sessions visibility
216   else if (opType == "/visibility") {
217     auto it = j.find("action");
218     if (it != j.end()) {
219       std::string action = *it;
220
221       it = j.find("addressPort");
222       std::string addressPort = (it != j.end()) ? *it : "";
223
224       it = j.find("socket");
225       int socket = (it != j.end()) ? it->get<int>() : -1;
226
227       result = eop.visibility(response, action, addressPort, socket);
228     }
229     else
230       response += "missing 'action' string field";
231   }
232
233   // Snapshots
234   else if (opType == "/collect") {
235     result = eop.collect(response);
236   }
237   else if (opType == "/context") {
238     auto it = j.find("targetFile");
239     std::string targetFile = (it != j.end()) ? *it : "";
240     result = eop.context(response, targetFile);
241   }
242   else if (opType == "/forceCountersRecord") {
243     result = eop.forceCountersRecord(response);
244   }
245   else if (opType == "/log-statistics-samples") {
246     auto it = j.find("list");
247     if (it != j.end())
248       result = eop.log_statistics_samples(response, *it);
249     else
250       response += "missing 'list' string field";
251   }
252
253   // Flow operations
254   else if ((opType == "/sendmsg2e")||(opType == "/sendmsg2c")) {
255     auto itJ = j.find("diameterJson");
256     if (itJ != j.end()) {
257       if (opType == "/sendmsg2e")
258         result = eop.sendmsg_hex_2e(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
259       else
260         result = eop.sendmsg_hex_2c(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
261     }
262     else
263       response += "missing 'diameterJson' object field";
264   }
265   else if ((opType == "/sendhex2e")||(opType == "/sendhex2c")) {
266     auto itH = j.find("diameterHex");
267     if (itH != j.end())
268       if (opType == "/sendhex2e")
269         result = eop.sendmsg_hex_2e(response, *itH, false);
270       else
271         result = eop.sendmsg_hex_2c(response, *itH, false);
272     else
273       response += "missing 'diameterHex' string field";
274   }
275   else if ((opType == "/answermsg2e")||(opType == "/answermsg2c")) {
276     auto itJ = j.find("diameterJson");
277     auto itA = j.find("action");
278     bool hasJ = (itJ != j.end());
279     bool hasA = (itA != j.end());
280
281     if (hasJ != hasA) { // XOR
282       std::string action;
283       if (hasA) {
284         action = *itA;
285         if (action == "") action = "list"; // default if missing
286       }
287
288       if (opType == "/answermsg2e")
289         result = eop.answermsg_action_2e(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
290       else
291         result = eop.answermsg_action_2c(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
292     }
293     else
294       response += "missing 'diameterJson' object or 'action' string field (only one accepted)";
295   }
296
297   // FSM testing
298   // test_id__<command>
299   else if (opType == "/testid-description") {
300     auto it = j.find("description");
301     if (it != j.end())
302       result = eop.test_id__description(response, atoi(param1.c_str()), *it);
303     else
304       response += "missing 'description' string field";
305   }
306   else if (opType == "/testid-ip-limit") {
307     auto it = j.find("amount");
308     int amount = (it != j.end()) ? it->get<int>() : 1;
309     result = eop.test_id__ip_limit(response, atoi(param1.c_str()), amount);
310   }
311   else if (opType == "/testid-timeout") {
312     auto it = j.find("msecs");
313     if (it != j.end())
314       result = eop.test_id__timeout(response, atoi(param1.c_str()), it->get<int>());
315     else
316       response += "missing 'msecs' integer field";
317   }
318   else if ((opType == "/testid-sendmsg2e")||(opType == "/testid-sendmsg2c")) {
319     auto it = j.find("diameterJson");
320     if (it != j.end()) {
321
322       auto itS = j.find("stepNumber");
323       int stepNumber = (itS != j.end()) ? itS->get<int>() : -1;
324
325       if (opType == "/testid-sendmsg2e")
326         result = eop.test_id__sendmsg2e(response, atoi(param1.c_str()), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
327       else
328         result = eop.test_id__sendmsg2c(response, atoi(param1.c_str()), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
329     }
330     else
331       response += "missing 'diameterJson' object field";
332   }
333   else if (opType == "/testid-delay") {
334     auto it = j.find("msecs");
335     if (it != j.end())
336       result = eop.test_id__delay(response, atoi(param1.c_str()), it->get<int>());
337     else
338       response += "missing 'msecs' integer field";
339   }
340   else if (opType == "/testid-sh-command") {
341     auto it = j.find("script");
342     if (it != j.end())
343       result = eop.test_id__sh_command(response, atoi(param1.c_str()), *it);
344     else
345       response += "missing 'script' string field";
346   }
347   else if ((opType == "/testid-waitfe-hex")||(opType == "/testid-waitfc-hex")) {
348     auto it = j.find("hex");
349     if (it != j.end()) {
350
351       auto itS = j.find("strict");
352       bool strict = (itS != j.end()) ? (*itS == "true") : false;
353
354       if (opType == "/testid-waitfe-hex")
355         result = eop.test_id__waitfe_hex(response, atoi(param1.c_str()), *it, strict);
356       else
357         result = eop.test_id__waitfc_hex(response, atoi(param1.c_str()), *it, strict);
358     }
359     else
360       response += "missing 'hex' string field";
361   }
362   else if ((opType == "/testid-waitfe-msg")||(opType == "/testid-waitfc-msg")) {
363     auto it = j.find("diameterJson");
364     if (it != j.end()) {
365
366       auto itS = j.find("strict");
367       bool strict = (itS != j.end()) ? (*itS == "true") : false;
368
369       if (opType == "/testid-waitfe-msg")
370         result = eop.test_id__waitfe_msg(response, atoi(param1.c_str()), it->dump(4), strict); // get the object as string (always indentation = 4)
371       else
372         result = eop.test_id__waitfc_msg(response, atoi(param1.c_str()), it->dump(4), strict); // get the object as string (always indentation = 4)
373     }
374     else
375       response += "missing 'diameterJson' object field";
376   }
377   else if ((opType == "/testid-waitfe")||(opType == "/testid-waitfc")) {
378     auto it = j.find("condition");
379     if (it != j.end()) {
380       // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
381       auto it_code = it->find("code");
382       auto it_bitR = it->find("bitR");
383       auto it_hopByHop = it->find("hopByHop");
384       auto it_applicationId = it->find("applicationId");
385       auto it_sessionId = it->find("sessionId");
386       auto it_resultCode = it->find("resultCode");
387       auto it_msisdn = it->find("msisdn");
388       auto it_imsi = it->find("imsi");
389       auto it_serviceContextId = it->find("serviceContextId");
390
391       std::string condition;
392       condition += (it_code != it->end()) ? *it_code : ""; condition += "|";
393       condition += (it_bitR != it->end()) ? *it_bitR : ""; condition += "|";
394       condition += (it_hopByHop != it->end()) ? *it_hopByHop : ""; condition += "|";
395       condition += (it_applicationId != it->end()) ? *it_applicationId : ""; condition += "|";
396       condition += (it_sessionId != it->end()) ? *it_sessionId : ""; condition += "|";
397       condition += (it_resultCode != it->end()) ? *it_resultCode : ""; condition += "|";
398       condition += (it_msisdn != it->end()) ? *it_msisdn : ""; condition += "|";
399       condition += (it_imsi != it->end()) ? *it_imsi : ""; condition += "|";
400       condition += (it_serviceContextId != it->end()) ? *it_serviceContextId : "";
401
402       if (opType == "/testid-waitfe")
403         result = eop.test_id__waitfe(response, atoi(param1.c_str()), condition);
404       else
405         result = eop.test_id__waitfc(response, atoi(param1.c_str()), condition);
406     }
407     else
408       response += "missing 'condition' object field";
409   }
410
411   // Testcases execution
412   // test__<command>
413   else if (opType == "/test-ttps") {
414     auto it = j.find("amount");
415     if (it != j.end())
416       result = eop.test__ttps(response, it->get<int>());
417     else
418       response += "missing 'amount' integer field";
419   }
420   else if (opType == "/test-next") {
421     auto it = j.find("syncAmount");
422     int syncAmount = (it != j.end()) ? it->get<int>() : 1;
423     result = eop.test__next(response, syncAmount);
424   }
425   else if (opType == "/test-ip-limit") {
426     auto it = j.find("amount");
427     int amount = (it != j.end()) ? it->get<int>() : -2; // default is: show current ip-limit and in-progress test cases amount
428     result = eop.test__ip_limit(response, amount);
429   }
430   else if (opType == "/test-goto") {
431     auto it = j.find("id");
432     if (it != j.end())
433       result = eop.test__goto(response, it->get<int>());
434     else
435       response += "missing 'id' integer field";
436   }
437   else if (opType == "/test-run") {
438     auto it = j.find("id");
439     if (it != j.end())
440       result = eop.test__run(response, it->get<int>());
441     else
442       response += "missing 'id' integer field";
443   }
444   else if (opType == "/test-look") {
445     auto it = j.find("id");
446     int id = (it != j.end()) ? it->get<int>() : -1; // default is: current
447     result = eop.test__look(response, id);
448   }
449   else if (opType == "/test-state") {
450     auto it = j.find("id");
451     int id = (it != j.end()) ? it->get<int>() : -1; // default is: current
452     result = eop.test__state(response, id);
453   }
454   else if (opType == "/test-interact") {
455     auto it = j.find("amount");
456     if (it != j.end()) {
457
458       auto itI = j.find("id");
459       int id = (itI != j.end()) ? itI->get<int>() : -1; // default is: current
460
461       result = eop.test__interact(response, it->get<int>(), id);
462     }
463     else
464       response += "missing 'amount' integer field";
465   }
466   else if (opType == "/test-reset") {
467     auto it = j.find("type");
468     if (it != j.end()) {
469
470       auto itI = j.find("id");
471       int id = (itI != j.end()) ? itI->get<int>() : -2; // default is: apply to all the tests
472
473       if ((*it == "soft") || (*it == "hard")) {
474         result = eop.test__reset(response, (*it == "soft"), id);
475       }
476       else
477         response += "invalid 'type' string field (allowed: soft|hard)";
478     }
479     else
480       response += "missing 'type' string field";
481   }
482   else if (opType == "/test-repeats") {
483     auto it = j.find("amount");
484     if (it != j.end())
485       result = eop.test__repeats(response, it->get<int>());
486     else
487       response += "missing 'amount' integer field";
488   }
489   else if (opType == "/test-auto-reset") {
490     auto it = j.find("type");
491     if (it != j.end()) {
492
493       if ((*it == "soft") || (*it == "hard")) {
494         result = eop.test__auto_reset(response, (*it == "soft"));
495       }
496       else
497         response += "invalid 'type' string field (allowed: soft|hard)";
498     }
499     else
500       response += "missing 'type' string field";
501   }
502   else if (opType == "/test-initialized") {
503     result = eop.test__initialized(response);
504   }
505   else if (opType == "/test-finished") {
506     result = eop.test__finished(response);
507   }
508   else if (opType == "/test-clear") {
509     result = eop.test__clear(response);
510   }
511   else if (opType == "/test-junit") {
512     auto it = j.find("targetFile");
513     std::string targetFile = (it != j.end()) ? *it : ""; // default is: get junit on response instead dumping on file
514     result = eop.test__junit(response, targetFile);
515   }
516   else if (opType == "/test-summary-counts") {
517     result = eop.test__summary_counts(response);
518   }
519   else if (opType == "/test-summary-states") {
520     result = eop.test__summary_states(response);
521   }
522   else if (opType == "/test-summary") {
523     result = eop.test__summary(response);
524   }
525   else if (opType == "/test-report") {
526     auto it = j.find("state");
527     std::string state = (it != j.end()) ? *it : "all"; // initialized|in-progress|failed|success|[all]|none
528
529     auto itA = j.find("action");
530     std::string action = (itA != j.end()) ? *itA : "enable"; // default is: enable
531
532     if ((action == "enable") || (action == "disable")) {
533       result = eop.test__report(response, state, (action == "enable"));
534     }
535     else
536       response += "invalid 'action' string field (allowed: enable|disable)";
537   }
538   else if ((opType == "/test-report-hex")||(opType == "/test-dump_stdout")) {
539
540     auto itA = j.find("action");
541     std::string action = (itA != j.end()) ? *itA : "enable"; // default is: enable
542
543     if ((action == "enable") || (action == "disable")) {
544       bool enable = (action == "enable");
545
546       if (opType == "/test-report-hex")
547         result = eop.test__report_hex(response, enable);
548       else
549         result = eop.test__dump_stdout(response, enable);
550     }
551     else
552       response += "invalid 'action' string field (allowed: enable|disable)";
553   }
554
555
556   return result;
557 }
558