NEW Restruct launcher source code. Separate classes in different files to improve...
[anna.git] / example / diameter / launcher / MyLocalServer.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 // Project
10 #include <anna/core/core.hpp>
11 #include <anna/diameter/functions.hpp>
12 #include <anna/time/functions.hpp>
13 #include <anna/diameter/codec/Engine.hpp>
14 #include <anna/diameter.comm/Response.hpp>
15
16 // Process
17 #include "MyLocalServer.hpp"
18 #include "Launcher.hpp"
19
20 // Auxiliary message for sendings
21 extern anna::diameter::comm::Message G_commMsgSent2c, G_commMsgFwd2e;
22 extern anna::diameter::codec::Message G_codecMsg, G_codecAnsMsg;
23 extern anna::Recycler<anna::diameter::comm::Message> G_commMessages; // create on requests forwards without programmed answer / release in answers forward
24
25
26 void MyLocalServer::eventRequest(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message)
27 throw(anna::RuntimeException) {
28   LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventRequest", ANNA_FILE_LOCATION));
29   // Performance stats:
30   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
31   CommandLine& cl(anna::CommandLine::instantiate());
32   // CommandId:
33   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
34   LOGDEBUG
35   (
36     std::string msg = "Request received: ";
37     msg += anna::diameter::functions::commandIdAsPairString(cid);
38     msg += " | DiameterServer: ";
39     msg += anna::functions::socketLiteralAsString(serverSession->getAddress(), serverSession->getPort());
40     msg += " | EventTime: ";
41     msg += anna::time::functions::currentTimeAsString();
42     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
43   );
44
45   // Write reception
46   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfc", serverSession->asString());
47
48   // If no answer is programmed and entity is configured, the failed request would be forwarded even being wrong (delegates at the end point)
49   int code = cid.first;
50   anna::diameter::codec::Message *programmed_answer = a_reactingAnswers.getMessage(code);
51   bool programmed = (programmed_answer != NULL);
52
53   anna::diameter::comm::Entity *entity = my_app.getEntity();
54   if(!programmed && entity) {  // forward condition (no programmed answer + entity available)
55     anna::diameter::comm::Message *msg = G_commMessages.create();
56     msg->updateEndToEnd(false); // end-to-end will be kept
57     msg->setBody(message);
58     msg->setRequestServerSessionKey(serverSession->getKey());
59     bool success = entity->send(msg, cl.exists("balance"));
60
61     // Detailed log:
62     if(my_app.logEnabled()) {
63       anna::diameter::comm::Server *usedServer = entity->getLastUsedResource();
64       anna::diameter::comm::ClientSession *usedClientSession = usedServer ? usedServer->getLastUsedResource() : NULL;
65       std::string detail = usedClientSession ? usedClientSession->asString() : "<null client session>"; // esto no deberia ocurrir
66       my_app.writeLogFile(message, (success ? "fwd2e" : "fwd2eError"), detail); // forwarded
67     }
68
69     return;
70   }
71
72   // Error analisys:
73   bool analysisOK = true; // by default
74   anna::diameter::codec::Message *answer_message = NULL;
75
76   if(!cl.exists("ignoreErrors")) {  // Error analysis
77     answer_message = (anna::diameter::codec::Message*) & G_codecAnsMsg;
78     answer_message->clear();
79
80     // Decode
81     try { G_codecMsg.decode(message, answer_message); } catch(anna::RuntimeException &ex) { ex.trace(); }
82
83     answer_message->setStandardToAnswer(G_codecMsg, my_app.getMyDiameterEngine()->getHost(), my_app.getMyDiameterEngine()->getRealm());
84     analysisOK = (answer_message->getResultCode() == anna::diameter::helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS);
85   }
86
87   // Programmed answer only when all is ok
88   if(analysisOK) {
89     if(programmed) {
90       answer_message = programmed_answer;
91       // Prepare answer:
92       my_app.getCommunicator()->prepareAnswer(answer_message, message);
93     } else return; // nothing done
94   }
95
96   anna::diameter::codec::Engine *codecEngine = (anna::functions::component <anna::diameter::codec::Engine> (ANNA_FILE_LOCATION));
97   anna::diameter::codec::Engine::ValidationMode::_v backupVM = codecEngine->getValidationMode();
98
99   if(!analysisOK)
100     codecEngine->setValidationMode(anna::diameter::codec::Engine::ValidationMode::Never);
101
102   try {
103     G_commMsgSent2c.setBody(answer_message->code());
104     /* response = NULL =*/serverSession->send(&G_commMsgSent2c);
105
106     if(my_app.logEnabled()) my_app.writeLogFile(*answer_message, "sent2c", serverSession->asString());
107   } catch(anna::RuntimeException &ex) {
108     ex.trace();
109
110     if(my_app.logEnabled()) my_app.writeLogFile(*answer_message, "send2cError", serverSession->asString());
111   }
112
113   // Restore validation mode
114   codecEngine->setValidationMode(backupVM);
115
116   // Pop front the reacting answer:
117   if(analysisOK && programmed) a_reactingAnswers.nextMessage(code);
118 }
119
120 void MyLocalServer::eventResponse(const anna::diameter::comm::Response &response)
121 throw(anna::RuntimeException) {
122   LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventResponse", ANNA_FILE_LOCATION));
123   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
124   CommandLine& cl(anna::CommandLine::instantiate());
125   anna::diameter::comm::ClassCode::_v code = response.getClassCode();
126   anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode();
127   anna::diameter::comm::Message* request = const_cast<anna::diameter::comm::Message*>(response.getRequest());
128   const anna::DataBlock* message = response.getMessage();
129   const anna::diameter::comm::ServerSession *serverSession = static_cast<const anna::diameter::comm::ServerSession *>(response.getSession());
130   bool isBindResponse = (code == anna::diameter::comm::ClassCode::Bind);
131   bool isApplicationMessage = (code == anna::diameter::comm::ClassCode::ApplicationMessage);
132   bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout);
133   bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
134   bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
135   // CommandId:
136   anna::diameter::CommandId request_cid = request->getCommandId();
137   LOGDEBUG
138   (
139     std::string msg = "Response received for original diameter request: ";
140     msg += anna::diameter::functions::commandIdAsPairString(request_cid);
141     msg += " | Response: ";
142     msg += response.asString();
143     msg += " | LocalServer: ";
144     msg += anna::functions::socketLiteralAsString(serverSession->getAddress(), serverSession->getPort());
145     msg += " | EventTime: ";
146     msg += anna::time::functions::currentTimeAsString();
147     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
148   );
149
150   if(isUnavailable) {
151     //if (isApplicationMessage)
152     LOGWARNING(anna::Logger::warning("Diameter client unavailable for Diameter Request", ANNA_FILE_LOCATION));
153   }
154
155   if(contextExpired) {
156     //if (isApplicationMessage)
157     LOGWARNING(anna::Logger::warning("Context Expired for Diameter Request which was sent to the client", ANNA_FILE_LOCATION));
158
159     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
160       if(my_app.logEnabled()) my_app.writeLogFile(*request, "req2c-expired", serverSession->asString());
161     }
162   }
163
164   if(isOK) {
165     LOGDEBUG(
166       std::string msg = "Received response for diameter message:  ";
167       msg += anna::diameter::functions::commandIdAsPairString(request_cid);
168       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
169     );
170
171     // Write reception
172     if(my_app.logEnabled()) my_app.writeLogFile(*message, "recvfc", serverSession->asString());
173
174     // This is not very usual, but answers could arrive from clients:
175     anna::diameter::comm::Entity *entity = my_app.getEntity();
176
177     if(entity) {
178       anna::diameter::comm::ClientSession *usedClientSession = my_app.getMyDiameterEngine()->findClientSession(request->getRequestClientSessionKey());
179       std::string detail;
180
181       if(my_app.logEnabled()) detail = usedClientSession ? usedClientSession->asString() : "<null client session>";  // esto no deberia ocurrir
182
183       try {
184         G_commMsgFwd2e.updateEndToEnd(false); // end-to-end will be kept
185         G_commMsgFwd2e.setBody(*message);
186
187         // Metodo 1:
188         if(usedClientSession) /* response = NULL =*/usedClientSession->send(&G_commMsgFwd2e);
189
190         // Metodo 2:
191         //G_commMsgFwd2e.setRequestClientSessionKey(request->getRequestClientSessionKey());
192         //bool success = entity->send(G_commMsgFwd2e);
193         G_commMessages.release(request);
194
195         if(my_app.logEnabled()) my_app.writeLogFile(*message, "fwd2e", detail);  // forwarded
196       } catch(anna::RuntimeException &ex) {
197         ex.trace();
198
199         if(my_app.logEnabled()) my_app.writeLogFile(*message, "fwd2eError", detail);  // forwarded
200       }
201     }
202   }
203 }
204
205 void MyLocalServer::eventUnknownResponse(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message)
206 throw(anna::RuntimeException) {
207   LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventUnknownResponse", ANNA_FILE_LOCATION));
208   // Performance stats:
209   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
210   // CommandId:
211   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
212   LOGDEBUG
213   (
214     std::string msg = "Out-of-context response received from client: ";
215     msg += anna::diameter::functions::commandIdAsPairString(cid);
216     msg += " | DiameterServer: ";
217     msg += anna::functions::socketLiteralAsString(serverSession->getAddress(), serverSession->getPort());
218     msg += " | EventTime: ";
219     msg += anna::time::functions::currentTimeAsString();
220     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
221   );
222
223   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfc-ans-unknown", serverSession->asString());
224 }
225
226 void MyLocalServer::eventDPA(anna::diameter::comm::ServerSession *serverSession, const anna::DataBlock &message)
227 throw(anna::RuntimeException) {
228   LOGMETHOD(anna::TraceMethod tm("launcher::MyLocalServer", "eventDPA", ANNA_FILE_LOCATION));
229   // Performance stats:
230   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
231   // CommandId:
232   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
233   LOGDEBUG
234   (
235     std::string msg = "Disconnect-Peer-Answer response received from client: ";
236     msg += anna::diameter::functions::commandIdAsPairString(cid);
237     msg += " | DiameterServer: ";
238     msg += anna::functions::socketLiteralAsString(serverSession->getAddress(), serverSession->getPort());
239     msg += " | EventTime: ";
240     msg += anna::time::functions::currentTimeAsString();
241     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
242   );
243
244   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfc", serverSession->asString());
245 }