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