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