1 // ANNA - Anna is Not Nothingness Anymore
3 // (c) Copyright 2005-2014 Eduardo Ramos Testillano & Francisco Ruiz Rayo
5 // http://redmine.teslayout.com/projects/anna-suite
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 the copyright holder nor the names of its
18 // contributors may be used to endorse or promote products derived from
19 // this software without specific prior written permission.
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 // Some operations could be done before sending CER, for example non-standard Origin-Host manipulation for
145 a_engine->bindingClientSession(this);
147 // OAM Lo comento, porque no se contabilizan los reintentos y por lo tanto no son muy Ăștiles.
148 // OamModule &oamModule = OamModule::instantiate();
149 // oamModule.count(a_server->isAvailable() ? OamModule::Counter::TCPConnectOK:OamModule::Counter::TCPConnectNOK);
152 LOGDEBUG(anna::Logger::debug("CER sent to the server", ANNA_FILE_LOCATION));
156 void ClientSession::setCERandDWR(const anna::DataBlock & cer, const anna::DataBlock & dwr) throw(anna::RuntimeException) {
157 if(codec::functions::getCommandId(cer) != helpers::base::COMMANDID__Capabilities_Exchange_Request) {
158 throw anna::RuntimeException("The message provided as 'CER' is not a Capabilities-Exchange-Request", ANNA_FILE_LOCATION);
161 if(codec::functions::getCommandId(dwr) != helpers::base::COMMANDID__Device_Watchdog_Request) {
162 throw anna::RuntimeException("The message provided as 'DWR' is not a Device-Watchdog-Request", ANNA_FILE_LOCATION);
165 // La verificacion ya se hace implicitamente antes
166 // if ((a_cer.isEmpty()) || (a_dwr.isEmpty())) {
167 // LOGDEBUG (anna::Logger::debug ("Must define valid CER and DWR messages before use bind !", ANNA_FILE_LOCATION));
175 const Response* ClientSession::send(const Message* message) throw(anna::RuntimeException) {
176 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
179 throw anna::RuntimeException("Cannot send a NULL message", ANNA_FILE_LOCATION);
183 diameter::CommandId cid = message->getCommandId(isRequest);
185 std::string msg = "Sending diameter message: ";
186 msg += anna::diameter::functions::commandIdAsPairString(cid);
187 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
191 if((cid.first != helpers::base::COMMANDID__Capabilities_Exchange_Request.first) /* not CER/CEA */
192 && (cid.first != helpers::base::COMMANDID__Device_Watchdog_Request.first) /* not DWR/DWA */
193 && (cid.first != helpers::base::COMMANDID__Disconnect_Peer_Request.first)) { /* not DPR/DPA */
195 std::string msg(asString());
196 msg += " | Client-session hidden for application messages delivery";
197 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
204 if((a_state == State::Closed) && (cid != helpers::base::COMMANDID__Capabilities_Exchange_Request)) {
205 string msg(asString());
206 msg += " | ClientSession::bind is not initiated";
207 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
210 if(a_state == State::WaitingBind) {
211 string msg(asString());
212 msg += " | Still waiting for bind ack (CEA)";
213 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
216 if(a_state == State::Failover) {
217 string msg(asString());
218 msg += " | Disabled sent on failover state";
219 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
222 if(a_state == State::Closing) {
223 string msg(asString());
224 msg += " | Disabled sent on closing state";
225 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
229 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
230 if(a_state != State::Closed) {
231 string msg(asString());
232 msg += " | Discarding CER on not closed state";
233 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
235 } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
236 if(a_state == State::WaitingDPA) {
237 string msg(asString());
238 msg += " | DWR is not sent on 'WaitingDPA' state";
239 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
240 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
244 if(a_state == State::Disconnecting) {
245 string msg(asString());
246 msg += " | DWR is not sent on 'Disconnecting' state";
247 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
248 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
252 if(a_state == State::Closing) {
253 string msg(asString());
254 msg += " | DWR is not sent on 'Closing' state";
255 //throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
256 LOGDEBUG(anna::Logger::debug(msg, ANNA_FILE_LOCATION));
259 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
260 if(a_state == State::WaitingDPA) {
261 string msg(asString());
262 msg += " | Still waiting for DPR ack (DPA)";
263 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
266 if(a_state == State::Disconnecting) {
267 string msg(asString());
268 msg += " | Client disconnection has already been initiated";
269 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
272 if((a_state == State::WaitingDPA) || (a_state == State::Disconnecting)) {
273 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
275 string msg("diameter::comm::ClientSession::send | ");
277 msg += " | Sents (request or answer) blocked to diameter server (disconnection in progress). Discarding ...";
278 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
285 // Trace send operation:
287 string msg("diameter::comm::ClientSession::send | ");
290 msg += message->asString();
291 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
293 bool fixed = false; // answers cannot be fixed
294 Message * message_nc = const_cast<Message*>(message);
297 if(/* entity */getParent()->getParent()->isDeprecated()) {
298 string msg(asString());
299 msg += " | Parent entity is deprecated. Request send blocked.";
300 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
304 fixed = message_nc->fixRequestSequence(a_nextHopByHop, a_nextEndToEnd, a_engine->getFreezeEndToEndOnSending());
305 message_nc->updateRequestTimestampMs(); // statistics purposes (processing time for request type)
310 message->send(*this);
312 // Next hop by hop & end to end identifiers:
313 if(isRequest) generateNextSequences();
316 // The Diameter protocol requires that agents maintain transaction
317 // state, which is used for failover purposes. Transaction state
318 // implies that upon forwarding a request, the Hop-by-Hop identifier
319 // is saved; the field is replaced with a locally unique identifier,
320 // which is restored to its original value when the corresponding
321 // answer is received. The request's state is released upon receipt
322 // of the answer. A stateless agent is one that only maintains
323 // transaction state.
325 updateOutgoingActivityTime();
327 countSendings(cid, true /* send ok */);
328 // Trace non-application messages:
331 if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) ||
332 (cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
333 (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
334 anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
335 try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
339 // Restore sequences:
340 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
341 } catch(anna::RuntimeException&) {
342 if(fixed) message_nc->restoreSequencesAfterFix(); // restore to application sequences after fix
345 countSendings(cid, false /* send no ok */);
350 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
351 setState(State::WaitingBind);
352 } else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
353 LOGWARNING(anna::Logger::warning("DPR has been sent to the peer (diameter server)", ANNA_FILE_LOCATION));
354 setState(State::WaitingDPA);
361 // Answers are not temporized:
362 if(!isRequest) return NULL;
365 if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
366 setWatchdogState(WatchdogState::WaitingDWA);
370 // Request will have context responses:
371 Response* result(NULL);
372 result = Response::instance(message->getClassCode(), a_nextHopByHop - 1 /* current request sent to server */);
373 result->setRequest(message);
374 response_add(result);
378 bool ClientSession::unbind(bool forceDisconnect)
379 throw(anna::RuntimeException) {
380 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
382 if(a_state == State::Closed)
386 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
388 // string msg("Server to be unbound | ");
389 // msg += a_server->asString();
390 // anna::Logger::debug(msg, ANNA_FILE_LOCATION);
393 if(forceDisconnect) {
394 LOGDEBUG(anna::Logger::debug("Immediate disconnection (forceDisconnect)", ANNA_FILE_LOCATION));
396 if(cs) cs->requestClose(); // this will invoke finalize()
401 if(a_state == State::Disconnecting) {
403 string msg("diameter::comm::ClientSession::unbind | ");
405 msg += " | Disconnection already in progress !";
406 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
411 if(a_state == State::Failover) {
413 string msg("diameter::comm::ClientSession::unbind | ");
415 msg += " | Unbind on failover state. Disconnect now.";
416 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
419 if(cs) cs->requestClose(); // this will invoke finalize()
424 if(a_state == State::WaitingBind) {
426 string msg("diameter::comm::ClientSession::unbind | ");
428 msg += " | Unbind on WaitingBind state. Disconnect now.";
429 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
432 if(cs) cs->requestClose(); // this will invoke finalize()
437 if(a_onDisconnect == OnDisconnect::IgnorePendings) {
438 LOGDEBUG(anna::Logger::debug("Immediate disconnection (IgnorePendings)", ANNA_FILE_LOCATION));
440 if(cs) cs->requestClose(); // this will invoke finalize()
445 if(getOTARequests() == 0) { // No pendings
446 LOGDEBUG(anna::Logger::debug("No pending answers. Perform client-session close.", ANNA_FILE_LOCATION));
448 if(cs) cs->requestClose(); // this will invoke finalize()
453 if(a_state == State::Closing) {
455 string msg("diameter::comm::ClientSession::unbind | ");
457 msg += " | Closing already in progress (waiting pendings) !";
458 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
465 void ClientSession::eventPeerShutdown() throw() {
466 // Inform father server:
467 a_parent->eventPeerShutdown(this);
470 void ClientSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
471 // Inform father server:
472 a_parent->eventResponse(response);
475 void ClientSession::eventRequest(const anna::DataBlock &request) throw(anna::RuntimeException) {
476 // Inform father server:
477 a_parent->eventRequest(this, request);
480 void ClientSession::eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) {
481 // Inform father server:
482 a_parent->eventUnknownResponse(this, response);
485 void ClientSession::eventDPA(const anna::DataBlock& response) throw(anna::RuntimeException) {
486 // Inform father server:
487 a_parent->eventDPA(this, response);
492 //------------------------------------------------------------------------------------------
493 // Se invoca desde el diameter::comm::Receiver
494 //------------------------------------------------------------------------------------------
495 void ClientSession::receive(const anna::comm::Message& message)
496 throw(anna::RuntimeException) {
497 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
499 updateIncomingActivityTime();
502 const anna::DataBlock & db = message.getBody();
503 diameter::CommandId cid = codec::functions::getCommandId(db);
504 bool isRequest = cid.second;
506 std::string msg = "Received diameter message: ";
507 msg += anna::diameter::functions::commandIdAsPairString(cid);
508 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
510 if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
511 try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
514 OamModule &oamModule = OamModule::instantiate();
515 oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
516 oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnClientSession : OamModule::Counter::AnswerReceivedOnClientSession);
518 a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
521 /////////////////////////////
522 // Here received a request //
523 /////////////////////////////
526 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
527 LOGWARNING(anna::Logger::warning("Received CER: unexpected message at client-side", ANNA_FILE_LOCATION));
531 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
532 // LOGWARNING(anna::Logger::warning("Received DWR: unexpected message at client-side", ANNA_FILE_LOCATION));
534 // Non-usual but could happen:
535 oamModule.count(OamModule::Counter::DWRReceived);
536 sendDWAToServer(db /* DWR datablock received from server */);
540 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
541 oamModule.count(OamModule::Counter::DPRReceived);
543 if(a_state == State::Bound) {
545 setState(State::Disconnecting);
546 LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter server)", ANNA_FILE_LOCATION));
548 if(getOTARequests() == 0) sendDPA();
550 return; // DPR won't be informed because virtual readDPA is available for this
556 } catch(anna::RuntimeException& ex) {
563 /////////////////////////////
564 // Here received an answer //
565 /////////////////////////////
566 bool doUnbind = false;
567 bool immediateUnbind = false;
571 resultCode = helpers::base::functions::getResultCode(db);
572 } catch(anna::RuntimeException& ex) {
577 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Answer) {
578 oamModule.count(OamModule::Counter::CEAReceived);
580 if(a_state != State::WaitingBind) {
581 LOGWARNING(anna::Logger::warning("Received CEA: unexpected message at not-WaitingBind state", ANNA_FILE_LOCATION));
582 return; // we don't send its request
583 // string msg("diameter::comm::ClientSession::receive | ");
584 // msg += asString();
585 // msg += " | Received CEA on not-WaitingBind state: unexpected Bind-response";
586 // throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
589 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
591 std::string msg = anna::functions::asString("Received CEA with non-success Result-Code (%d). Unbinding connection.", resultCode);
592 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
596 setState(State::Bound);
597 //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
598 // Inform father server (availability changes):
599 bool changes = a_parent->refreshAvailability();
604 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {
605 oamModule.count(OamModule::Counter::DWAReceived);
606 setWatchdogState(WatchdogState::WaitingTimerExpiration);
608 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS)
610 std::string msg = anna::functions::asString("Received DWA with non-success Result-Code (%d)... but ASSUME keep-alive is reached", resultCode);
611 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
614 if(a_state == State::Failover) {
615 setState(State::Bound);
617 string msg("diameter::comm::ClientSession::receive | ");
619 msg += " | Received DWA on failover state: recovering Bound state";
620 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
624 // Keep-Alive don't manage context
628 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) {
629 oamModule.count(OamModule::Counter::DPAReceived);
631 if(a_state == State::WaitingDPA) {
632 if(resultCode != helpers::base::AVPVALUES__Result_Code::DIAMETER_SUCCESS) {
634 std::string msg = anna::functions::asString("Received DPA with non-success Result-Code (%d). Ignoring and recovering Bound state", resultCode);
635 anna::Logger::warning(msg, ANNA_FILE_LOCATION);
637 setState(State::Bound);
639 LOGWARNING(anna::Logger::warning("Received DPA With Result-Code = DIAMETER_SUCCESS. Disconnect now.", ANNA_FILE_LOCATION));
640 immediateUnbind = true;
646 HopByHop hopByHop = codec::functions::getHopByHop(db); // context identification
647 Response* response = response_find(hopByHop);
649 // Out-of-context responses:
652 oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
653 oamModule.count(OamModule::Counter::AnswerReceivedOnClientSessionUnknown);
654 oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnClientSessionUnknown);
655 eventUnknownResponse(db);
656 string msg(asString());
657 msg += anna::functions::asString(" | Response received from entity, for non registered context (HopByHop: %u)", hopByHop);
658 throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
661 response->setResultCode(Response::ResultCode::Success);
662 response->cancelTimer();
664 string msg("diameter::comm::ClientSession::receive | ");
666 msg += " | Received answer";
667 msg += response->asString();
668 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
671 anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
672 anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
673 anna::Millisecond timeToAnswerMs = current - request;
674 a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs);
677 std::string msg = "This diameter request context lasted ";
678 msg += anna::functions::asString(timeToAnswerMs);
679 msg += " milliseconds at diameter server (included network time)";
680 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
682 // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
683 Message * requestMessage = const_cast<Message*>(response->getRequest());
684 requestMessage->setRequestServerSessionKey(response->getRequest()->getRequestServerSessionKey()); // -1 means unkown/unset
686 if(cid != helpers::base::COMMANDID__Disconnect_Peer_Answer) {
688 response->setMessage(&db);
689 // Restore received datablock
691 string msg("diameter::comm::ClientSession::receive | Restore answer to original request sequences (hop-by-hop = ");
692 msg += anna::functions::asString(response->getRequest()->getRequestHopByHop());
693 msg += ", end-to-end = ";
694 msg += anna::functions::asString(response->getRequest()->getRequestEndToEnd());
696 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
698 diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
699 diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
700 eventResponse(*response);
701 } catch(anna::RuntimeException& ex) {
706 // unbind is automatically performed, anyway we can inform to the application just in case some additional
707 // procedure could be issued:
711 response_erase(response);
715 unbind(immediateUnbind);
718 void ClientSession::finalize() throw() {
719 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
721 // Check deprecated entity:
722 const Entity *entity = getParent() /* server */ ->getParent() /* entity */;
723 // Inform father server (availability changes):
724 bool changes = a_parent->refreshAvailability();
726 const Server *server = getParent();
727 bool multipleConnections = (server->getMaxClientSessions() > 1);
728 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
729 OamModule &oamModule = OamModule::instantiate();
731 if(multipleConnections) {
732 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
733 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
735 oamModule.activateAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
736 oamModule.count(OamModule::Counter::LostAvailabilityOverClientSession);
740 void ClientSession::recover() throw() {
741 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "recover", ANNA_FILE_LOCATION));
745 } catch(anna::RuntimeException &ex) {
747 anna::comm::ClientSocket * cs = const_cast<anna::comm::ClientSocket*>(a_server->getClientSocket());
749 if(cs) cs->requestClose();
754 // Inform father server (availability changes):
755 bool changes = a_parent->refreshAvailability();
757 const Server *server = getParent();
758 bool multipleConnections = (server->getMaxClientSessions() > 1);
759 std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
760 OamModule &oamModule = OamModule::instantiate();
762 if(multipleConnections) {
763 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__ClientSessionId__d__, socket.c_str(), getSocketId());
764 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
766 oamModule.cancelAlarm(OamModule::Alarm::c_LostAvailabilityOverClientSessionWithServer__s__, socket.c_str());
767 oamModule.count(OamModule::Counter::RecoveredAvailabilityOverClientSession);
771 void ClientSession::sendDWAToServer(const anna::DataBlock& dwrDB)
772 throw(anna::RuntimeException) {
773 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWAToServer", ANNA_FILE_LOCATION));
774 anna::DataBlock dwa(true);
775 a_engine->readDWA(dwa, dwrDB); // Asume that DWA is valid ...
778 throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote server never will validate this connection health", ANNA_FILE_LOCATION);
785 //-------------------------------------------------------------------------
786 // Se invoca desde diameter::comm::Timer
787 //-------------------------------------------------------------------------
788 void ClientSession::expireResponse(diameter::comm::Response* response)
790 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
791 Session::expireResponse(response);
793 OamModule &oamModule = OamModule::instantiate();
794 oamModule.count(OamModule::Counter::RequestSentExpired);
795 oamModule.count(OamModule::Counter::RequestSentOnClientSessionExpired);
796 oamModule.activateAlarm(OamModule::Alarm::RequestSentOnClientSessionExpired);
798 // Check father server idleness:
799 if(idle()) a_parent->childIdle();
802 // LOGDEBUG(anna::Logger::debug("ClientSession is idle after an expiration...", ANNA_FILE_LOCATION));
803 // a_parent->childIdle();
806 // LOGDEBUG(anna::Logger::debug("ClientSession is busy after an expiration...", ANNA_FILE_LOCATION));
811 std::string ClientSession::asString() const
813 string result = Session::asString();
814 result += " | Parent Server: ";
815 result += anna::functions::socketLiteralAsString(getAddress(), getPort());
816 result += " | Auto-recovery: ";
817 result += (a_autoRecovery ? "yes" : "no");
818 result += " | WatchdogState: ";
819 result += asText(a_watchdogState);
820 // Diferente del timeout de ApplicationMessage:
821 result += " | Watchdog Period: ";
822 result += getTimeout().asString();
823 result += " | Hidden: ";
824 result += (hidden() ? "yes" : "no");
827 result += " | MaxConnectionDelay: ";
828 result += a_server->getMaxConnectionDelay().asString();
831 return result += " }";
834 anna::xml::Node* ClientSession::asXML(anna::xml::Node* parent) const
836 anna::xml::Node* result = Session::asXML(parent);
837 parent->createChild("diameter.comm.ClientSession");
838 result->createAttribute("ParentServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
839 result->createAttribute("AutoRecovery", (a_autoRecovery ? "yes" : "no"));
840 result->createAttribute("WatchdogState", asText(a_watchdogState));
841 // Diferente del timeout de ApplicationMessage:
842 result->createAttribute("WatchdogPeriod", getTimeout().asString());
844 if(a_server) result->createAttribute("MaxConnectionDelay", a_server->getMaxConnectionDelay().asString());
846 result->createAttribute("Hidden", hidden() ? "yes" : "no");
851 const char* ClientSession::asText(const WatchdogState::_v watchdogState)
853 static const char* text [] = { "TimerStopped", "WaitingTimerExpiration", "WaitingDWA" };
854 return text [watchdogState];
858 //------------------------------------------------------------------------------
859 //------------------------------------------------------ ClientSession::expire()
860 //------------------------------------------------------------------------------
861 void ClientSession::expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {
862 LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (watchdog timer)", ANNA_FILE_LOCATION));
864 // The client MUST NOT close the primary connection until the
865 // primary's watchdog timer has expired at least twice without a
866 // response (note that the watchdog is not sent a second time,
868 if(a_watchdogState == WatchdogState::WaitingDWA) {
869 if(a_state == State::Failover) {
870 LOGWARNING(anna::Logger::warning("Unbinding client-session: Tw expired after first DWA missing (2*Tw elapsed)", ANNA_FILE_LOCATION));
872 return; // finalize will stop the stopped timer ...
875 setState(State::Failover);
876 LOGWARNING(anna::Logger::warning("Going to Failover state: first DWA missing", ANNA_FILE_LOCATION));
877 activateTimer(); // another chance on failover
881 // WaitingTimerExpiration arrives here:
882 const Response* sent;
886 } catch(anna::RuntimeException&) {
887 LOGDEBUG(anna::Logger::debug("Failed to send DWR to the server: unbinding ...", ANNA_FILE_LOCATION));
888 setState(State::Failover);
893 LOGDEBUG(if(sent) anna::Logger::debug("DWR sent to the server", ANNA_FILE_LOCATION););
898 void ClientSession::setWatchdogPeriod(const anna::Millisecond & watchdogPeriod) throw() {
899 setTimeout(watchdogPeriod);
902 void ClientSession::setWatchdogState(WatchdogState::_v wState) throw() {
905 if(wState != a_watchdogState) {
906 std::string msg("Session watchdog state change: ");
907 msg += asText(a_watchdogState);
909 msg += asText(wState);
910 anna::Logger::debug(msg, ANNA_FILE_LOCATION);
913 a_watchdogState = wState;
917 void ClientSession::timerStopped() throw() {
918 LOGDEBUG(anna::Logger::debug("Watchdog timer stopped", ANNA_FILE_LOCATION));
919 setWatchdogState(WatchdogState::TimerStopped);
922 void ClientSession::timerStarted() throw() {
923 LOGDEBUG(anna::Logger::debug("Watchdog timer started", ANNA_FILE_LOCATION));
925 if(a_watchdogState == WatchdogState::WaitingDWA) return;
927 setWatchdogState(WatchdogState::WaitingTimerExpiration);
931 //------------------------------------------------------------------------------
932 //---------------------------------- ClientSession::updateIncomingActivityTime()
933 //------------------------------------------------------------------------------
934 void ClientSession::updateIncomingActivityTime() throw() {
935 Session::updateIncomingActivityTime();
936 a_parent->updateIncomingActivityTime();
940 //------------------------------------------------------------------------------
941 //---------------------------------- ClientSession::updateOutgoingActivityTime()
942 //------------------------------------------------------------------------------
943 void ClientSession::updateOutgoingActivityTime(void) throw() {
944 Session::updateOutgoingActivityTime();
945 a_parent->updateOutgoingActivityTime();
950 //------------------------------------------------------------------------------
951 //----------------------------------------------- ClientSession::countSendings()
952 //------------------------------------------------------------------------------
953 void ClientSession::countSendings(const diameter::CommandId & cid, bool ok)throw() {
954 OamModule &oamModule = OamModule::instantiate();
955 bool isRequest = cid.second;
959 oamModule.count(isRequest ? OamModule::Counter::RequestSentOK : OamModule::Counter::AnswerSentOK);
960 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionOK : OamModule::Counter::AnswerSentOnClientSessionOK);
962 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentOK);
963 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);
964 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentOK); // not usual (dwr was received from server)
965 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
966 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
969 oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
970 oamModule.count(isRequest ? OamModule::Counter::RequestSentOnClientSessionNOK : OamModule::Counter::AnswerSentOnClientSessionNOK);
972 if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) oamModule.count(OamModule::Counter::CERSentNOK);
973 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);
974 else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) oamModule.count(OamModule::Counter::DWASentNOK); // not usual (dwr was received from server)
975 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
976 else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);