d2433c83a082115a62994b49a07c1d07c016ee48
[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() && it->is_string())
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() && it->is_object())
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() && it->is_string())
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() && it->is_object())
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() && it->is_object()) {
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() && it->is_number_integer())
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->is_string()) ? *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() && it->is_string()) {
219       std::string action = *it;
220
221       it = j.find("addressPort");
222       std::string addressPort = (it != j.end() && it->is_string()) ? *it : "";
223
224       it = j.find("socket");
225       int socket = (it != j.end() && it->is_number_integer()) ? 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->is_string()) ? *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     std::string list = (it != j.end() && it->is_string()) ? *it : "list";
248     result = eop.log_statistics_samples(response, list);
249   }
250
251   // Flow operations
252   else if ((opType == "/sendmsg2e")||(opType == "/sendmsg2c")) {
253     auto itJ = j.find("diameterJson");
254     if (itJ != j.end() && itJ->is_object()) {
255       if (opType == "/sendmsg2e")
256         result = eop.sendmsg_hex_2e(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
257       else
258         result = eop.sendmsg_hex_2c(response, itJ->dump(4), true); // get the object as string (always indentation = 4)
259     }
260     else
261       response += "missing 'diameterJson' object field";
262   }
263   else if ((opType == "/sendhex2e")||(opType == "/sendhex2c")) {
264     auto itH = j.find("diameterHex");
265     if (itH != j.end() && itH->is_string())
266       if (opType == "/sendhex2e")
267         result = eop.sendmsg_hex_2e(response, *itH, false);
268       else
269         result = eop.sendmsg_hex_2c(response, *itH, false);
270     else
271       response += "missing 'diameterHex' string field";
272   }
273   else if ((opType == "/answermsg2e")||(opType == "/answermsg2c")) {
274     auto itJ = j.find("diameterJson");
275     auto itA = j.find("action");
276     bool hasJ = (itJ != j.end() && itJ->is_object());
277     bool hasA = (itA != j.end() && itA->is_string());
278
279     if (hasJ != hasA) { // XOR
280       std::string action;
281       if (hasA) {
282         action = *itA;
283         if (action == "") action = "list"; // default if missing
284       }
285
286       if (opType == "/answermsg2e")
287         result = eop.answermsg_action_2e(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
288       else
289         result = eop.answermsg_action_2c(response, (hasJ ? itJ->dump(4) : action), hasJ); // itJ: get the object as string (always indentation = 4)
290     }
291     else
292       response += "missing 'diameterJson' object or 'action' string field (only one accepted)";
293   }
294
295   // FSM testing
296   // test_id__<command>
297   else if (opType == "/testid-description") {
298     auto it = j.find("description");
299     if (it != j.end() && it->is_string())
300       result = eop.test_id__description(response, atoi(param1.c_str()), *it);
301     else
302       response += "missing 'description' string field";
303   }
304   else if (opType == "/testid-ip-limit") {
305     auto it = j.find("amount");
306     int amount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : 1;
307     result = eop.test_id__ip_limit(response, atoi(param1.c_str()), amount);
308   }
309   else if (opType == "/testid-timeout") {
310     auto it = j.find("msecs");
311     if (it != j.end() && it->is_number_integer())
312       result = eop.test_id__timeout(response, atoi(param1.c_str()), it->get<int>());
313     else
314       response += "missing 'msecs' integer field";
315   }
316   else if ((opType == "/testid-sendmsg2e")||(opType == "/testid-sendmsg2c")) {
317     auto it = j.find("diameterJson");
318     if (it != j.end() && it->is_object()) {
319
320       auto itS = j.find("stepNumber");
321       int stepNumber = (itS != j.end() && itS->is_number_integer()) ? itS->get<int>() : -1;
322
323       result = eop.test_id__sendmsg2e_2c(response, atoi(param1.c_str()),
324                                         (opType == "/testid-sendmsg2e"), it->dump(4), stepNumber); // get the object as string (always indentation = 4)
325     }
326     else
327       response += "missing 'diameterJson' object field";
328   }
329   else if (opType == "/testid-delay") {
330     auto it = j.find("msecs");
331     if (it != j.end() && it->is_number_integer())
332       result = eop.test_id__delay(response, atoi(param1.c_str()), it->get<int>());
333     else
334       response += "missing 'msecs' integer field";
335   }
336   else if (opType == "/testid-sh-command") {
337     auto it = j.find("script");
338     if (it != j.end() && it->is_string())
339       result = eop.test_id__sh_command(response, atoi(param1.c_str()), *it);
340     else
341       response += "missing 'script' string field";
342   }
343   else if ((opType == "/testid-waitfe-hex")||(opType == "/testid-waitfc-hex")) {
344     auto it = j.find("hex");
345     if (it != j.end() && it->is_string()) {
346
347       auto itS = j.find("strict");
348       bool strict = (itS != j.end() && itS->is_string()) ? (*itS == "true") : false;
349
350       result = eop.test_id__waitfefc_hex(response, atoi(param1.c_str()), (opType == "/testid-waitfe-hex"), *it, strict);
351     }
352     else
353       response += "missing 'hex' string field";
354   }
355   else if ((opType == "/testid-waitfe-msg")||(opType == "/testid-waitfc-msg")) {
356     auto it = j.find("diameterJson");
357     if (it != j.end() && it->is_object()) {
358
359       auto itS = j.find("strict");
360       bool strict = (itS != j.end() && itS->is_string()) ? (*itS == "true") : false;
361
362       result = eop.test_id__waitfefc_msg(response, atoi(param1.c_str()), (opType == "/testid-waitfe-msg"), it->dump(4), strict); // get the object as string (always indentation = 4)
363     }
364     else
365       response += "missing 'diameterJson' object field";
366   }
367   else if ((opType == "/testid-waitfe")||(opType == "/testid-waitfc")) {
368     auto it = j.find("condition");
369     if (it != j.end() && it->is_object()) {
370
371 /*
372       auto j2 = it->get<nlohmann::json::object_t>();
373
374       // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
375       auto it_code = j2.find("code");
376       auto it_bitR = j2.find("bitR");
377       auto it_hopByHop = j2.find("hopByHop");
378       auto it_applicationId = j2.find("applicationId");
379       auto it_sessionId = j2.find("sessionId");
380       auto it_resultCode = j2.find("resultCode");
381       auto it_msisdn = j2.find("msisdn");
382       auto it_imsi = j2.find("imsi");
383       auto it_serviceContextId = j2.find("serviceContextId");
384
385       std::string p1 = (it_code != j2.end() && it_code->is_string()) ? *it_code : "";
386       std::string p2 = (it_bitR != j2.end() && it_bitR->is_string()) ? *it_bitR : "";
387       std::string p3 = (it_hopByHop != it->end() && it_hopByHop->is_string()) ? *it_hopByHop : "";
388       std::string p4 = (it_applicationId != it->end() && it_applicationId->is_string()) ? *it_applicationId : "";
389       std::string p5 = (it_sessionId != it->end() && it_sessionId->is_string()) ? *it_sessionId : "";
390       std::string p6 = (it_resultCode != it->end() && it_resultCode->is_string()) ? *it_resultCode : "";
391       std::string p7 = (it_msisdn != it->end() && it_msisdn->is_string()) ? *it_msisdn : "";
392       std::string p8 = (it_imsi != it->end() && it_imsi->is_string()) ? *it_imsi : "";
393       std::string p9 = (it_serviceContextId != it->end() && it_serviceContextId->is_string()) ? *it_serviceContextId : "";
394 */
395
396       // [code]|[bitR]|[hopByHop]|[applicationId]|[sessionId]|[resultCode]|[msisdn]|[imsi]|[serviceContextId]
397       auto it_code = it->find("code");
398       auto it_bitR = it->find("bitR");
399       auto it_hopByHop = it->find("hopByHop");
400       auto it_applicationId = it->find("applicationId");
401       auto it_sessionId = it->find("sessionId");
402       auto it_resultCode = it->find("resultCode");
403       auto it_msisdn = it->find("msisdn");
404       auto it_imsi = it->find("imsi");
405       auto it_serviceContextId = it->find("serviceContextId");
406
407       std::string p1 = (it_code != it->end() && it_code->is_string()) ? *it_code : "";
408       std::string p2 = (it_bitR != it->end() && it_bitR->is_string()) ? *it_bitR : "";
409       std::string p3 = (it_hopByHop != it->end() && it_hopByHop->is_string()) ? *it_hopByHop : "";
410       std::string p4 = (it_applicationId != it->end() && it_applicationId->is_string()) ? *it_applicationId : "";
411       std::string p5 = (it_sessionId != it->end() && it_sessionId->is_string()) ? *it_sessionId : "";
412       std::string p6 = (it_resultCode != it->end() && it_resultCode->is_string()) ? *it_resultCode : "";
413       std::string p7 = (it_msisdn != it->end() && it_msisdn->is_string()) ? *it_msisdn : "";
414       std::string p8 = (it_imsi != it->end() && it_imsi->is_string()) ? *it_imsi : "";
415       std::string p9 = (it_serviceContextId != it->end() && it_serviceContextId->is_string()) ? *it_serviceContextId : "";
416
417       result = eop.test_id__waitfefc(response, atoi(param1.c_str()), (opType == "/testid-waitfe"), p1, p2, p3, p4, p5, p6, p7, p8, p9);
418     }
419     else
420       response += "missing 'condition' object field";
421   }
422
423   // Testcases execution
424   // test__<command>
425   else if (opType == "/test-ttps") {
426     auto it = j.find("amount");
427     if (it != j.end() && it->is_number_integer())
428       result = eop.test__ttps(response, it->get<int>());
429     else
430       response += "missing 'amount' integer field";
431   }
432   else if (opType == "/test-next") {
433     auto it = j.find("syncAmount");
434     int syncAmount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : 1;
435     result = eop.test__next(response, syncAmount);
436   }
437   else if (opType == "/test-ip-limit") {
438     auto it = j.find("amount");
439     int amount = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -2; // default is: show current ip-limit and in-progress test cases amount
440     result = eop.test__ip_limit(response, amount);
441   }
442   else if (opType == "/test-goto") {
443     auto it = j.find("id");
444     if (it != j.end() && it->is_number_integer())
445       result = eop.test__goto(response, it->get<int>());
446     else
447       response += "missing 'id' integer field";
448   }
449   else if (opType == "/test-run") {
450     auto it = j.find("id");
451     if (it != j.end() && it->is_number_integer())
452       result = eop.test__run(response, it->get<int>());
453     else
454       response += "missing 'id' integer field";
455   }
456   else if (opType == "/test-look") {
457     auto it = j.find("id");
458     int id = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -1; // default is: current
459     result = eop.test__look(response, id);
460   }
461   else if (opType == "/test-state") {
462     auto it = j.find("id");
463     int id = (it != j.end() && it->is_number_integer()) ? it->get<int>() : -1; // default is: current
464     result = eop.test__state(response, id);
465   }
466   else if (opType == "/test-interact") {
467     auto it = j.find("amount");
468     if (it != j.end() && it->is_number_integer()) {
469
470       auto itI = j.find("id");
471       int id = (itI != j.end() && itI->is_number_integer()) ? itI->get<int>() : -1; // default is: current
472
473       result = eop.test__interact(response, it->get<int>(), id);
474     }
475     else
476       response += "missing 'amount' integer field";
477   }
478   else if (opType == "/test-reset") {
479     auto it = j.find("type");
480     std::string type = (it != j.end() && it->is_string()) ? *it : "soft";
481
482     auto itI = j.find("id");
483     int id = (itI != j.end() && itI->is_number_integer()) ? itI->get<int>() : -1; // default is: apply to all the tests
484
485     if ((type == "soft") || (type == "hard")) {
486       result = eop.test__reset(response, (type == "soft"), id);
487     }
488     else
489       response += "invalid 'type' string field (allowed: [soft]|hard)";
490   }
491   else if (opType == "/test-repeats") {
492     auto it = j.find("amount");
493     if (it != j.end() && it->is_number_integer())
494       result = eop.test__repeats(response, it->get<int>());
495     else
496       response += "missing 'amount' integer field";
497   }
498   else if (opType == "/test-auto-reset") {
499     auto it = j.find("type");
500     if (it != j.end() && it->is_string()) {
501
502       if ((*it == "soft") || (*it == "hard")) {
503         result = eop.test__auto_reset(response, (*it == "soft"));
504       }
505       else
506         response += "invalid 'type' string field (allowed: soft|hard)";
507     }
508     else
509       response += "missing 'type' string field";
510   }
511   else if (opType == "/test-initialized") {
512     result = eop.test__initialized(response);
513   }
514   else if (opType == "/test-finished") {
515     result = eop.test__finished(response);
516   }
517   else if (opType == "/test-clear") {
518     result = eop.test__clear(response);
519   }
520   else if (opType == "/test-junit") {
521     auto it = j.find("targetFile");
522     std::string targetFile = (it != j.end() && it->is_string()) ? *it : "/tmp/junit.xml";
523     result = eop.test__junit(response, targetFile);
524   }
525   else if (opType == "/test-summary-counts") {
526     result = eop.test__summary_counts(response);
527   }
528   else if (opType == "/test-summary-states") {
529     result = eop.test__summary_states(response);
530   }
531   else if (opType == "/test-summary") {
532     result = eop.test__summary(response);
533   }
534   else if (opType == "/test-report") {
535     auto it = j.find("state");
536     std::string state = (it != j.end() && it->is_string()) ? *it : "all"; // initialized|in-progress|failed|success|[all]|none
537
538     auto itA = j.find("action");
539     std::string action = (itA != j.end() && itA->is_string()) ? *itA : "enable"; // default is: enable
540
541     if ((action == "enable") || (action == "disable")) {
542       result = eop.test__report(response, state, (action == "enable"));
543     }
544     else
545       response += "invalid 'action' string field (allowed: enable|disable)";
546   }
547   else if ((opType == "/test-report-hex")||(opType == "/test-dump-stdout")) {
548
549     auto itA = j.find("action");
550     std::string action = (itA != j.end() && itA->is_string()) ? *itA : "enable"; // default is: enable
551
552     if ((action == "enable") || (action == "disable")) {
553       bool enable = (action == "enable");
554
555       if (opType == "/test-report-hex")
556         result = eop.test__report_hex(response, enable);
557       else
558         result = eop.test__dump_stdout(response, enable);
559     }
560     else
561       response += "invalid 'action' string field (allowed: enable|disable)";
562   }
563
564
565   return result;
566 }
567