1 // ANNA - Anna is Not 'N' Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // https://bitbucket.org/testillano/anna
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
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
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.
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.
33 // Authors: eduardo.ramos.testillano@gmail.com
34 // cisco.tierra@gmail.com
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>
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>
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>
68 #include <stdlib.h> // rand()
74 using namespace anna::diameter;
75 using namespace anna::diameter::comm;
78 const anna::Millisecond ClientSession::DefaultWatchdogPeriod(30000); // Watchdog messages timeout
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
88 void ClientSession::initialize() throw() {
89 Session::initialize();
90 a_autoRecovery = true;
93 a_watchdogState = WatchdogState::TimerStopped;
97 //ClientSession::~ClientSession() {;}
100 const std::string& ClientSession::getAddress() const throw() {
101 return a_parent->getAddress();
104 int ClientSession::getPort() const throw() {
105 return a_parent->getPort();
109 void ClientSession::setState(State::_v state) throw() {
110 Session::setState(state);
111 // Inform father server (availability changes):
112 bool changes = a_parent->refreshAvailability();
115 void ClientSession::bind() throw(anna::RuntimeException) {
116 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "bind", ANNA_FILE_LOCATION));
118 if(a_state != State::Closed)
122 string msg("diameter::comm::ClientSession::bind | ");
124 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
129 throw anna::RuntimeException("Server is not yet created", ANNA_FILE_LOCATION);
131 bool serverAvailable = a_server->isAvailable();
134 if(serverAvailable) anna::Logger::debug("Server AVAILABLE", ANNA_FILE_LOCATION);
135 else anna::Logger::debug("Server NOT AVAILABLE. Connecting ...", ANNA_FILE_LOCATION);
138 if(!serverAvailable) {
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);
148 LOGDEBUG(anna::Logger::debug("CER sent to the server", ANNA_FILE_LOCATION));
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);
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);
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));
171 const Response* ClientSession::send(const Message* message) throw(anna::RuntimeException) {
172 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
175 throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
179 diameter::CommandId cid = message->getCommandId(isRequest);
181 std::string msg = "Sending diameter message: ";
182 msg += anna::diameter::functions::commandIdAsPairString(cid);
183 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
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 */
191 std::string msg(asString());
192 msg += " | Client-session hidden for application messages delivery";
193 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
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);
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);
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);
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);
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);
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));
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));
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));
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);
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);
268 if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
269 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
271 string msg("diameter::comm::ClientSession::send | ");
273 msg += " | Sents (request or answer) blocked to diameter server (disconnection in progress). Discarding ...";
274 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
281 // Trace send operation:
283 string msg("diameter::comm::ClientSession::send | ");
286 msg += message->asString();
287 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
289 bool fixed = false; // answers cannot be fixed
290 Message * message_nc = const_cast<Message*>(message);
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);
300 fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd, a_engine->getFreezeEndToEndOnSending());
301 message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
306 message->send(*this);
308 // Next hop by hop & end to end identifiers:
309 if(isRequest) generateNextSequences();
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.
321 updateOutgoingActivityTime();
323 countSendings(cid, true /* send ok */);
324 // Trace non-application messages:
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&) {;}
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
341 countSendings(cid, false /* send no ok */);
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);
357 // Answers are not temporized:
358 if(!isRequest) return NULL;
361 if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
362 setWatchdogState(WatchdogState::WaitingDWA);
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);
374 bool ClientSession::unbind(bool forceDisconnect)
375 throw(anna::RuntimeException) {
376 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
378 if(a_state == State::Closed)
382 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
384 // string msg("Server to be unbound | ");
385 // msg += a_server->asString();
386 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
389 if(forceDisconnect) {
390 LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
392 if(cs) cs->requestClose(); // this will invoke finalize()
397 if(a_state == State::Disconnecting) {
399 string msg("diameter::comm::ClientSession::unbind | ");
401 msg += " | Disconnection already in progress !";
402 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
407 if(a_state == State::Failover) {
409 string msg("diameter::comm::ClientSession::unbind | ");
411 msg += " | Unbind on failover state. Disconnect now.";
412 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
415 if(cs) cs->requestClose(); // this will invoke finalize()
420 if(a_state == State::WaitingBind) {
422 string msg("diameter::comm::ClientSession::unbind | ");
424 msg += " | Unbind on WaitingBind state. Disconnect now.";
425 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
428 if(cs) cs->requestClose(); // this will invoke finalize()
433 if(a_onDisconnect == OnDisconnect::IgnorePendings) {
434 LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
436 if(cs) cs->requestClose(); // this will invoke finalize()
441 if(getOTARequests() == 0) { // No pendings
442 LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
444 if(cs) cs->requestClose(); // this will invoke finalize()
449 if(a_state == State::Closing) {
451 string msg("diameter::comm::ClientSession::unbind | ");
453 msg += " | Closing already in progress (waiting pendings) !";
454 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
461 void ClientSession::eventPeerShutdown() throw() {
462 // Inform father server:
463 a_parent->eventPeerShutdown(this);
466 void ClientSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
467 // Inform father server:
468 a_parent->eventResponse(response);
471 void ClientSession::eventRequest(const anna::DataBlock &request) throw(anna::RuntimeException) {
472 // Inform father server:
473 a_parent->eventRequest(this, request);
476 void ClientSession::eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) {
477 // Inform father server:
478 a_parent->eventUnknownResponse(this, response);
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));
490 updateIncomingActivityTime();
493 const anna::DataBlock & db = message.getBody();
494 diameter::CommandId cid = codec::functions::getCommandId(db);
495 bool isRequest = cid.second;
497 std::string msg = "Received diameter message: ";
498 msg += anna::diameter::functions::commandIdAsPairString(cid);
499 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
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&) {;}
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);
509 a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
512 /////////////////////////////
513 // Here received a request //
514 /////////////////////////////
517 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
518 LOGWARNING(anna::Logger::warning("Received CER: unexpected message at client-side", ANNA_FILE_LOCATION));
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));
525 // Non-usual but could happen:
526 oamModule.count(OamModule::Counter::DWRReceived);
527 sendDWAToServer(db /* DWR datablock received from server */);
531 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
532 oamModule.count(OamModule::Counter::DPRReceived);
534 if(a_state == State::Bound) {
536 setState(State::Disconnecting);
537 LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter server)", ANNA_FILE_LOCATION));
539 if(getOTARequests() == 0) sendDPA();
541 return; // DPR won't be informed because virtual readDPA is available for this
547 } catch(anna::RuntimeException& ex) {
554 /////////////////////////////
555 // Here received an answer //
556 /////////////////////////////
557 bool doUnbind = false;
558 bool immediateUnbind = false;
562 resultCode = helpers::base::functions::getResultCode(db);
563 } catch(anna::RuntimeException& ex) {
568 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
569 oamModule.count(OamModule::Counter::CEAReceived);
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);
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));
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();
592 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {
593 oamModule.count(OamModule::Counter::DWAReceived);
594 setWatchdogState(WatchdogState::WaitingTimerExpiration);
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));
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) {
618 LOGWARNING(anna::Logger::warning("Received DPA with non-success Result-Code. Ignoring and recovering Bound state", ANNA_FILE_LOCATION));
619 setState(State::Bound);
621 LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
622 immediateUnbind = true;
628 HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
629 Response* response = response_find(hopByHop);
631 // Out-of-context responses:
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);
643 response->setResultCode(Response::ResultCode::Success);
644 response->cancelTimer();
646 string msg("diameter::comm::ClientSession::receive | ");
648 msg += " | Received answer";
649 msg += response->asString();
650 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
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);
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);
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
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
671 response->setMessage(&db);
672 // Restore received datablock
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());
679 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
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) {
689 response_erase(response);
693 unbind(immediateUnbind);
696 void ClientSession::finalize() throw() {
697 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
699 // Check deprecated entity:
700 const Entity *entity = getParent() /* server */ ->getParent() /* entity */;
701 // Inform father server (availability changes):
702 bool changes = a_parent->refreshAvailability();
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();
709 if(multipleConnections) {
710 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
711 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
713 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
714 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
718 void ClientSession::recover() throw() {
719 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "recover", ANNA_FILE_LOCATION));
723 } catch(anna::RuntimeException &ex) {
725 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
727 if(cs) cs->requestClose();
732 // Inform father server (availability changes):
733 bool changes = a_parent->refreshAvailability();
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();
740 if(multipleConnections) {
741 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
742 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
744 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
745 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
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 ...
756 throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote server never will validate this connection health", ANNA_FILE_LOCATION);
763 //-------------------------------------------------------------------------
764 // Se invoca desde diameter::comm::Timer
765 //-------------------------------------------------------------------------
766 void ClientSession::expireResponse(diameter::comm::Response* response)
768 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
769 Session::expireResponse(response);
771 OamModule &oamModule = OamModule::instantiate();
772 oamModule.count(OamModule::Counter::RequestSentExpired);
773 oamModule.count(OamModule::Counter::RequestSentOnClientSessionExpired);
774 oamModule.activateAlarm(OamModule::Alarm::RequestSentOnClientSessionExpired);
776 // Check father server idleness:
777 if(idle()) a_parent->childIdle();
780 // LOGDEBUG(anna::Logger::debug("ClientSession is idle after an expiration...", ANNA_FILE_LOCATION));
781 // a_parent->childIdle();
784 // LOGDEBUG(anna::Logger::debug("ClientSession is busy after an expiration...", ANNA_FILE_LOCATION));
789 std::string ClientSession::asString() const
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");
805 result += " | MaxConnectionDelay: ";
806 result += a_server->getMaxConnectionDelay().asString();
809 return result += " }";
812 anna::xml::Node* ClientSession::asXML(anna::xml::Node* parent) const
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());
822 if(a_server) result->createAttribute("MaxConnectionDelay", a_server->getMaxConnectionDelay().asString());
824 result->createAttribute("Hidden", hidden() ? "yes" : "no");
829 const char* ClientSession::asText(const WatchdogState::_v watchdogState)
831 static const char* text [] = { "TimerStopped", "WaitingTimerExpiration", "WaitingDWA" };
832 return text [watchdogState];
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));
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,
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));
850 return; // finalize will stop the stopped timer ...
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
859 // WaitingTimerExpiration arrives here:
860 const Response* sent;
864 } catch(anna::RuntimeException&) {
865 LOGDEBUG(anna::Logger::debug("Failed to send DWR to the server: unbinding ...", ANNA_FILE_LOCATION));
866 setState(State::Failover);
871 LOGDEBUG(if(sent) anna::Logger::debug("DWR sent to the server", ANNA_FILE_LOCATION););
876 void ClientSession::setWatchdogPeriod(const anna::Millisecond & watchdogPeriod) throw() {
877 setTimeout(watchdogPeriod);
880 void ClientSession::setWatchdogState(WatchdogState::_v wState) throw() {
883 if(wState != a_watchdogState) {
884 std::string msg("Session watchdog state change: ");
885 msg += asText(a_watchdogState);
887 msg += asText(wState);
888 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
891 a_watchdogState = wState;
895 void ClientSession::timerStopped() throw() {
896 LOGDEBUG(anna::Logger::debug("Watchdog timer stopped", ANNA_FILE_LOCATION));
897 setWatchdogState(WatchdogState::TimerStopped);
900 void ClientSession::timerStarted() throw() {
901 LOGDEBUG(anna::Logger::debug("Watchdog timer started", ANNA_FILE_LOCATION));
903 if(a_watchdogState == WatchdogState::WaitingDWA) return;
905 setWatchdogState(WatchdogState::WaitingTimerExpiration);
909 //------------------------------------------------------------------------------
910 //---------------------------------- ClientSession::updateIncomingActivityTime()
911 //------------------------------------------------------------------------------
912 void ClientSession::updateIncomingActivityTime() throw() {
913 Session::updateIncomingActivityTime();
914 a_parent->updateIncomingActivityTime();
918 //------------------------------------------------------------------------------
919 //---------------------------------- ClientSession::updateOutgoingActivityTime()
920 //------------------------------------------------------------------------------
921 void ClientSession::updateOutgoingActivityTime(void) throw() {
922 Session::updateOutgoingActivityTime();
923 a_parent->updateOutgoingActivityTime();
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;
937 oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
938 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionOK : OamModule::Counter::AnswerSentOnClientSessionOK);
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);
947 oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
948 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionNOK : OamModule::Counter::AnswerSentOnClientSessionNOK);
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);