Fix local server for multiple applications
[anna.git] / source / diameter.comm / ClientSession.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 #include <anna/core/functions.hpp>
10 #include <anna/diameter/defines.hpp>
11 #include <anna/diameter/functions.hpp>
12 #include <anna/diameter/helpers/helpers.hpp>
13 #include <anna/diameter/codec/functions.hpp>
14 #include <anna/diameter/codec/Message.hpp>
15 #include <anna/diameter/helpers/base/functions.hpp>
16 #include <anna/time/functions.hpp>
17
18 #include <anna/diameter.comm/ClientSession.hpp>
19 #include <anna/diameter.comm/Engine.hpp>
20 #include <anna/diameter.comm/Entity.hpp>
21 #include <anna/diameter.comm/Server.hpp>
22 #include <anna/diameter.comm/Response.hpp>
23 #include <anna/diameter.comm/Message.hpp>
24 #include <anna/diameter.comm/OamModule.hpp>
25 #include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
26 #include <anna/diameter.comm/TimerManager.hpp>
27 #include <anna/diameter.comm/Timer.hpp>
28 #include <anna/diameter.comm/ClientSessionReceiver.hpp>
29 #include <anna/diameter.comm/ReceiverFactoryImpl.hpp>
30
31 #include <anna/comm/ClientSocket.hpp>
32 #include <anna/app/functions.hpp>
33 #include <anna/core/functions.hpp>
34 #include <anna/core/DataBlock.hpp>
35 #include <anna/core/tracing/Logger.hpp>
36 #include <anna/core/tracing/TraceMethod.hpp>
37 #include <anna/xml/Node.hpp>
38 #include <anna/timex/Engine.hpp>
39
40 // Standard
41 #include <stdlib.h> // rand()
42 #include <time.h>
43
44
45 using namespace std;
46 using namespace anna::diameter;
47 using namespace anna::diameter::comm;
48
49 //static
50 const anna::Millisecond ClientSession::DefaultWatchdogPeriod(30000); // Watchdog messages timeout
51
52
53 ClientSession::ClientSession() : Session("diameter::comm::ClientSession", "Diameter Keep-Alive Timer"),
54   a_receiverFactory(this),
55   a_cer(ClassCode::Bind),
56   a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
57 { initialize(); }
58
59
60 void ClientSession::initialize() {
61   Session::initialize();
62   a_autoRecovery = true;
63   a_parent = NULL;
64   a_server = NULL;
65   a_watchdogState = WatchdogState::TimerStopped;
66   a_hidden = false;
67 }
68
69 //ClientSession::~ClientSession() {;}
70
71
72 const std::string& ClientSession::getAddress() const {
73   return a_parent->getAddress();
74 }
75
76 int ClientSession::getPort() const {
77   return a_parent->getPort();
78 }
79
80
81 void ClientSession::setState(State::_v state) {
82   Session::setState(state);
83   // Inform father server (availability changes):
84   a_parent->refreshAvailability();
85 }
86
87 void ClientSession::bind() noexcept(false) {
88   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "bind", ANNA_FILE_LOCATION));
89
90   if(a_state != State::Closed)
91     return;
92
93   LOGDEBUG(
94     string msg("diameter::comm::ClientSession::bind | ");
95     msg += asString();
96     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
97   );
98
99   // Connection:
100   if(!a_server)
101     throw anna::RuntimeException("Server is not yet created", ANNA_FILE_LOCATION);
102
103   bool serverAvailable = a_server->isAvailable();
104   LOGDEBUG(
105
106     if(serverAvailable) anna::Logger::debug("Server AVAILABLE", ANNA_FILE_LOCATION);
107     else anna::Logger::debug("Server NOT AVAILABLE. Connecting ...", ANNA_FILE_LOCATION);
108     );
109
110   if(!serverAvailable) {
111     a_server->connect();
112     return;
113   }
114
115   // Some operations could be done before sending CER, for example non-standard Origin-Host manipulation for
116   // Tekelec PCRF
117   a_engine->bindingClientSession(this);
118
119   // OAM Lo comento, porque no se contabilizan los reintentos y por lo tanto no son muy �tiles.
120 //  OamModule &oamModule = OamModule::instantiate();
121 //  oamModule.count(a_server->isAvailable() ? OamModule::Counter::TCPConnectOK:OamModule::Counter::TCPConnectNOK);
122   // Application bind
123   if(send(&a_cer))
124     LOGDEBUG(anna::Logger::debug("CER sent to the server", ANNA_FILE_LOCATION));
125 }
126
127
128 const Response* ClientSession::send(const Message* message) noexcept(false) {
129   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
130
131   if(!message)
132     throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
133
134   // Command id:
135   bool isRequest;
136   diameter::CommandId cid = message->getCommandId(isRequest);
137   diameter::ApplicationId aid = message->getApplicationId();
138
139   LOGDEBUG(
140     std::string msg = "Sending diameter message: ";
141     msg += anna::diameter::functions::commandIdAsPairString(cid);
142     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
143   );
144
145   if(hidden()) {
146     if((cid.first != helpers::base::COMMANDID__Capabilities_Exchange_Request.first)  /* not CER/CEA */
147         && (cid.first != helpers::base::COMMANDID__Device_Watchdog_Request.first) /* not DWR/DWA */
148         && (cid.first != helpers::base::COMMANDID__Disconnect_Peer_Request.first)) { /* not DPR/DPA */
149       LOGDEBUG(
150         std::string msg(asString());
151         msg += " | Client-session hidden for application messages delivery";
152         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
153       );
154       return NULL;
155     }
156   }
157
158   // Checkings:
159   if((a_state == State::Closed) && (cid != helpers::base::COMMANDID__Capabilities_Exchange_Request)) {
160     string msg(asString());
161     msg += " | ClientSession::bind is not initiated";
162     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
163   }
164
165   if(a_state == State::WaitingBind) {
166     string msg(asString());
167     msg += " | Still waiting for bind ack (CEA)";
168     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
169   }
170
171   if(a_state == State::Failover) {
172     string msg(asString());
173     msg += " | Disabled sent on failover state";
174     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
175   }
176
177   if(a_state == State::Closing) {
178     string msg(asString());
179     msg += " | Disabled sent on closing state";
180     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
181   }
182
183   // Check states:
184   if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
185     if(a_state != State::Closed) {
186       string msg(asString());
187       msg += " | Discarding CER on not closed state";
188       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
189     }
190   } else if(cid ==  helpers::base::COMMANDID__Device_Watchdog_Request) {
191     if(a_state == State::WaitingDPA) {
192       string msg(asString());
193       msg += " | DWR is not sent on 'WaitingDPA' state";
194       //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
195       LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
196       return NULL;
197     }
198
199     if(a_state == State::Disconnecting) {
200       string msg(asString());
201       msg += " | DWR is not sent on 'Disconnecting' state";
202       //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
203       LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
204       return NULL;
205     }
206
207     if(a_state == State::Closing) {
208       string msg(asString());
209       msg += " | DWR is not sent on 'Closing' state";
210       //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
211       LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
212       return NULL;
213     }
214   } else if(cid ==  helpers::base::COMMANDID__Disconnect_Peer_Request) {
215     if(a_state == State::WaitingDPA) {
216       string msg(asString());
217       msg += " | Still waiting for DPR ack (DPA)";
218       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
219     }
220
221     if(a_state == State::Disconnecting) {
222       string msg(asString());
223       msg += " | Client disconnection has already been initiated";
224       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
225     }
226   } else {
227     if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
228       if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
229         LOGDEBUG(
230           string msg("diameter::comm::ClientSession::send | ");
231           msg += asString();
232           msg += " | Sents (request or answer) blocked to diameter server (disconnection in progress). Discarding ...";
233           anna::Logger::debug(msg, ANNA_FILE_LOCATION);
234         );
235         return NULL;
236       }
237     }
238   }
239
240   // Trace send operation:
241   LOGDEBUG(
242     string msg("diameter::comm::ClientSession::send | ");
243     msg += asString();
244     msg += " | ";
245     msg += message->asString();
246     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
247   );
248   bool fixed = false; // answers cannot be fixed
249   Message * message_nc = const_cast<Message*>(message);
250
251   if(isRequest) {
252     if(/* entity */getParent()->getParent()->isDeprecated()) {
253       string msg(asString());
254       msg += " | Parent entity is deprecated. Request send blocked.";
255       throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
256     }
257
258     // Fixing indicator:
259     fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd);
260     message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
261   }
262
263   // Send message
264   try {
265     message->send(*this);
266
267     // Next hop by hop & end to end identifiers:
268     if(isRequest) generateNextSequences();
269
270     //   Transaction state
271     //         The Diameter protocol requires that agents maintain transaction
272     //         state, which is used for failover purposes.  Transaction state
273     //         implies that upon forwarding a request, the Hop-by-Hop identifier
274     //         is saved; the field is replaced with a locally unique identifier,
275     //         which is restored to its original value when the corresponding
276     //         answer is received.  The request's state is released upon receipt
277     //         of the answer.  A stateless agent is one that only maintains
278     //         transaction state.
279     //
280     updateOutgoingActivityTime();
281     // OAM
282     countSendings(cid, aid, true /* send ok */);
283     // Trace non-application messages:
284     LOGDEBUG(
285       if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
286          (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
287          (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
288       anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
289       try {
290         anna::diameter::codec::Message msg(a_engine->getBaseProtocolCodecEngine()); msg.decode(message->getBody()); /* decode to be traced */
291       }
292       catch(anna::RuntimeException &ex) {
293         std::string msg = ex.getText();
294         msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
295         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
296       }
297     }
298     );
299
300     // Restore sequences:
301     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
302   } catch(anna::RuntimeException&) {
303     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
304
305     // OAM
306     countSendings(cid, aid, false /* send no ok */);
307     throw;
308   }
309
310   // Renew states:
311   if(cid ==  helpers::base::COMMANDID__Capabilities_Exchange_Request) {
312     setState(State::WaitingBind);
313   } else if(cid ==  helpers::base::COMMANDID__Disconnect_Peer_Request) {
314     LOGWARNING(anna::Logger::warning("DPR has been sent to the peer (diameter server)", ANNA_FILE_LOCATION));
315     setState(State::WaitingDPA);
316   }
317
318 //   else {
319 //      // No changes
320 //   }
321
322   // Answers are not temporized:
323   if(!isRequest) return NULL;
324
325   // Neither DWR:
326   if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
327     setWatchdogState(WatchdogState::WaitingDWA);
328     return NULL;
329   }
330
331   // Request will have context responses:
332   Response* result(NULL);
333   result = Response::instance(message->getClassCode(), a_nextHopByHop - 1 /* current request sent to server */);
334   result->setRequest(message);
335   response_add(result);
336   return result;
337 }
338
339 bool ClientSession::unbind(bool forceDisconnect)
340 noexcept(false) {
341   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
342
343   if(a_state == State::Closed)
344     return false;
345
346   // Client socket:
347   anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
348 //   LOGDEBUG(
349 //      string msg("Server to be unbound | ");
350 //      msg += a_server->asString();
351 //      anna::Logger::debug(msg, ANNA_FILE_LOCATION);
352 //   );
353
354   if(forceDisconnect) {
355     LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
356
357     if(cs) cs->requestClose();  // this will invoke finalize()
358
359     return true;
360   }
361
362   if(a_state == State::Disconnecting) {
363     LOGDEBUG(
364       string msg("diameter::comm::ClientSession::unbind | ");
365       msg += asString();
366       msg += " | Disconnection already in progress !";
367       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
368     );
369     return false;
370   }
371
372   if(a_state == State::Failover) {
373     LOGDEBUG(
374       string msg("diameter::comm::ClientSession::unbind | ");
375       msg += asString();
376       msg += " | Unbind on failover state. Disconnect now.";
377       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
378     );
379
380     if(cs) cs->requestClose();  // this will invoke finalize()
381
382     return true;
383   }
384
385   if(a_state == State::WaitingBind) {
386     LOGDEBUG(
387       string msg("diameter::comm::ClientSession::unbind | ");
388       msg += asString();
389       msg += " | Unbind on WaitingBind state. Disconnect now.";
390       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
391     );
392
393     if(cs) cs->requestClose();  // this will invoke finalize()
394
395     return true;
396   }
397
398   if(a_onDisconnect == OnDisconnect::IgnorePendings) {
399     LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
400
401     if(cs) cs->requestClose();  // this will invoke finalize()
402
403     return true;
404   }
405
406   if(getOTARequests() == 0) {  // No pendings
407     LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
408
409     if(cs) cs->requestClose();  // this will invoke finalize()
410
411     return true;
412   }
413
414   if(a_state == State::Closing) {
415     LOGDEBUG(
416       string msg("diameter::comm::ClientSession::unbind | ");
417       msg += asString();
418       msg += " | Closing already in progress (waiting pendings) !";
419       anna::Logger::debug(msg, ANNA_FILE_LOCATION);
420     );
421   }
422
423   return false;
424 }
425
426 void ClientSession::eventPeerShutdown() {
427   // Inform father server:
428   a_parent->eventPeerShutdown(this);
429 }
430
431 void ClientSession::eventRequestRetransmission(Message *request) {
432
433   // OAM
434   OamModule &oamModule = OamModule::instantiate();
435   oamModule.count(OamModule::Counter::RequestRetransmitted);
436   oamModule.count(OamModule::Counter::RequestRetransmittedOnClientSession);
437
438   // Inform father server:
439   a_parent->eventRequestRetransmission(this, request);
440 }
441
442 void ClientSession::eventResponse(const Response& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
443   // Inform father server:
444   a_parent->eventResponse(response, myNode);
445 }
446
447 void ClientSession::eventRequest(const anna::DataBlock &request, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
448   // Inform father server:
449   a_parent->eventRequest(this, request, myNode);
450 }
451
452 void ClientSession::eventUnknownResponse(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
453   // Inform father server:
454   a_parent->eventUnknownResponse(this, response, myNode);
455 }
456
457 void ClientSession::eventDPA(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
458   // Inform father server:
459   a_parent->eventDPA(this, response, myNode);
460 }
461
462
463
464 //------------------------------------------------------------------------------------------
465 // Se invoca desde el diameter::comm::Receiver
466 //------------------------------------------------------------------------------------------
467 void ClientSession::receive(const anna::comm::Message& message)
468 noexcept(false) {
469   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
470   // Activity:
471   updateIncomingActivityTime();
472   activateTimer();
473   // Command id:
474   const anna::DataBlock & db = message.getBody();
475   diameter::CommandId cid = codec::functions::getCommandId(db);
476   bool isRequest = cid.second;
477   LOGDEBUG(
478     std::string msg = "Received diameter message: ";
479     msg += anna::diameter::functions::commandIdAsPairString(cid);
480     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
481
482     if( (cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) ||
483         (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first)) {
484       try {
485         anna::diameter::codec::Message dmsg(a_engine->getBaseProtocolCodecEngine()); dmsg.decode(db); /* decode to be traced */
486       }
487       catch(anna::RuntimeException &ex) {
488         std::string msg = ex.getText();
489         msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
490         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
491       }
492     }
493 );
494
495   // Main counters:
496   OamModule &oamModule = OamModule::instantiate();
497   oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
498   oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnClientSession : OamModule::Counter::AnswerReceivedOnClientSession);
499   // Statistic (size)
500   a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize(), cid); // only on reception (application could manage sent sizes)
501
502   if(isRequest) {
503     /////////////////////////////
504     // Here received a request //
505     /////////////////////////////
506
507     // Received CER
508     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
509       LOGWARNING(anna::Logger::warning("Received CER: unexpected message at client-side", ANNA_FILE_LOCATION));
510       return;
511     }
512     // Received DWR
513     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
514 //         LOGWARNING(anna::Logger::warning("Received DWR: unexpected message at client-side", ANNA_FILE_LOCATION));
515 //         return;
516       // Non-usual but could happen:
517       oamModule.count(OamModule::Counter::DWRReceived);
518       sendDWAToServer(db /* DWR datablock received from server */);
519       return;
520     }
521     // Received DPR
522     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
523       oamModule.count(OamModule::Counter::DPRReceived);
524
525       if(a_state == State::Bound) {
526         setState(State::Disconnecting);
527         LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter server)", ANNA_FILE_LOCATION));
528
529         if(getOTARequests() == 0) sendDPA(a_engine, db);
530
531         return; // DPR won't be informed because virtual readDPA is available for this
532       }
533     }
534
535     try {
536       // application message counters
537       ApplicationMessageOamModule::instantiate().count(cid.first, -1 /* no result code */, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Request_Received_AsClient);
538
539       eventRequest(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
540     } catch(anna::RuntimeException& ex) {
541       ex.trace();
542     }
543
544     return;
545   }
546
547   /////////////////////////////
548   // Here received an answer //
549   /////////////////////////////
550   bool doUnbind = false;
551   bool immediateUnbind = false;
552   int resultCode = -1;
553
554   try {
555     resultCode = helpers::base::functions::getResultCode(db);
556   } catch(anna::RuntimeException& ex) {
557     // Not always mandatory ...
558     //ex.trace();
559   }
560
561   // Received CEA
562   if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
563     oamModule.count(OamModule::Counter::CEAReceived);
564
565     if(a_state != State::WaitingBind) {
566       LOGWARNING(anna::Logger::warning("Received CEA: unexpected message at not-WaitingBind state", ANNA_FILE_LOCATION));
567       return; // we don't send its request
568 //         string msg("diameter::comm::ClientSession::receive | ");
569 //         msg += asString();
570 //         msg += " | Received CEA on not-WaitingBind state: unexpected Bind-response";
571 //         throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
572     }
573
574     if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
575       LOGWARNING(
576         std::string msg = anna::functions::asString("Received CEA with non-success Result-Code (%d). Unbinding connection.", resultCode);
577         anna::Logger::warning(msg, ANNA_FILE_LOCATION);
578       );
579       doUnbind = true;
580     } else {
581       setState(State::Bound);
582       //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
583       // Inform father server (availability changes):
584       a_parent->refreshAvailability();
585       //startClock();
586     }
587   }
588   // Received DWA
589   else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {
590     oamModule.count(OamModule::Counter::DWAReceived);
591     setWatchdogState(WatchdogState::WaitingTimerExpiration);
592
593     if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS)
594       LOGWARNING(
595         std::string msg = anna::functions::asString("Received DWA with non-success Result-Code (%d)... but ASSUME keep-alive is reached", resultCode);
596         anna::Logger::warning(msg, ANNA_FILE_LOCATION);
597       );
598
599     if(a_state == State::Failover) {
600       setState(State::Bound);
601       LOGDEBUG(
602         string msg("diameter::comm::ClientSession::receive | ");
603         msg += asString();
604         msg += " | Received DWA on failover state: recovering Bound state";
605         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
606       );
607     }
608
609     // Keep-Alive don't manage context
610     return;
611   }
612   // Received DPA
613   else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
614     oamModule.count(OamModule::Counter::DPAReceived);
615
616     if(a_state == State::WaitingDPA) {
617       if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
618         LOGWARNING(
619           std::string msg = anna::functions::asString("Received DPA with non-success Result-Code (%d). Ignoring and recovering Bound state", resultCode);
620           anna::Logger::warning(msg, ANNA_FILE_LOCATION);
621         );
622         setState(State::Bound);
623       } else {
624         LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
625         immediateUnbind = true;
626         doUnbind = true;
627       }
628     }
629   }
630
631   HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
632   Response* response = response_find(hopByHop);
633
634   // Out-of-context responses:
635   if(!response) {
636     // OAM
637     oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
638     oamModule.count(OamModule::Counter::AnswerReceivedOnClientSessionUnknown);
639     oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnClientSessionUnknown);
640
641     // application message counters
642     ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_UnknownReceived_AsClient);
643
644     eventUnknownResponse(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
645     string msg(asString());
646     msg += anna::functions::asString(" | Response received from entity, for non registered context (HopByHop: %u)", hopByHop);
647     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
648   }
649
650   response->setResultCode(Response::ResultCode::Success);
651   response->cancelTimer();
652   LOGDEBUG(
653     string msg("diameter::comm::ClientSession::receive | ");
654     msg += asString();
655     msg += " | Received answer";
656     msg += response->asString();
657     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
658   );
659   // Statistics
660   anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
661   anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
662   anna::Millisecond timeToAnswerMs = current - request;
663   a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs, cid);
664   //LOGDEBUG
665   //(
666   //  std::string msg = "This diameter request context lasted ";
667   //  msg += anna::functions::asString(timeToAnswerMs);
668   //  msg += " milliseconds at diameter server (included network time)";
669   //  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
670   //);
671   // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
672   Message * requestMessage = const_cast<Message*>(response->getRequest());
673   requestMessage->setRequestServerSessionKey(response->getRequest()->getRequestServerSessionKey()); // -1 means unkown/unset
674
675   if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
676     try {
677       response->setMessage(&db);
678       // Restore received datablock
679       LOGDEBUG(
680         string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
681         msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
682         msg += ", end-to-end = ";
683         msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
684         msg += ")";
685         anna::Logger::debug(msg, ANNA_FILE_LOCATION);
686       );
687       diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
688       diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
689
690       // application message counters
691       if(cid != helpers::base::COMMANDID__Capabilities_Exchange_Answer)
692         ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_Received_AsClient);
693
694       eventResponse(*response, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
695     } catch(anna::RuntimeException& ex) {
696       ex.trace();
697     }
698   }
699   else { // DPA
700     // unbind is automatically performed, anyway we can inform to the application just in case some additional
701     //  procedure could be issued:
702     eventDPA(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
703   }
704
705   response_erase(response);
706
707   // Unbind trigger
708   if(doUnbind)
709     unbind(immediateUnbind);
710 }
711
712 void ClientSession::finalize() {
713   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
714   Session::finalize();
715   // Inform father server (availability changes):
716   a_parent->refreshAvailability();
717   // OAM
718   const Server *server = getParent();
719   bool multipleConnections = (server->getMaxClientSessions() > 1);
720   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
721   OamModule &oamModule = OamModule::instantiate();
722
723   if(multipleConnections) {
724     oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
725     oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
726   } else {
727     oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
728     oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
729   }
730 }
731
732 void ClientSession::recover() {
733   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "recover", ANNA_FILE_LOCATION));
734
735   try {
736     bind();
737   } catch(anna::RuntimeException &ex) {
738     // Again:
739     anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
740
741     if(cs) cs->requestClose();
742
743     ex.trace();
744   }
745
746   // Inform father server (availability changes):
747   a_parent->refreshAvailability();
748   // OAM
749   const Server *server = getParent();
750   bool multipleConnections = (server->getMaxClientSessions() > 1);
751   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
752   OamModule &oamModule = OamModule::instantiate();
753
754   if(multipleConnections) {
755     oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
756     oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
757   } else {
758     oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
759     oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
760   }
761 }
762
763 void ClientSession::sendDWAToServer(const anna::DataBlock& dwrDB)
764 noexcept(false) {
765   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWAToServer", ANNA_FILE_LOCATION));
766   anna::DataBlock dwa(true);
767   a_engine->readDWA(dwa, dwrDB); // Asume that DWA is valid ...
768
769   if(dwa.isEmpty())
770     throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote server never will validate this connection health", ANNA_FILE_LOCATION);
771
772   Message msgDwa;
773   msgDwa.setBody(dwa);
774   send(&msgDwa);
775 }
776
777 //-------------------------------------------------------------------------
778 // Se invoca desde diameter::comm::Timer
779 //-------------------------------------------------------------------------
780 void ClientSession::expireResponse(diameter::comm::Response* response)
781 {
782   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
783   Session::expireResponse(response);
784   // OAM
785   OamModule &oamModule = OamModule::instantiate();
786   oamModule.count(OamModule::Counter::RequestSentExpired);
787   oamModule.count(OamModule::Counter::RequestSentOnClientSessionExpired);
788   oamModule.activateAlarm(OamModule::Alarm::RequestSentOnClientSessionExpired);
789
790   // Check father server idleness:
791   if(idle()) a_parent->childIdle();
792
793 //   if (idle()) {
794 //      LOGDEBUG(anna::Logger::debug("ClientSession is idle after an expiration...", ANNA_FILE_LOCATION));
795 //      a_parent->childIdle();
796 //   }
797 //   else {
798 //      LOGDEBUG(anna::Logger::debug("ClientSession is busy after an expiration...", ANNA_FILE_LOCATION));
799 //   }
800 }
801
802
803 std::string ClientSession::asString() const
804 {
805   string result = Session::asString();
806   result += " | Parent Server: ";
807   result += anna::functions::socketLiteralAsString(getAddress(), getPort());
808   result += " | Auto-recovery: ";
809   result += (a_autoRecovery ? "yes" : "no");
810   result += " | WatchdogState: ";
811   result += asText(a_watchdogState);
812   // Diferente del timeout de ApplicationMessage:
813   result += " | Watchdog Period: ";
814   result += getTimeout().asString();
815   result += " | Hidden: ";
816   result += (hidden() ? "yes" : "no");
817
818   if(a_server) {
819     result += " | MaxConnectionDelay: ";
820     result += a_server->getMaxConnectionDelay().asString();
821   }
822
823   return result += " }";
824 }
825
826 anna::xml::Node* ClientSession::asXML(anna::xml::Node* parent) const
827 {
828   anna::xml::Node* result = Session::asXML(parent);
829   parent->createChild("diameter.comm.ClientSession");
830   result->createAttribute("ParentServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
831   result->createAttribute("AutoRecovery", (a_autoRecovery ? "yes" : "no"));
832   result->createAttribute("WatchdogState", asText(a_watchdogState));
833   // Diferente del timeout de ApplicationMessage:
834   result->createAttribute("WatchdogPeriod", getTimeout().asString());
835
836   if(a_server) result->createAttribute("MaxConnectionDelay", a_server->getMaxConnectionDelay().asString());
837
838   result->createAttribute("Hidden", hidden() ? "yes" : "no");
839   return result;
840 }
841
842
843 const char* ClientSession::asText(const WatchdogState::_v watchdogState)
844 {
845   static const char* text [] = { "TimerStopped", "WaitingTimerExpiration", "WaitingDWA" };
846   return text [watchdogState];
847 }
848
849
850 //------------------------------------------------------------------------------
851 //------------------------------------------------------ ClientSession::expire()
852 //------------------------------------------------------------------------------
853 void ClientSession::expire(anna::timex::Engine *timeController) noexcept(false) {
854   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (watchdog timer)", ANNA_FILE_LOCATION));
855
856   //   The client MUST NOT close the primary connection until the
857   //    primary's watchdog timer has expired at least twice without a
858   //    response (note that the watchdog is not sent a second time,
859   //    however).
860   if(a_watchdogState == WatchdogState::WaitingDWA) {
861     if(a_state == State::Failover) {
862       LOGWARNING(anna::Logger::warning("Unbinding client-session: Tw expired after first DWA missing (2*Tw elapsed)", ANNA_FILE_LOCATION));
863       unbind();
864       return; // finalize will stop the stopped timer ...
865     }
866
867     setState(State::Failover);
868     LOGWARNING(anna::Logger::warning("Going to Failover state: first DWA missing", ANNA_FILE_LOCATION));
869     activateTimer(); // another chance on failover
870     return;
871   }
872
873   // WaitingTimerExpiration arrives here:
874   const Response* sent;
875
876   try {
877     sent = send(&a_dwr);
878   } catch(anna::RuntimeException&) {
879     LOGDEBUG(anna::Logger::debug("Failed to send DWR to the server: unbinding ...", ANNA_FILE_LOCATION));
880     setState(State::Failover);
881     unbind();
882     throw;
883   }
884
885   LOGDEBUG(if(sent) anna::Logger::debug("DWR sent to the server", ANNA_FILE_LOCATION););
886
887   activateTimer();
888 }
889
890 void ClientSession::setWatchdogPeriod(const anna::Millisecond & watchdogPeriod) {
891   setTimeout(watchdogPeriod);
892 }
893
894 void ClientSession::setWatchdogState(WatchdogState::_v wState) {
895   LOGDEBUG(
896
897   if(wState != a_watchdogState) {
898   std::string msg("Session watchdog state change: ");
899     msg += asText(a_watchdogState);
900     msg += " -> ";
901     msg += asText(wState);
902     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
903   }
904   );
905   a_watchdogState = wState;
906 }
907
908
909 void ClientSession::timerStopped() {
910   LOGDEBUG(anna::Logger::debug("Watchdog timer stopped", ANNA_FILE_LOCATION));
911   setWatchdogState(WatchdogState::TimerStopped);
912 }
913
914 void ClientSession::timerStarted() {
915   LOGDEBUG(anna::Logger::debug("Watchdog timer started", ANNA_FILE_LOCATION));
916
917   if(a_watchdogState == WatchdogState::WaitingDWA) return;
918
919   setWatchdogState(WatchdogState::WaitingTimerExpiration);
920 }
921
922
923 //------------------------------------------------------------------------------
924 //---------------------------------- ClientSession::updateIncomingActivityTime()
925 //------------------------------------------------------------------------------
926 void ClientSession::updateIncomingActivityTime() {
927   Session::updateIncomingActivityTime();
928   a_parent->updateIncomingActivityTime();
929 }
930
931
932 //------------------------------------------------------------------------------
933 //---------------------------------- ClientSession::updateOutgoingActivityTime()
934 //------------------------------------------------------------------------------
935 void ClientSession::updateOutgoingActivityTime(void) {
936   Session::updateOutgoingActivityTime();
937   a_parent->updateOutgoingActivityTime();
938 }
939
940
941
942 //------------------------------------------------------------------------------
943 //----------------------------------------------- ClientSession::countSendings()
944 //------------------------------------------------------------------------------
945 void ClientSession::countSendings(const diameter::CommandId & cid, unsigned int aid, bool ok){
946   OamModule &oamModule = OamModule::instantiate();
947   ApplicationMessageOamModule &appMsgOamModule = ApplicationMessageOamModule::instantiate();
948
949   bool isRequest = cid.second;
950
951   if(ok) {
952     // Main counters:
953     oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
954     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionOK : OamModule::Counter::AnswerSentOnClientSessionOK);
955
956     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentOK);
957     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);
958     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK);  // not usual (dwr was received from server)
959     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
960     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
961     // Application messages:
962     else {
963       appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentOK_AsClient : ApplicationMessageOamModule::Counter::Answer_SentOK_AsClient);
964     }
965   } else {
966     // Main counters:
967     oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
968     oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionNOK : OamModule::Counter::AnswerSentOnClientSessionNOK);
969
970     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentNOK);
971     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);
972     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK);  // not usual (dwr was received from server)
973     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
974     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);
975     // Application messages:
976     else {
977       appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentNOK_AsClient : ApplicationMessageOamModule::Counter::Answer_SentNOK_AsClient);
978     }
979   }
980 }
981