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