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