Multistack launcher
[anna.git] / example / diameter / launcher / MyDiameterEntity.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.comm/Response.hpp>
14 #include <anna/diameter/helpers/base/functions.hpp>
15 #include <anna/diameter/helpers/dcca/functions.hpp>
16
17 // Process
18 #include "MyDiameterEntity.hpp"
19 #include "Launcher.hpp"
20
21
22 void MyDiameterEntity::eventRequestRetransmission(const anna::diameter::comm::ClientSession* clientSession, anna::diameter::comm::Message *request) throw() {
23
24   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequestRetransmission", ANNA_FILE_LOCATION));
25
26   // Base class:
27   Entity::eventRequestRetransmission(clientSession, request); // warning trace
28
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(request->getBody());
34   LOGDEBUG
35   (
36     std::string msg = "Request retransmitted: ";
37     msg += anna::diameter::functions::commandIdAsPairString(cid);
38     msg += " | DiameterServer: ";
39     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
40     msg += " | EventTime: ";
41     msg += anna::time::functions::currentTimeAsString();
42     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
43   );
44
45   // Write retransmission
46   if(my_app.logEnabled()) my_app.writeLogFile(request->getBody(), "retry", clientSession->asString(), a_codecEngine);
47 }
48
49
50 int MyDiameterEntity::readSocketId(const anna::diameter::comm::Message* message, int maxClientSessions) const throw() {
51   CommandLine& cl(anna::CommandLine::instantiate());
52   std::string sessionBasedModelsType = (cl.exists("sessionBasedModelsClientSocketSelection") ? cl.getValue("sessionBasedModelsClientSocketSelection") : "SessionIdLowPart");
53
54   if(sessionBasedModelsType == "RoundRobin") return -1;  // IEC also would return -1
55
56   try {
57     // Service-Context-Id:
58     anna::diameter::helpers::dcca::ChargingContext::_v chargingContext;
59     std::string scid = anna::diameter::helpers::dcca::functions::getServiceContextId(message->getBody(), chargingContext);
60
61     switch(chargingContext) {
62     case anna::diameter::helpers::dcca::ChargingContext::Data:
63     case anna::diameter::helpers::dcca::ChargingContext::Voice:
64     case anna::diameter::helpers::dcca::ChargingContext::Content: {
65       // Session-Id: '<DiameterIdentity>;<high 32 bits>;<low 32 bits>[;<optional value>="">]'
66       std::string sid = anna::diameter::helpers::base::functions::getSessionId(message->getBody());
67       std::string diameterIdentity, optional;
68       anna::U32 high, low;
69       anna::diameter::helpers::base::functions::decodeSessionId(sid, diameterIdentity, high, low /* context-teid */, optional);
70
71       if(sessionBasedModelsType == "SessionIdLowPart") return (low % maxClientSessions);
72
73       if(sessionBasedModelsType == "SessionIdHighPart") return (high % maxClientSessions);
74
75       if(sessionBasedModelsType == "SessionIdOptionalPart") return (atoi(optional.c_str()) % maxClientSessions);
76     }
77     case anna::diameter::helpers::dcca::ChargingContext::SMS:
78     case anna::diameter::helpers::dcca::ChargingContext::MMS:
79     case anna::diameter::helpers::dcca::ChargingContext::Unknown:
80     default:
81        return -1; // IEC model and Unknown traffic types
82     }
83   } catch(anna::RuntimeException &ex) {
84     LOGDEBUG(
85       std::string msg = ex.getText();
86       msg += " | Round-robin between sessions will be used to send";
87       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
88     );
89   }
90
91   return -1;
92 }
93
94 void MyDiameterEntity::eventRequest(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
95 throw(anna::RuntimeException) {
96   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventRequest", ANNA_FILE_LOCATION));
97   // Performance stats:
98   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
99   CommandLine& cl(anna::CommandLine::instantiate());
100   // CommandId:
101   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
102   LOGDEBUG
103   (
104     std::string msg = "Request received: ";
105     msg += anna::diameter::functions::commandIdAsPairString(cid);
106     msg += " | DiameterServer: ";
107     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
108     msg += " | EventTime: ";
109     msg += anna::time::functions::currentTimeAsString();
110     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
111   );
112
113   // Write reception
114   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfe", clientSession->asString(), a_codecEngine);
115
116   // Lookup reacting answers list:
117   int code = cid.first;
118   anna::diameter::codec::Message *answer_message = a_reactingAnswers.getMessage(code);
119   if (answer_message) {
120     // Prepare answer:
121     my_app.getCommunicator()->prepareAnswer(answer_message, message);
122
123     try {
124       anna::diameter::comm::Message *msg = my_app.createCommMessage();
125       msg->setBody(answer_message->code());
126       /* response = NULL =*/clientSession->send(msg);
127       my_app.releaseCommMessage(msg);
128
129       if(my_app.logEnabled()) my_app.writeLogFile(*answer_message, "sent2e", clientSession->asString());
130     } catch(anna::RuntimeException &ex) {
131       ex.trace();
132
133       if(my_app.logEnabled()) my_app.writeLogFile(*answer_message, "send2eError", clientSession->asString());
134     }
135
136     // Pop front the reacting answer:
137     a_reactingAnswers.nextMessage(code);
138     return;
139   }
140
141   LOGDEBUG
142   (
143     std::string msg = "No answers programmed (maybe sold out) for request coming from entity: ";
144     msg += anna::diameter::functions::commandIdAsPairString(cid);
145     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
146   );
147
148   // not found: forward to client (if exists)
149   // Forward to client:
150   anna::diameter::comm::LocalServer *localServer = my_app.getDiameterLocalServer();
151
152   if(localServer && (cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CER */) {
153     try {
154       anna::diameter::comm::Message *msg = my_app.createCommMessage();
155       msg->forwardEndToEnd(); // end-to-end will be kept
156       msg->setBody(message);
157       msg->setRequestClientSessionKey(clientSession->getKey());
158       bool success = localServer->send(msg);
159
160       // Detailed log:
161       if(my_app.logEnabled()) {
162         anna::diameter::comm::ServerSession *usedServerSession = localServer->getLastUsedResource();
163         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
164         my_app.writeLogFile(message, (success ? "fwd2c" : "fwd2cError"), detail, a_codecEngine);
165       }
166     } catch(anna::RuntimeException &ex) {
167       ex.trace();
168     }
169   }
170 }
171
172 void MyDiameterEntity::eventResponse(const anna::diameter::comm::Response &response)
173 throw(anna::RuntimeException) {
174   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventResponse", ANNA_FILE_LOCATION));
175   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
176   CommandLine& cl(anna::CommandLine::instantiate());
177   anna::diameter::comm::ClassCode::_v code = response.getClassCode();
178   anna::diameter::comm::Response::ResultCode::_v result = response.getResultCode();
179   anna::diameter::comm::Message* request = const_cast<anna::diameter::comm::Message*>(response.getRequest());
180   const anna::DataBlock* message = response.getMessage();
181   const anna::diameter::comm::ClientSession *clientSession = static_cast<const anna::diameter::comm::ClientSession *>(response.getSession());
182   bool isBindResponse = (code == anna::diameter::comm::ClassCode::Bind);
183   bool isApplicationMessage = (code == anna::diameter::comm::ClassCode::ApplicationMessage);
184   bool contextExpired = (result == anna::diameter::comm::Response::ResultCode::Timeout);
185   bool isUnavailable = (result == anna::diameter::comm::Response::ResultCode::DiameterUnavailable);
186   bool isOK = (result == anna::diameter::comm::Response::ResultCode::Success);
187   // CommandId:
188   anna::diameter::CommandId request_cid = request->getCommandId();
189   LOGDEBUG
190   (
191     std::string msg = "Response received for original diameter request: ";
192     msg += anna::diameter::functions::commandIdAsPairString(request_cid);
193     msg += " | Response: ";
194     msg += response.asString();
195     msg += " | DiameterServer: ";
196     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
197     msg += " | EventTime: ";
198     msg += anna::time::functions::currentTimeAsString();
199     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
200   );
201
202   if(isUnavailable) {
203     //if (isApplicationMessage)
204     LOGWARNING(anna::Logger::warning("Diameter entity unavailable for Diameter Request", ANNA_FILE_LOCATION));
205   }
206
207   if(contextExpired) {
208     //if (isApplicationMessage)
209     LOGWARNING(anna::Logger::warning("Context Expired for Diameter Request which was sent to the entity", ANNA_FILE_LOCATION));
210
211     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
212       if(my_app.logEnabled()) my_app.writeLogFile(*request, "req2e-expired", clientSession->asString(), a_codecEngine);
213     }
214   }
215
216   if(isOK) {
217     LOGDEBUG(
218       std::string msg = "Received response for diameter message:  ";
219       msg += anna::diameter::functions::commandIdAsPairString(request_cid);
220       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
221     );
222
223     // Write reception
224     if(request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) {  // don't trace CEA
225       if(my_app.logEnabled()) {
226         my_app.writeLogFile(*message, "recvfe", clientSession->asString(), a_codecEngine);
227       }
228     }
229
230     // Forward to client:
231     anna::diameter::comm::LocalServer *localServer = my_app.getDiameterLocalServer();
232
233     if(localServer && (request_cid != anna::diameter::helpers::base::COMMANDID__Capabilities_Exchange_Request) /* don't forward CEA */) {
234       try {
235         anna::diameter::comm::Message *msg = my_app.createCommMessage();
236         msg->forwardEndToEnd(); // end-to-end will be kept
237         msg->setBody(*message);
238         bool success = localServer->send(msg, request->getRequestServerSessionKey());
239         my_app.releaseCommMessage(msg);
240         my_app.releaseCommMessage(request);
241
242         // Detailed log:
243         anna::diameter::comm::ServerSession *usedServerSession = my_app.getMyDiameterEngine()->findServerSession(request->getRequestServerSessionKey());
244         std::string detail = usedServerSession ? usedServerSession->asString() : "<null server session>"; // esto no deberia ocurrir
245
246         if(my_app.logEnabled()) {
247           my_app.writeLogFile(*message, (success ? "fwd2c" : "fwd2cError"), detail, a_codecEngine);
248         }
249       } catch(anna::RuntimeException &ex) {
250         ex.trace();
251       }
252     }
253   }
254
255   // Triggering burst:
256   if(isOK || contextExpired) my_app.sendBurstMessage();
257 }
258
259 void MyDiameterEntity::eventUnknownResponse(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
260 throw(anna::RuntimeException) {
261   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventUnknownResponse", ANNA_FILE_LOCATION));
262   // Performance stats:
263   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
264   // CommandId:
265   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
266   LOGDEBUG
267   (
268     std::string msg = "Out-of-context response received from entity: ";
269     msg += anna::diameter::functions::commandIdAsPairString(cid);
270     msg += " | DiameterServer: ";
271     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
272     msg += " | EventTime: ";
273     msg += anna::time::functions::currentTimeAsString();
274     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
275   );
276
277   // Write reception
278   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfe-ans-unknown", clientSession->asString(), a_codecEngine);
279 }
280
281 void MyDiameterEntity::eventDPA(anna::diameter::comm::ClientSession *clientSession, const anna::DataBlock &message)
282 throw(anna::RuntimeException) {
283   LOGMETHOD(anna::TraceMethod tm("launcher::MyDiameterEntity", "eventDPA", ANNA_FILE_LOCATION));
284   // Performance stats:
285   Launcher& my_app = static_cast <Launcher&>(anna::app::functions::getApp());
286   // CommandId:
287   anna::diameter::CommandId cid = anna::diameter::codec::functions::getCommandId(message);
288   LOGDEBUG
289   (
290     std::string msg = "Disconnect-Peer-Answer received from entity: ";
291     msg += anna::diameter::functions::commandIdAsPairString(cid);
292     msg += " | DiameterServer: ";
293     msg += anna::functions::socketLiteralAsString(clientSession->getAddress(), clientSession->getPort());
294     msg += " | EventTime: ";
295     msg += anna::time::functions::currentTimeAsString();
296     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
297   );
298
299   // Write reception
300   if(my_app.logEnabled()) my_app.writeLogFile(message, "recvfe", clientSession->asString(), a_codecEngine);
301 }