1 // ANNA - Anna is Not Nothingness Anymore //
3 // (c) Copyright 2005-2015 Eduardo Ramos Testillano & Francisco Ruiz Rayo //
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 //
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>
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/TimerManager.hpp>
26 #include <anna/diameter.comm/Timer.hpp>
27 #include <anna/diameter.comm/ClientSessionReceiver.hpp>
28 #include <anna/diameter.comm/ReceiverFactoryImpl.hpp>
30 #include <anna/comm/ClientSocket.hpp>
31 #include <anna/app/functions.hpp>
32 #include <anna/core/functions.hpp>
33 #include <anna/core/DataBlock.hpp>
34 #include <anna/core/tracing/Logger.hpp>
35 #include <anna/core/tracing/TraceMethod.hpp>
36 #include <anna/xml/Node.hpp>
37 #include <anna/timex/Engine.hpp>
40 #include <stdlib.h> // rand()
46 using namespace anna::diameter;
47 using namespace anna::diameter::comm;
50 const anna::Millisecond ClientSession::DefaultWatchdogPeriod(30000); // Watchdog messages timeout
53 ClientSession::ClientSession() : Session("diameter::comm::ClientSession", "Diameter Keep-Alive Timer"),
54 a_receiverFactory(this),
55 a_cer(ClassCode::Bind),
56 a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
60 void ClientSession::initialize() throw() {
61 Session::initialize();
62 a_autoRecovery = true;
65 a_watchdogState = WatchdogState::TimerStopped;
69 //ClientSession::~ClientSession() {;}
72 const std::string& ClientSession::getAddress() const throw() {
73 return a_parent->getAddress();
76 int ClientSession::getPort() const throw() {
77 return a_parent->getPort();
81 void ClientSession::setState(State::_v state) throw() {
82 Session::setState(state);
83 // Inform father server (availability changes):
84 bool changes = a_parent->refreshAvailability();
87 void ClientSession::bind() throw(anna::RuntimeException) {
88 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "bind", ANNA_FILE_LOCATION));
90 if(a_state != State::Closed)
94 string msg("diameter::comm::ClientSession::bind | ");
96 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
101 throw anna::RuntimeException("Server is not yet created", ANNA_FILE_LOCATION);
103 bool serverAvailable = a_server->isAvailable();
106 if(serverAvailable) anna::Logger::debug("Server AVAILABLE", ANNA_FILE_LOCATION);
107 else anna::Logger::debug("Server NOT AVAILABLE. Connecting ...", ANNA_FILE_LOCATION);
110 if(!serverAvailable) {
115 // Some operations could be done before sending CER, for example non-standard Origin-Host manipulation for
117 a_engine->bindingClientSession(this);
119 // OAM Lo comento, porque no se contabilizan los reintentos y por lo tanto no son muy Ăștiles.
120 // OamModule &oamModule = OamModule::instantiate();
121 // oamModule.count(a_server->isAvailable() ? OamModule::Counter::TCPConnectOK:OamModule::Counter::TCPConnectNOK);
124 LOGDEBUG(anna::Logger::debug("CER sent to the server", ANNA_FILE_LOCATION));
128 void ClientSession::setCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
129 if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
130 throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
133 if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
134 throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
137 // La verificacion ya se hace implicitamente antes
138 // if ((a_cer.isEmpty()) || (a_dwr.isEmpty())) {
139 // LOGDEBUG (anna::Logger::debug ("Must define valid CER and DWR messages before use bind !", ANNA_FILE_LOCATION));
147 const Response* ClientSession::send(const Message* message) throw(anna::RuntimeException) {
148 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
151 throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
155 diameter::CommandId cid = message->getCommandId(isRequest);
157 std::string msg = "Sending diameter message: ";
158 msg += anna::diameter::functions::commandIdAsPairString(cid);
159 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
163 if((cid.first != helpers::base::COMMANDID__Capabilities_Exchange_Request.first) /* not CER/CEA */
164 && (cid.first != helpers::base::COMMANDID__Device_Watchdog_Request.first) /* not DWR/DWA */
165 && (cid.first != helpers::base::COMMANDID__Disconnect_Peer_Request.first)) { /* not DPR/DPA */
167 std::string msg(asString());
168 msg += " | Client-session hidden for application messages delivery";
169 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
176 if((a_state == State::Closed) && (cid != helpers::base::COMMANDID__Capabilities_Exchange_Request)) {
177 string msg(asString());
178 msg += " | ClientSession::bind is not initiated";
179 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
182 if(a_state == State::WaitingBind) {
183 string msg(asString());
184 msg += " | Still waiting for bind ack (CEA)";
185 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
188 if(a_state == State::Failover) {
189 string msg(asString());
190 msg += " | Disabled sent on failover state";
191 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
194 if(a_state == State::Closing) {
195 string msg(asString());
196 msg += " | Disabled sent on closing state";
197 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
201 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
202 if(a_state != State::Closed) {
203 string msg(asString());
204 msg += " | Discarding CER on not closed state";
205 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
207 } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
208 if(a_state == State::WaitingDPA) {
209 string msg(asString());
210 msg += " | DWR is not sent on 'WaitingDPA' state";
211 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
212 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
216 if(a_state == State::Disconnecting) {
217 string msg(asString());
218 msg += " | DWR is not sent on 'Disconnecting' state";
219 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
220 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
224 if(a_state == State::Closing) {
225 string msg(asString());
226 msg += " | DWR is not sent on 'Closing' state";
227 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
228 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
231 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
232 if(a_state == State::WaitingDPA) {
233 string msg(asString());
234 msg += " | Still waiting for DPR ack (DPA)";
235 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
238 if(a_state == State::Disconnecting) {
239 string msg(asString());
240 msg += " | Client disconnection has already been initiated";
241 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
244 if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
245 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
247 string msg("diameter::comm::ClientSession::send | ");
249 msg += " | Sents (request or answer) blocked to diameter server (disconnection in progress). Discarding ...";
250 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
257 // Trace send operation:
259 string msg("diameter::comm::ClientSession::send | ");
262 msg += message->asString();
263 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
265 bool fixed = false; // answers cannot be fixed
266 Message * message_nc = const_cast<Message*>(message);
269 if(/* entity */getParent()->getParent()->isDeprecated()) {
270 string msg(asString());
271 msg += " | Parent entity is deprecated. Request send blocked.";
272 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
276 fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd, a_engine->getFreezeEndToEndOnSending());
277 message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
282 message->send(*this);
284 // Next hop by hop & end to end identifiers:
285 if(isRequest) generateNextSequences();
288 // The Diameter protocol requires that agents maintain transaction
289 // state, which is used for failover purposes. Transaction state
290 // implies that upon forwarding a request, the Hop-by-Hop identifier
291 // is saved; the field is replaced with a locally unique identifier,
292 // which is restored to its original value when the corresponding
293 // answer is received. The request's state is released upon receipt
294 // of the answer. A stateless agent is one that only maintains
295 // transaction state.
297 updateOutgoingActivityTime();
299 countSendings(cid, true /* send ok */);
300 // Trace non-application messages:
303 if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
304 (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
305 (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
306 anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
307 try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
311 // Restore sequences:
312 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
313 } catch(anna::RuntimeException&) {
314 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
317 countSendings(cid, false /* send no ok */);
322 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
323 setState(State::WaitingBind);
324 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
325 LOGWARNING(anna::Logger::warning("DPR has been sent to the peer (diameter server)", ANNA_FILE_LOCATION));
326 setState(State::WaitingDPA);
333 // Answers are not temporized:
334 if(!isRequest) return NULL;
337 if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
338 setWatchdogState(WatchdogState::WaitingDWA);
342 // Request will have context responses:
343 Response* result(NULL);
344 result = Response::instance(message->getClassCode(), a_nextHopByHop - 1 /* current request sent to server */);
345 result->setRequest(message);
346 response_add(result);
350 bool ClientSession::unbind(bool forceDisconnect)
351 throw(anna::RuntimeException) {
352 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
354 if(a_state == State::Closed)
358 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
360 // string msg("Server to be unbound | ");
361 // msg += a_server->asString();
362 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
365 if(forceDisconnect) {
366 LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
368 if(cs) cs->requestClose(); // this will invoke finalize()
373 if(a_state == State::Disconnecting) {
375 string msg("diameter::comm::ClientSession::unbind | ");
377 msg += " | Disconnection already in progress !";
378 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
383 if(a_state == State::Failover) {
385 string msg("diameter::comm::ClientSession::unbind | ");
387 msg += " | Unbind on failover state. Disconnect now.";
388 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
391 if(cs) cs->requestClose(); // this will invoke finalize()
396 if(a_state == State::WaitingBind) {
398 string msg("diameter::comm::ClientSession::unbind | ");
400 msg += " | Unbind on WaitingBind state. Disconnect now.";
401 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
404 if(cs) cs->requestClose(); // this will invoke finalize()
409 if(a_onDisconnect == OnDisconnect::IgnorePendings) {
410 LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
412 if(cs) cs->requestClose(); // this will invoke finalize()
417 if(getOTARequests() == 0) { // No pendings
418 LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
420 if(cs) cs->requestClose(); // this will invoke finalize()
425 if(a_state == State::Closing) {
427 string msg("diameter::comm::ClientSession::unbind | ");
429 msg += " | Closing already in progress (waiting pendings) !";
430 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
437 void ClientSession::eventPeerShutdown() throw() {
438 // Inform father server:
439 a_parent->eventPeerShutdown(this);
442 void ClientSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
443 // Inform father server:
444 a_parent->eventResponse(response);
447 void ClientSession::eventRequest(const anna::DataBlock &request) throw(anna::RuntimeException) {
448 // Inform father server:
449 a_parent->eventRequest(this, request);
452 void ClientSession::eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) {
453 // Inform father server:
454 a_parent->eventUnknownResponse(this, response);
457 void ClientSession::eventDPA(const anna::DataBlock& response) throw(anna::RuntimeException) {
458 // Inform father server:
459 a_parent->eventDPA(this, response);
464 //------------------------------------------------------------------------------------------
465 // Se invoca desde el diameter::comm::Receiver
466 //------------------------------------------------------------------------------------------
467 void ClientSession::receive(const anna::comm::Message& message)
468 throw(anna::RuntimeException) {
469 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
471 updateIncomingActivityTime();
474 const anna::DataBlock & db = message.getBody();
475 diameter::CommandId cid = codec::functions::getCommandId(db);
476 bool isRequest = cid.second;
478 std::string msg = "Received diameter message: ";
479 msg += anna::diameter::functions::commandIdAsPairString(cid);
480 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
482 if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
483 try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
486 OamModule &oamModule = OamModule::instantiate();
487 oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
488 oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnClientSession : OamModule::Counter::AnswerReceivedOnClientSession);
490 a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
493 /////////////////////////////
494 // Here received a request //
495 /////////////////////////////
498 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
499 LOGWARNING(anna::Logger::warning("Received CER: unexpected message at client-side", ANNA_FILE_LOCATION));
503 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
504 // LOGWARNING(anna::Logger::warning("Received DWR: unexpected message at client-side", ANNA_FILE_LOCATION));
506 // Non-usual but could happen:
507 oamModule.count(OamModule::Counter::DWRReceived);
508 sendDWAToServer(db /* DWR datablock received from server */);
512 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
513 oamModule.count(OamModule::Counter::DPRReceived);
515 if(a_state == State::Bound) {
517 setState(State::Disconnecting);
518 LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter server)", ANNA_FILE_LOCATION));
520 if(getOTARequests() == 0) sendDPA();
522 return; // DPR won't be informed because virtual readDPA is available for this
528 } catch(anna::RuntimeException& ex) {
535 /////////////////////////////
536 // Here received an answer //
537 /////////////////////////////
538 bool doUnbind = false;
539 bool immediateUnbind = false;
543 resultCode = helpers::base::functions::getResultCode(db);
544 } catch(anna::RuntimeException& ex) {
549 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
550 oamModule.count(OamModule::Counter::CEAReceived);
552 if(a_state != State::WaitingBind) {
553 LOGWARNING(anna::Logger::warning("Received CEA: unexpected message at not-WaitingBind state", ANNA_FILE_LOCATION));
554 return; // we don't send its request
555 // string msg("diameter::comm::ClientSession::receive | ");
556 // msg += asString();
557 // msg += " | Received CEA on not-WaitingBind state: unexpected Bind-response";
558 // throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
561 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
563 std::string msg = anna::functions::asString("Received CEA with non-success Result-Code (%d). Unbinding connection.", resultCode);
564 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
568 setState(State::Bound);
569 //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
570 // Inform father server (availability changes):
571 bool changes = a_parent->refreshAvailability();
576 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {
577 oamModule.count(OamModule::Counter::DWAReceived);
578 setWatchdogState(WatchdogState::WaitingTimerExpiration);
580 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS)
582 std::string msg = anna::functions::asString("Received DWA with non-success Result-Code (%d)... but ASSUME keep-alive is reached", resultCode);
583 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
586 if(a_state == State::Failover) {
587 setState(State::Bound);
589 string msg("diameter::comm::ClientSession::receive | ");
591 msg += " | Received DWA on failover state: recovering Bound state";
592 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
596 // Keep-Alive don't manage context
600 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
601 oamModule.count(OamModule::Counter::DPAReceived);
603 if(a_state == State::WaitingDPA) {
604 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
606 std::string msg = anna::functions::asString("Received DPA with non-success Result-Code (%d). Ignoring and recovering Bound state", resultCode);
607 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
609 setState(State::Bound);
611 LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
612 immediateUnbind = true;
618 HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
619 Response* response = response_find(hopByHop);
621 // Out-of-context responses:
624 oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
625 oamModule.count(OamModule::Counter::AnswerReceivedOnClientSessionUnknown);
626 oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnClientSessionUnknown);
627 eventUnknownResponse(db);
628 string msg(asString());
629 msg += anna::functions::asString(" | Response received from entity, for non registered context (HopByHop: %u)", hopByHop);
630 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
633 response->setResultCode(Response::ResultCode::Success);
634 response->cancelTimer();
636 string msg("diameter::comm::ClientSession::receive | ");
638 msg += " | Received answer";
639 msg += response->asString();
640 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
643 anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
644 anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
645 anna::Millisecond timeToAnswerMs = current - request;
646 a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs);
649 std::string msg = "This diameter request context lasted ";
650 msg += anna::functions::asString(timeToAnswerMs);
651 msg += " milliseconds at diameter server (included network time)";
652 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
654 // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
655 Message * requestMessage = const_cast<Message*>(response->getRequest());
656 requestMessage->setRequestServerSessionKey(response->getRequest()->getRequestServerSessionKey()); // -1 means unkown/unset
658 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
660 response->setMessage(&db);
661 // Restore received datablock
663 string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
664 msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
665 msg += ", end-to-end = ";
666 msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
668 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
670 diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
671 diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
672 eventResponse(*response);
673 } catch(anna::RuntimeException& ex) {
678 // unbind is automatically performed, anyway we can inform to the application just in case some additional
679 // procedure could be issued:
683 response_erase(response);
687 unbind(immediateUnbind);
690 void ClientSession::finalize() throw() {
691 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
693 // Check deprecated entity:
694 const Entity *entity = getParent() /* server */ ->getParent() /* entity */;
695 // Inform father server (availability changes):
696 bool changes = a_parent->refreshAvailability();
698 const Server *server = getParent();
699 bool multipleConnections = (server->getMaxClientSessions() > 1);
700 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
701 OamModule &oamModule = OamModule::instantiate();
703 if(multipleConnections) {
704 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
705 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
707 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
708 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
712 void ClientSession::recover() throw() {
713 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "recover", ANNA_FILE_LOCATION));
717 } catch(anna::RuntimeException &ex) {
719 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
721 if(cs) cs->requestClose();
726 // Inform father server (availability changes):
727 bool changes = a_parent->refreshAvailability();
729 const Server *server = getParent();
730 bool multipleConnections = (server->getMaxClientSessions() > 1);
731 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
732 OamModule &oamModule = OamModule::instantiate();
734 if(multipleConnections) {
735 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
736 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
738 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
739 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
743 void ClientSession::sendDWAToServer(const anna::DataBlock& dwrDB)
744 throw(anna::RuntimeException) {
745 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWAToServer", ANNA_FILE_LOCATION));
746 anna::DataBlock dwa(true);
747 a_engine->readDWA(dwa, dwrDB); // Asume that DWA is valid ...
750 throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote server never will validate this connection health", ANNA_FILE_LOCATION);
757 //-------------------------------------------------------------------------
758 // Se invoca desde diameter::comm::Timer
759 //-------------------------------------------------------------------------
760 void ClientSession::expireResponse(diameter::comm::Response* response)
762 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
763 Session::expireResponse(response);
765 OamModule &oamModule = OamModule::instantiate();
766 oamModule.count(OamModule::Counter::RequestSentExpired);
767 oamModule.count(OamModule::Counter::RequestSentOnClientSessionExpired);
768 oamModule.activateAlarm(OamModule::Alarm::RequestSentOnClientSessionExpired);
770 // Check father server idleness:
771 if(idle()) a_parent->childIdle();
774 // LOGDEBUG(anna::Logger::debug("ClientSession is idle after an expiration...", ANNA_FILE_LOCATION));
775 // a_parent->childIdle();
778 // LOGDEBUG(anna::Logger::debug("ClientSession is busy after an expiration...", ANNA_FILE_LOCATION));
783 std::string ClientSession::asString() const
785 string result = Session::asString();
786 result += " | Parent Server: ";
787 result += anna::functions::socketLiteralAsString(getAddress(), getPort());
788 result += " | Auto-recovery: ";
789 result += (a_autoRecovery ? "yes" : "no");
790 result += " | WatchdogState: ";
791 result += asText(a_watchdogState);
792 // Diferente del timeout de ApplicationMessage:
793 result += " | Watchdog Period: ";
794 result += getTimeout().asString();
795 result += " | Hidden: ";
796 result += (hidden() ? "yes" : "no");
799 result += " | MaxConnectionDelay: ";
800 result += a_server->getMaxConnectionDelay().asString();
803 return result += " }";
806 anna::xml::Node* ClientSession::asXML(anna::xml::Node* parent) const
808 anna::xml::Node* result = Session::asXML(parent);
809 parent->createChild("diameter.comm.ClientSession");
810 result->createAttribute("ParentServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
811 result->createAttribute("AutoRecovery", (a_autoRecovery ? "yes" : "no"));
812 result->createAttribute("WatchdogState", asText(a_watchdogState));
813 // Diferente del timeout de ApplicationMessage:
814 result->createAttribute("WatchdogPeriod", getTimeout().asString());
816 if(a_server) result->createAttribute("MaxConnectionDelay", a_server->getMaxConnectionDelay().asString());
818 result->createAttribute("Hidden", hidden() ? "yes" : "no");
823 const char* ClientSession::asText(const WatchdogState::_v watchdogState)
825 static const char* text [] = { "TimerStopped", "WaitingTimerExpiration", "WaitingDWA" };
826 return text [watchdogState];
830 //------------------------------------------------------------------------------
831 //------------------------------------------------------ ClientSession::expire()
832 //------------------------------------------------------------------------------
833 void ClientSession::expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {
834 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (watchdog timer)", ANNA_FILE_LOCATION));
836 // The client MUST NOT close the primary connection until the
837 // primary's watchdog timer has expired at least twice without a
838 // response (note that the watchdog is not sent a second time,
840 if(a_watchdogState == WatchdogState::WaitingDWA) {
841 if(a_state == State::Failover) {
842 LOGWARNING(anna::Logger::warning("Unbinding client-session: Tw expired after first DWA missing (2*Tw elapsed)", ANNA_FILE_LOCATION));
844 return; // finalize will stop the stopped timer ...
847 setState(State::Failover);
848 LOGWARNING(anna::Logger::warning("Going to Failover state: first DWA missing", ANNA_FILE_LOCATION));
849 activateTimer(); // another chance on failover
853 // WaitingTimerExpiration arrives here:
854 const Response* sent;
858 } catch(anna::RuntimeException&) {
859 LOGDEBUG(anna::Logger::debug("Failed to send DWR to the server: unbinding ...", ANNA_FILE_LOCATION));
860 setState(State::Failover);
865 LOGDEBUG(if(sent) anna::Logger::debug("DWR sent to the server", ANNA_FILE_LOCATION););
870 void ClientSession::setWatchdogPeriod(const anna::Millisecond & watchdogPeriod) throw() {
871 setTimeout(watchdogPeriod);
874 void ClientSession::setWatchdogState(WatchdogState::_v wState) throw() {
877 if(wState != a_watchdogState) {
878 std::string msg("Session watchdog state change: ");
879 msg += asText(a_watchdogState);
881 msg += asText(wState);
882 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
885 a_watchdogState = wState;
889 void ClientSession::timerStopped() throw() {
890 LOGDEBUG(anna::Logger::debug("Watchdog timer stopped", ANNA_FILE_LOCATION));
891 setWatchdogState(WatchdogState::TimerStopped);
894 void ClientSession::timerStarted() throw() {
895 LOGDEBUG(anna::Logger::debug("Watchdog timer started", ANNA_FILE_LOCATION));
897 if(a_watchdogState == WatchdogState::WaitingDWA) return;
899 setWatchdogState(WatchdogState::WaitingTimerExpiration);
903 //------------------------------------------------------------------------------
904 //---------------------------------- ClientSession::updateIncomingActivityTime()
905 //------------------------------------------------------------------------------
906 void ClientSession::updateIncomingActivityTime() throw() {
907 Session::updateIncomingActivityTime();
908 a_parent->updateIncomingActivityTime();
912 //------------------------------------------------------------------------------
913 //---------------------------------- ClientSession::updateOutgoingActivityTime()
914 //------------------------------------------------------------------------------
915 void ClientSession::updateOutgoingActivityTime(void) throw() {
916 Session::updateOutgoingActivityTime();
917 a_parent->updateOutgoingActivityTime();
922 //------------------------------------------------------------------------------
923 //----------------------------------------------- ClientSession::countSendings()
924 //------------------------------------------------------------------------------
925 void ClientSession::countSendings(const diameter::CommandId & cid, bool ok)throw() {
926 OamModule &oamModule = OamModule::instantiate();
927 bool isRequest = cid.second;
931 oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
932 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionOK : OamModule::Counter::AnswerSentOnClientSessionOK);
934 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentOK);
935 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);
936 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK); // not usual (dwr was received from server)
937 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
938 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
941 oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
942 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionNOK : OamModule::Counter::AnswerSentOnClientSessionNOK);
944 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentNOK);
945 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);
946 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK); // not usual (dwr was received from server)
947 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
948 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);