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