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/ApplicationMessageOamModule.hpp>
26 #include <anna/diameter.comm/TimerManager.hpp>
27 #include <anna/diameter.comm/Timer.hpp>
28 #include <anna/diameter.comm/ClientSessionReceiver.hpp>
29 #include <anna/diameter.comm/ReceiverFactoryImpl.hpp>
31 #include <anna/comm/ClientSocket.hpp>
32 #include <anna/app/functions.hpp>
33 #include <anna/core/functions.hpp>
34 #include <anna/core/DataBlock.hpp>
35 #include <anna/core/tracing/Logger.hpp>
36 #include <anna/core/tracing/TraceMethod.hpp>
37 #include <anna/xml/Node.hpp>
38 #include <anna/timex/Engine.hpp>
41 #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() {
61 Session::initialize();
62 a_autoRecovery = true;
65 a_watchdogState = WatchdogState::TimerStopped;
69 //ClientSession::~ClientSession() {;}
72 const std::string& ClientSession::getAddress() const {
73 return a_parent->getAddress();
76 int ClientSession::getPort() const {
77 return a_parent->getPort();
81 void ClientSession::setState(State::_v state) {
82 Session::setState(state);
83 // Inform father server (availability changes):
84 a_parent->refreshAvailability();
87 void ClientSession::bind() noexcept(false) {
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 const Response* ClientSession::send(const Message* message) noexcept(false) {
129 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
132 throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
136 diameter::CommandId cid = message->getCommandId(isRequest);
137 diameter::ApplicationId aid = message->getApplicationId();
140 std::string msg = "Sending diameter message: ";
141 msg += anna::diameter::functions::commandIdAsPairString(cid);
142 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
146 if((cid.first != helpers::base::COMMANDID__Capabilities_Exchange_Request.first) /* not CER/CEA */
147 && (cid.first != helpers::base::COMMANDID__Device_Watchdog_Request.first) /* not DWR/DWA */
148 && (cid.first != helpers::base::COMMANDID__Disconnect_Peer_Request.first)) { /* not DPR/DPA */
150 std::string msg(asString());
151 msg += " | Client-session hidden for application messages delivery";
152 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
159 if((a_state == State::Closed) && (cid != helpers::base::COMMANDID__Capabilities_Exchange_Request)) {
160 string msg(asString());
161 msg += " | ClientSession::bind is not initiated";
162 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
165 if(a_state == State::WaitingBind) {
166 string msg(asString());
167 msg += " | Still waiting for bind ack (CEA)";
168 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
171 if(a_state == State::Failover) {
172 string msg(asString());
173 msg += " | Disabled sent on failover state";
174 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
177 if(a_state == State::Closing) {
178 string msg(asString());
179 msg += " | Disabled sent on closing state";
180 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
184 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
185 if(a_state != State::Closed) {
186 string msg(asString());
187 msg += " | Discarding CER on not closed state";
188 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
190 } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
191 if(a_state == State::WaitingDPA) {
192 string msg(asString());
193 msg += " | DWR is not sent on 'WaitingDPA' state";
194 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
195 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
199 if(a_state == State::Disconnecting) {
200 string msg(asString());
201 msg += " | DWR is not sent on 'Disconnecting' state";
202 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
203 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
207 if(a_state == State::Closing) {
208 string msg(asString());
209 msg += " | DWR is not sent on 'Closing' state";
210 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
211 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
214 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
215 if(a_state == State::WaitingDPA) {
216 string msg(asString());
217 msg += " | Still waiting for DPR ack (DPA)";
218 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
221 if(a_state == State::Disconnecting) {
222 string msg(asString());
223 msg += " | Client disconnection has already been initiated";
224 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
227 if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
228 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
230 string msg("diameter::comm::ClientSession::send | ");
232 msg += " | Sents (request or answer) blocked to diameter server (disconnection in progress). Discarding ...";
233 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
240 // Trace send operation:
242 string msg("diameter::comm::ClientSession::send | ");
245 msg += message->asString();
246 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
248 bool fixed = false; // answers cannot be fixed
249 Message * message_nc = const_cast<Message*>(message);
252 if(/* entity */getParent()->getParent()->isDeprecated()) {
253 string msg(asString());
254 msg += " | Parent entity is deprecated. Request send blocked.";
255 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
259 fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd);
260 message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
265 message->send(*this);
267 // Next hop by hop & end to end identifiers:
268 if(isRequest) generateNextSequences();
271 // The Diameter protocol requires that agents maintain transaction
272 // state, which is used for failover purposes. Transaction state
273 // implies that upon forwarding a request, the Hop-by-Hop identifier
274 // is saved; the field is replaced with a locally unique identifier,
275 // which is restored to its original value when the corresponding
276 // answer is received. The request's state is released upon receipt
277 // of the answer. A stateless agent is one that only maintains
278 // transaction state.
280 updateOutgoingActivityTime();
282 countSendings(cid, aid, true /* send ok */);
283 // Trace non-application messages:
285 if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
286 (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
287 (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
288 anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
290 anna::diameter::codec::Message msg(a_engine->getBaseProtocolCodecEngine()); msg.decode(message->getBody()); /* decode to be traced */
292 catch(anna::RuntimeException &ex) {
293 std::string msg = ex.getText();
294 msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
295 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
300 // Restore sequences:
301 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
302 } catch(anna::RuntimeException&) {
303 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
306 countSendings(cid, aid, false /* send no ok */);
311 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
312 setState(State::WaitingBind);
313 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
314 LOGWARNING(anna::Logger::warning("DPR has been sent to the peer (diameter server)", ANNA_FILE_LOCATION));
315 setState(State::WaitingDPA);
322 // Answers are not temporized:
323 if(!isRequest) return NULL;
326 if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
327 setWatchdogState(WatchdogState::WaitingDWA);
331 // Request will have context responses:
332 Response* result(NULL);
333 result = Response::instance(message->getClassCode(), a_nextHopByHop - 1 /* current request sent to server */);
334 result->setRequest(message);
335 response_add(result);
339 bool ClientSession::unbind(bool forceDisconnect)
341 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
343 if(a_state == State::Closed)
347 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
349 // string msg("Server to be unbound | ");
350 // msg += a_server->asString();
351 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
354 if(forceDisconnect) {
355 LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
357 if(cs) cs->requestClose(); // this will invoke finalize()
362 if(a_state == State::Disconnecting) {
364 string msg("diameter::comm::ClientSession::unbind | ");
366 msg += " | Disconnection already in progress !";
367 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
372 if(a_state == State::Failover) {
374 string msg("diameter::comm::ClientSession::unbind | ");
376 msg += " | Unbind on failover state. Disconnect now.";
377 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
380 if(cs) cs->requestClose(); // this will invoke finalize()
385 if(a_state == State::WaitingBind) {
387 string msg("diameter::comm::ClientSession::unbind | ");
389 msg += " | Unbind on WaitingBind state. Disconnect now.";
390 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
393 if(cs) cs->requestClose(); // this will invoke finalize()
398 if(a_onDisconnect == OnDisconnect::IgnorePendings) {
399 LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
401 if(cs) cs->requestClose(); // this will invoke finalize()
406 if(getOTARequests() == 0) { // No pendings
407 LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
409 if(cs) cs->requestClose(); // this will invoke finalize()
414 if(a_state == State::Closing) {
416 string msg("diameter::comm::ClientSession::unbind | ");
418 msg += " | Closing already in progress (waiting pendings) !";
419 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
426 void ClientSession::eventPeerShutdown() {
427 // Inform father server:
428 a_parent->eventPeerShutdown(this);
431 void ClientSession::eventRequestRetransmission(Message *request) {
434 OamModule &oamModule = OamModule::instantiate();
435 oamModule.count(OamModule::Counter::RequestRetransmitted);
436 oamModule.count(OamModule::Counter::RequestRetransmittedOnClientSession);
438 // Inform father server:
439 a_parent->eventRequestRetransmission(this, request);
442 void ClientSession::eventResponse(const Response& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
443 // Inform father server:
444 a_parent->eventResponse(response, myNode);
447 void ClientSession::eventRequest(const anna::DataBlock &request, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
448 // Inform father server:
449 a_parent->eventRequest(this, request, myNode);
452 void ClientSession::eventUnknownResponse(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
453 // Inform father server:
454 a_parent->eventUnknownResponse(this, response, myNode);
457 void ClientSession::eventDPA(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
458 // Inform father server:
459 a_parent->eventDPA(this, response, myNode);
464 //------------------------------------------------------------------------------------------
465 // Se invoca desde el diameter::comm::Receiver
466 //------------------------------------------------------------------------------------------
467 void ClientSession::receive(const anna::comm::Message& message)
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) ||
483 (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first)) {
485 anna::diameter::codec::Message dmsg(a_engine->getBaseProtocolCodecEngine()); dmsg.decode(db); /* decode to be traced */
487 catch(anna::RuntimeException &ex) {
488 std::string msg = ex.getText();
489 msg += " | Use diameter::comm::Engine::setBaseProtocolCodecEngine() to allow internal base protocol messages full tracing";
490 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
496 OamModule &oamModule = OamModule::instantiate();
497 oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
498 oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnClientSession : OamModule::Counter::AnswerReceivedOnClientSession);
500 a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize(), cid); // only on reception (application could manage sent sizes)
503 /////////////////////////////
504 // Here received a request //
505 /////////////////////////////
508 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
509 LOGWARNING(anna::Logger::warning("Received CER: unexpected message at client-side", ANNA_FILE_LOCATION));
513 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
514 // LOGWARNING(anna::Logger::warning("Received DWR: unexpected message at client-side", ANNA_FILE_LOCATION));
516 // Non-usual but could happen:
517 oamModule.count(OamModule::Counter::DWRReceived);
518 sendDWAToServer(db /* DWR datablock received from server */);
522 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
523 oamModule.count(OamModule::Counter::DPRReceived);
525 if(a_state == State::Bound) {
526 setState(State::Disconnecting);
527 LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter server)", ANNA_FILE_LOCATION));
529 if(getOTARequests() == 0) sendDPA(a_engine, db);
531 return; // DPR won't be informed because virtual readDPA is available for this
536 // application message counters
537 ApplicationMessageOamModule::instantiate().count(cid.first, -1 /* no result code */, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Request_Received_AsClient);
539 eventRequest(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
540 } catch(anna::RuntimeException& ex) {
547 /////////////////////////////
548 // Here received an answer //
549 /////////////////////////////
550 bool doUnbind = false;
551 bool immediateUnbind = false;
555 resultCode = helpers::base::functions::getResultCode(db);
556 } catch(anna::RuntimeException& ex) {
557 // Not always mandatory ...
562 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
563 oamModule.count(OamModule::Counter::CEAReceived);
565 if(a_state != State::WaitingBind) {
566 LOGWARNING(anna::Logger::warning("Received CEA: unexpected message at not-WaitingBind state", ANNA_FILE_LOCATION));
567 return; // we don't send its request
568 // string msg("diameter::comm::ClientSession::receive | ");
569 // msg += asString();
570 // msg += " | Received CEA on not-WaitingBind state: unexpected Bind-response";
571 // throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
574 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
576 std::string msg = anna::functions::asString("Received CEA with non-success Result-Code (%d). Unbinding connection.", resultCode);
577 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
581 setState(State::Bound);
582 //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
583 // Inform father server (availability changes):
584 a_parent->refreshAvailability();
589 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {
590 oamModule.count(OamModule::Counter::DWAReceived);
591 setWatchdogState(WatchdogState::WaitingTimerExpiration);
593 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS)
595 std::string msg = anna::functions::asString("Received DWA with non-success Result-Code (%d)... but ASSUME keep-alive is reached", resultCode);
596 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
599 if(a_state == State::Failover) {
600 setState(State::Bound);
602 string msg("diameter::comm::ClientSession::receive | ");
604 msg += " | Received DWA on failover state: recovering Bound state";
605 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
609 // Keep-Alive don't manage context
613 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
614 oamModule.count(OamModule::Counter::DPAReceived);
616 if(a_state == State::WaitingDPA) {
617 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
619 std::string msg = anna::functions::asString("Received DPA with non-success Result-Code (%d). Ignoring and recovering Bound state", resultCode);
620 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
622 setState(State::Bound);
624 LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
625 immediateUnbind = true;
631 HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
632 Response* response = response_find(hopByHop);
634 // Out-of-context responses:
637 oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
638 oamModule.count(OamModule::Counter::AnswerReceivedOnClientSessionUnknown);
639 oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnClientSessionUnknown);
641 // application message counters
642 ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_UnknownReceived_AsClient);
644 eventUnknownResponse(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
645 string msg(asString());
646 msg += anna::functions::asString(" | Response received from entity, for non registered context (HopByHop: %u)", hopByHop);
647 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
650 response->setResultCode(Response::ResultCode::Success);
651 response->cancelTimer();
653 string msg("diameter::comm::ClientSession::receive | ");
655 msg += " | Received answer";
656 msg += response->asString();
657 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
660 anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
661 anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
662 anna::Millisecond timeToAnswerMs = current - request;
663 a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs, cid);
666 // std::string msg = "This diameter request context lasted ";
667 // msg += anna::functions::asString(timeToAnswerMs);
668 // msg += " milliseconds at diameter server (included network time)";
669 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
671 // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
672 Message * requestMessage = const_cast<Message*>(response->getRequest());
673 requestMessage->setRequestServerSessionKey(response->getRequest()->getRequestServerSessionKey()); // -1 means unkown/unset
675 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
677 response->setMessage(&db);
678 // Restore received datablock
680 string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
681 msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
682 msg += ", end-to-end = ";
683 msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
685 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
687 diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
688 diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
690 // application message counters
691 if(cid != helpers::base::COMMANDID__Capabilities_Exchange_Answer)
692 ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_Received_AsClient);
694 eventResponse(*response, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
695 } catch(anna::RuntimeException& ex) {
700 // unbind is automatically performed, anyway we can inform to the application just in case some additional
701 // procedure could be issued:
702 eventDPA(db, nullptr /* client classes have a valid engine usage to distinguish, and get origin host from */);
705 response_erase(response);
709 unbind(immediateUnbind);
712 void ClientSession::finalize() {
713 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
715 // Inform father server (availability changes):
716 a_parent->refreshAvailability();
718 const Server *server = getParent();
719 bool multipleConnections = (server->getMaxClientSessions() > 1);
720 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
721 OamModule &oamModule = OamModule::instantiate();
723 if(multipleConnections) {
724 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
725 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
727 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
728 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
732 void ClientSession::recover() {
733 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "recover", ANNA_FILE_LOCATION));
737 } catch(anna::RuntimeException &ex) {
739 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
741 if(cs) cs->requestClose();
746 // Inform father server (availability changes):
747 a_parent->refreshAvailability();
749 const Server *server = getParent();
750 bool multipleConnections = (server->getMaxClientSessions() > 1);
751 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
752 OamModule &oamModule = OamModule::instantiate();
754 if(multipleConnections) {
755 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
756 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
758 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
759 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
763 void ClientSession::sendDWAToServer(const anna::DataBlock& dwrDB)
765 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWAToServer", ANNA_FILE_LOCATION));
766 anna::DataBlock dwa(true);
767 a_engine->readDWA(dwa, dwrDB); // Asume that DWA is valid ...
770 throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote server never will validate this connection health", ANNA_FILE_LOCATION);
777 //-------------------------------------------------------------------------
778 // Se invoca desde diameter::comm::Timer
779 //-------------------------------------------------------------------------
780 void ClientSession::expireResponse(diameter::comm::Response* response)
782 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
783 Session::expireResponse(response);
785 OamModule &oamModule = OamModule::instantiate();
786 oamModule.count(OamModule::Counter::RequestSentExpired);
787 oamModule.count(OamModule::Counter::RequestSentOnClientSessionExpired);
788 oamModule.activateAlarm(OamModule::Alarm::RequestSentOnClientSessionExpired);
790 // Check father server idleness:
791 if(idle()) a_parent->childIdle();
794 // LOGDEBUG(anna::Logger::debug("ClientSession is idle after an expiration...", ANNA_FILE_LOCATION));
795 // a_parent->childIdle();
798 // LOGDEBUG(anna::Logger::debug("ClientSession is busy after an expiration...", ANNA_FILE_LOCATION));
803 std::string ClientSession::asString() const
805 string result = Session::asString();
806 result += " | Parent Server: ";
807 result += anna::functions::socketLiteralAsString(getAddress(), getPort());
808 result += " | Auto-recovery: ";
809 result += (a_autoRecovery ? "yes" : "no");
810 result += " | WatchdogState: ";
811 result += asText(a_watchdogState);
812 // Diferente del timeout de ApplicationMessage:
813 result += " | Watchdog Period: ";
814 result += getTimeout().asString();
815 result += " | Hidden: ";
816 result += (hidden() ? "yes" : "no");
819 result += " | MaxConnectionDelay: ";
820 result += a_server->getMaxConnectionDelay().asString();
823 return result += " }";
826 anna::xml::Node* ClientSession::asXML(anna::xml::Node* parent) const
828 anna::xml::Node* result = Session::asXML(parent);
829 parent->createChild("diameter.comm.ClientSession");
830 result->createAttribute("ParentServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
831 result->createAttribute("AutoRecovery", (a_autoRecovery ? "yes" : "no"));
832 result->createAttribute("WatchdogState", asText(a_watchdogState));
833 // Diferente del timeout de ApplicationMessage:
834 result->createAttribute("WatchdogPeriod", getTimeout().asString());
836 if(a_server) result->createAttribute("MaxConnectionDelay", a_server->getMaxConnectionDelay().asString());
838 result->createAttribute("Hidden", hidden() ? "yes" : "no");
843 const char* ClientSession::asText(const WatchdogState::_v watchdogState)
845 static const char* text [] = { "TimerStopped", "WaitingTimerExpiration", "WaitingDWA" };
846 return text [watchdogState];
850 //------------------------------------------------------------------------------
851 //------------------------------------------------------ ClientSession::expire()
852 //------------------------------------------------------------------------------
853 void ClientSession::expire(anna::timex::Engine *timeController) noexcept(false) {
854 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (watchdog timer)", ANNA_FILE_LOCATION));
856 // The client MUST NOT close the primary connection until the
857 // primary's watchdog timer has expired at least twice without a
858 // response (note that the watchdog is not sent a second time,
860 if(a_watchdogState == WatchdogState::WaitingDWA) {
861 if(a_state == State::Failover) {
862 LOGWARNING(anna::Logger::warning("Unbinding client-session: Tw expired after first DWA missing (2*Tw elapsed)", ANNA_FILE_LOCATION));
864 return; // finalize will stop the stopped timer ...
867 setState(State::Failover);
868 LOGWARNING(anna::Logger::warning("Going to Failover state: first DWA missing", ANNA_FILE_LOCATION));
869 activateTimer(); // another chance on failover
873 // WaitingTimerExpiration arrives here:
874 const Response* sent;
878 } catch(anna::RuntimeException&) {
879 LOGDEBUG(anna::Logger::debug("Failed to send DWR to the server: unbinding ...", ANNA_FILE_LOCATION));
880 setState(State::Failover);
885 LOGDEBUG(if(sent) anna::Logger::debug("DWR sent to the server", ANNA_FILE_LOCATION););
890 void ClientSession::setWatchdogPeriod(const anna::Millisecond & watchdogPeriod) {
891 setTimeout(watchdogPeriod);
894 void ClientSession::setWatchdogState(WatchdogState::_v wState) {
897 if(wState != a_watchdogState) {
898 std::string msg("Session watchdog state change: ");
899 msg += asText(a_watchdogState);
901 msg += asText(wState);
902 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
905 a_watchdogState = wState;
909 void ClientSession::timerStopped() {
910 LOGDEBUG(anna::Logger::debug("Watchdog timer stopped", ANNA_FILE_LOCATION));
911 setWatchdogState(WatchdogState::TimerStopped);
914 void ClientSession::timerStarted() {
915 LOGDEBUG(anna::Logger::debug("Watchdog timer started", ANNA_FILE_LOCATION));
917 if(a_watchdogState == WatchdogState::WaitingDWA) return;
919 setWatchdogState(WatchdogState::WaitingTimerExpiration);
923 //------------------------------------------------------------------------------
924 //---------------------------------- ClientSession::updateIncomingActivityTime()
925 //------------------------------------------------------------------------------
926 void ClientSession::updateIncomingActivityTime() {
927 Session::updateIncomingActivityTime();
928 a_parent->updateIncomingActivityTime();
932 //------------------------------------------------------------------------------
933 //---------------------------------- ClientSession::updateOutgoingActivityTime()
934 //------------------------------------------------------------------------------
935 void ClientSession::updateOutgoingActivityTime(void) {
936 Session::updateOutgoingActivityTime();
937 a_parent->updateOutgoingActivityTime();
942 //------------------------------------------------------------------------------
943 //----------------------------------------------- ClientSession::countSendings()
944 //------------------------------------------------------------------------------
945 void ClientSession::countSendings(const diameter::CommandId & cid, unsigned int aid, bool ok){
946 OamModule &oamModule = OamModule::instantiate();
947 ApplicationMessageOamModule &appMsgOamModule = ApplicationMessageOamModule::instantiate();
949 bool isRequest = cid.second;
953 oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
954 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionOK : OamModule::Counter::AnswerSentOnClientSessionOK);
956 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentOK);
957 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);
958 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK); // not usual (dwr was received from server)
959 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
960 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
961 // Application messages:
963 appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentOK_AsClient : ApplicationMessageOamModule::Counter::Answer_SentOK_AsClient);
967 oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
968 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionNOK : OamModule::Counter::AnswerSentOnClientSessionNOK);
970 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentNOK);
971 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);
972 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK); // not usual (dwr was received from server)
973 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
974 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);
975 // Application messages:
977 appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentNOK_AsClient : ApplicationMessageOamModule::Counter::Answer_SentNOK_AsClient);