Fix local server for multiple applications
[anna.git] / source / diameter.comm / ServerSession.cpp
index 1be1470..ab7ca11 100644 (file)
 #include <anna/diameter.comm/Response.hpp>
 #include <anna/diameter.comm/Message.hpp>
 #include <anna/diameter.comm/OamModule.hpp>
+#include <anna/diameter.comm/ApplicationMessageOamModule.hpp>
 #include <anna/diameter.comm/TimerManager.hpp>
 #include <anna/diameter.comm/Timer.hpp>
 #include <anna/diameter.comm/LocalServer.hpp>
 #include <anna/diameter.comm/ServerSessionReceiver.hpp>
 #include <anna/diameter.comm/ReceiverFactoryImpl.hpp>
+#include <anna/diameter.comm/OriginHostManager.hpp>
+#include <anna/diameter.comm/OriginHost.hpp>
 
 #include <anna/app/functions.hpp>
 #include <anna/comm/ClientSocket.hpp>
@@ -52,12 +55,10 @@ const anna::Millisecond ServerSession::DefaultAllowedInactivityTime(90000); // I
 
 
 ServerSession::ServerSession() : Session("diameter::comm::ServerSession", "Diameter Inactivity Detection Timer"),
-  a_receiverFactory(this),
-  a_cer(ClassCode::Bind),
-  a_dwr(ClassCode::ApplicationMessage) // realmente no es necesario, los Message son por defecto de aplicacion
+  a_receiverFactory(this)
 { initialize(); }
 
-void ServerSession::initialize() throw() {
+void ServerSession::initialize() {
   Session::initialize();
   a_parent = NULL;
   a_clientSocket = NULL;
@@ -66,22 +67,21 @@ void ServerSession::initialize() throw() {
 
 //ServerSession::~ServerSession() {;}
 
-void ServerSession::setClientSocket(anna::comm::ClientSocket *clientSocket) throw() {
+void ServerSession::setClientSocket(anna::comm::ClientSocket *clientSocket) {
   a_clientSocket = clientSocket;
   a_clientSocket->setReceiverFactory(a_receiverFactory);
 }
 
 
-const std::string& ServerSession::getAddress() const throw() {
+const std::string& ServerSession::getAddress() const {
   return a_parent->getKey().first;
 }
 
-int ServerSession::getPort() const throw() {
+int ServerSession::getPort() const {
   return a_parent->getKey().second;
 }
 
-
-const Response* ServerSession::send(const Message* message) throw(anna::RuntimeException) {
+const Response* ServerSession::send(const Message* message) noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "send", ANNA_FILE_LOCATION));
 
   if(!message)
@@ -90,6 +90,8 @@ const Response* ServerSession::send(const Message* message) throw(anna::RuntimeE
   // Command id:
   bool isRequest;
   diameter::CommandId cid = message->getCommandId(isRequest);
+  diameter::ApplicationId aid = message->getApplicationId();
+
   LOGDEBUG(
     std::string msg = "Sending diameter message: ";
     msg += anna::diameter::functions::commandIdAsPairString(cid);
@@ -208,16 +210,7 @@ const Response* ServerSession::send(const Message* message) throw(anna::RuntimeE
     //
     updateOutgoingActivityTime();
     // OAM
-    countSendings(cid, true /* send ok */);
-    // Trace non-application messages:
-    LOGDEBUG(
-
-      if((cid == helpers::base::COMMANDID__Device_Watchdog_Request) ||
-    (cid == helpers::base::COMMANDID__Disconnect_Peer_Request)) {
-    anna::Logger::debug("Sent DataBlock to XML representation:", ANNA_FILE_LOCATION);
-      try { anna::diameter::codec::Message msg; msg.decode(message->getBody()); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
-    }
-    );
+    countSendings(cid, aid, true /* send ok */);
 
     // Restore sequences:
     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
@@ -225,7 +218,7 @@ const Response* ServerSession::send(const Message* message) throw(anna::RuntimeE
     if(fixed) message_nc->restoreSequencesAfterFix();  // restore to application sequences after fix
 
     // OAM
-    countSendings(cid, false /* send no ok */);
+    countSendings(cid, aid, false /* send no ok */);
     throw;
   }
 
@@ -251,7 +244,7 @@ const Response* ServerSession::send(const Message* message) throw(anna::RuntimeE
 
 
 
-bool ServerSession::unbind(bool forceDisconnect) throw(anna::RuntimeException) {
+bool ServerSession::unbind(bool forceDisconnect) noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "unbind", ANNA_FILE_LOCATION));
 
   if(a_state == State::Closed)
@@ -298,37 +291,80 @@ bool ServerSession::unbind(bool forceDisconnect) throw(anna::RuntimeException) {
   return false;
 }
 
-void ServerSession::eventPeerShutdown() throw() {
+void ServerSession::eventPeerShutdown() {
   // Inform father server:
   a_parent->eventPeerShutdown(this);
 }
 
-void ServerSession::eventResponse(const Response& response) throw(anna::RuntimeException) {
+void ServerSession::eventRequestRetransmission(Message *request) {
+
+  // OAM
+  OamModule &oamModule = OamModule::instantiate();
+  oamModule.count(OamModule::Counter::RequestRetransmitted);
+  oamModule.count(OamModule::Counter::RequestRetransmittedOnServerSession);
+
   // Inform father server:
-  a_parent->eventResponse(response);
+  a_parent->eventRequestRetransmission(this, request);
 }
 
-void ServerSession::eventRequest(const anna::DataBlock &request) throw(anna::RuntimeException) {
+void ServerSession::eventResponse(const Response& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
   // Inform father server:
-  a_parent->eventRequest(this, request);
+  a_parent->eventResponse(response, myNode);
 }
 
-void ServerSession::eventUnknownResponse(const anna::DataBlock& response) throw(anna::RuntimeException) {
+void ServerSession::eventRequest(const anna::DataBlock &request, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
   // Inform father server:
-  a_parent->eventUnknownResponse(this, response);
+  a_parent->eventRequest(this, request, myNode);
 }
 
-void ServerSession::eventDPA(const anna::DataBlock& response) throw(anna::RuntimeException) {
+void ServerSession::eventUnknownResponse(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
   // Inform father server:
-  a_parent->eventDPA(this, response);
+  a_parent->eventUnknownResponse(this, response, myNode);
+}
+
+void ServerSession::eventDPA(const anna::DataBlock& response, const anna::diameter::comm::OriginHost *myNode) noexcept(false) {
+  // Inform father server:
+  a_parent->eventDPA(this, response, myNode);
+}
+
+
+anna::U32 ServerSession::getAuthApplicationIdFromCER(const anna::DataBlock &cer, bool &found) const {
+
+  anna::U32 result{};
+  found = true;
+
+  anna::diameter::codec::Message codecMsg; // codec engine to pre-assigned, but will be inferred from ApplicationId during decoding:
+  try { codecMsg.decode(cer); } catch(anna::RuntimeException &ex) { ex.trace(); found = false; return result; }
+
+  // Look at first level:
+  try {
+    result = codecMsg.getAvp(helpers::base::AVPID__Auth_Application_Id)->getUnsigned32()->getValue();
+  }
+  catch(anna::RuntimeException &ex) {
+    found = false;
+  }
+
+  // Look within Vendor-Specific-Application-Id:
+  if (!found) {
+    try {
+      result = codecMsg.getAvp(helpers::base::AVPID__Vendor_Specific_Application_Id)->getAvp(helpers::base::AVPID__Auth_Application_Id)->getUnsigned32()->getValue();
+    }
+    catch(anna::RuntimeException &ex) {
+      found = false;
+    }
+  }
+
+  return result;
 }
 
 //------------------------------------------------------------------------------------------
 // Se invoca desde el diameter::comm::Receiver
 //------------------------------------------------------------------------------------------
 void ServerSession::receive(const anna::comm::Message& message)
-throw(anna::RuntimeException) {
+noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "receive", ANNA_FILE_LOCATION));
+
+
   // Activity:
   updateIncomingActivityTime();
   activateTimer();
@@ -340,16 +376,23 @@ throw(anna::RuntimeException) {
     std::string msg = "Received diameter message: ";
     msg += anna::diameter::functions::commandIdAsPairString(cid);
     anna::Logger::debug(msg, ANNA_FILE_LOCATION);
-
-    if((cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) || (cid.first == helpers::base::COMMANDID__Device_Watchdog_Request.first))
-  try { anna::diameter::codec::Message dmsg; dmsg.decode(db); /* decode to be traced */ } catch(anna::RuntimeException&) {;}
-);
+  );
   // Main counters:
   OamModule &oamModule = OamModule::instantiate();
   oamModule.count(isRequest ? OamModule::Counter::RequestReceived : OamModule::Counter::AnswerReceived);
   oamModule.count(isRequest ? OamModule::Counter::RequestReceivedOnServerSession : OamModule::Counter::AnswerReceivedOnServerSession);
   // Statistic (size)
-  a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize()); // only on reception (application could manage sent sizes)
+  a_parent->updateReceivedMessageSizeStatisticConcept(message.getSize(), cid); // only on reception (application could manage sent sizes)
+
+  // OriginHostManager (to register remote origin host in order to associate with specific comm engine):
+  anna::diameter::comm::OriginHostManager &ohm = anna::diameter::comm::OriginHostManager::instantiate();
+
+  // Extract OriginHost from datablock (db):
+  std::string remoteOriginHost = anna::diameter::helpers::base::functions::getOriginHost(db);
+  LOGDEBUG(anna::Logger::debug(anna::functions::asString("REMOTE ORIGIN HOST FOR THE MESSAGE RECEIVED: %s", remoteOriginHost.c_str()), ANNA_FILE_LOCATION));
+
+  // Now, get the corresponding own origin host for it; in case of CER received, this will be unkonwn:
+  const anna::diameter::comm::OriginHost *originHost = ohm.getOriginHostForRemoteOriginHost(remoteOriginHost);
 
   if(isRequest) {
     // Si recibo un request, el message solo tiene fiable el DataBlock. Como por defecto se construye como ApplicationMessage,
@@ -361,14 +404,35 @@ throw(anna::RuntimeException) {
 
     // Received CER
     if(cid == helpers::base::COMMANDID__Capabilities_Exchange_Request) {
+
+      // For CERs, we need to extract the Auth-Application-Id:
+      bool found;
+      anna::U32 authApplicationId = getAuthApplicationIdFromCER(db, found);
+
+      // Now, sequential search in OriginHostManager for that id:
+      anna::diameter::comm::OriginHost *originHost = ohm.getOriginHost(authApplicationId);
+
+      if (!originHost) {
+        LOGWARNING(anna::Logger::warning("DIAMETER_NO_COMMON_APPLICATION with received CER. TODO: send CEA with that result code", ANNA_FILE_LOCATION));
+        unbind(true /* always immediate */);
+        return;
+      }
+
+      // Map origin host of received CER, to own OriginHost pointer. This will be used in future: DWR, DPR, normal messages
+      ohm.registerRemoteOriginHost(remoteOriginHost, originHost->getName());
+
+
+      // OAM
       oamModule.count(OamModule::Counter::CERReceived);
 
       if(a_state == State::Bound) {
         LOGWARNING(anna::Logger::warning("Received another CER over already bound connection. Anyway, will be replied with CEA", ANNA_FILE_LOCATION));
       }
 
-      a_cer.setBody(db);
-      sendCEA();
+      // Basic DRA:
+      originHost->getCommEngine()->manageDrDhServerSession(this, true /* register */);
+
+      sendCEA(originHost->getCommEngine(), db);
       //activateTimer(); // Ya se invoca al inicio de este metodo ::receive
       //bool changes = a_parent->refreshAvailability();
       return; // (*)
@@ -376,26 +440,34 @@ throw(anna::RuntimeException) {
     // Received DWR
     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) {
       oamModule.count(OamModule::Counter::DWRReceived);
-      a_dwr.setBody(db);
-      sendDWA();
+
+      if (!originHost) return; // TODO, responding DWA with result code error
+
+      sendDWA(originHost->getCommEngine(), db);
       return; // (**)
     }
     // Received DPR
     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) {
       oamModule.count(OamModule::Counter::DPRReceived);
 
+      if (!originHost) return; // TODO, responding DPA with result code error
+
       if(a_state == State::Bound) {
-        a_dpr.setBody(db);
         setState(State::Disconnecting);
         LOGWARNING(anna::Logger::warning("DPR has been received from peer (diameter client)", ANNA_FILE_LOCATION));
         // Ignore pending on server sessions:
-        /*if (getOTARequests() == 0) */sendDPA();
+        /*if (getOTARequests() == 0) */sendDPA(originHost->getCommEngine(), db);
         return; // DPR won't be informed because virtual readDPA is available for this
       }
     }
 
     try {
-      eventRequest(db);
+      // application message counters
+      ApplicationMessageOamModule::instantiate().count(cid.first, -1 /* no result code */, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Request_Received_AsServer);
+
+      if (!originHost) return; // TODO, responding DWA with result code error
+
+      eventRequest(db, originHost);
     } catch(anna::RuntimeException& ex) {
       ex.trace();
     }
@@ -429,7 +501,8 @@ throw(anna::RuntimeException) {
       }
     }
 
-    eventDPA(db);
+    if (originHost)
+      eventDPA(db, originHost);
 
   } else if(cid == helpers::base::COMMANDID__Device_Watchdog_Answer) {  // non usual (server should not send DWR's)
     oamModule.count(OamModule::Counter::DWAReceived);
@@ -444,7 +517,13 @@ throw(anna::RuntimeException) {
     oamModule.count(OamModule::Counter::AnswerReceivedUnknown);
     oamModule.count(OamModule::Counter::AnswerReceivedOnServerSessionUnknown);
     oamModule.activateAlarm(OamModule::Alarm::AnswerReceivedOnServerSessionUnknown);
-    eventUnknownResponse(db);
+
+    // application message counters
+    ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_UnknownReceived_AsServer);
+
+    if (originHost)
+      eventUnknownResponse(db, originHost);
+
     string msg(asString());
     msg += anna::functions::asString(" | Response received from client, for non registered context (HopByHop: %u)", hopByHop);
     throw anna::RuntimeException(msg, ANNA_FILE_LOCATION);
@@ -463,14 +542,14 @@ throw(anna::RuntimeException) {
   anna::Millisecond current = (anna::Millisecond)anna::functions::millisecond();
   anna::Millisecond request = response->getRequest()->getRequestTimestampMs();
   anna::Millisecond timeToAnswerMs = current - request;
-  a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs);
-  LOGDEBUG
-  (
-    std::string msg = "This diameter request context lasted ";
-    msg += anna::functions::asString(timeToAnswerMs);
-    msg += " milliseconds at diameter client (included network time)";
-    anna::Logger::debug(msg, ANNA_FILE_LOCATION);
-  );
+  a_parent->updateProcessingTimeStatisticConcept(timeToAnswerMs, cid);
+  //LOGDEBUG
+  //(
+  //  std::string msg = "This diameter request context lasted ";
+  //  msg += anna::functions::asString(timeToAnswerMs);
+  //  msg += " milliseconds at diameter client (included network time)";
+  //  anna::Logger::debug(msg, ANNA_FILE_LOCATION);
+  //);
   // Progress origin for tracking purposes on asyncronous boxes with both diameter interfaces (entities and clients)
   Message * requestMessage = const_cast<Message*>(response->getRequest());
   requestMessage->setRequestClientSessionKey(response->getRequest()->getRequestClientSessionKey()); // "" means unkown/unset
@@ -490,7 +569,13 @@ throw(anna::RuntimeException) {
       );
       diameter::codec::functions::setHopByHop((anna::DataBlock&)db, response->getRequest()->getRequestHopByHop());
       diameter::codec::functions::setEndToEnd((anna::DataBlock&)db, response->getRequest()->getRequestEndToEnd());
-      eventResponse(*response);
+
+      // application message counters
+      ApplicationMessageOamModule::instantiate().count(cid.first, resultCode, anna::diameter::codec::functions::getApplicationId(db), ApplicationMessageOamModule::Counter::Answer_Received_AsServer);
+
+      if (originHost)
+        eventResponse(*response, originHost);
+
     } catch(anna::RuntimeException& ex) {
       ex.trace();
     }
@@ -503,7 +588,7 @@ throw(anna::RuntimeException) {
     unbind(true /* always immediate */);
 }
 
-void ServerSession::finalize() throw() {
+void ServerSession::finalize() {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "finalize", ANNA_FILE_LOCATION));
   // Configuration overiddings
   setOnDisconnect(OnDisconnect::IgnorePendings);
@@ -522,7 +607,7 @@ void ServerSession::finalize() throw() {
   }
 
   // Inform father local server (availability changes):
-  bool changes = getParent()->refreshAvailability();
+  getParent()->refreshAvailability();
   // OAM
   bool multipleConnections = (getParent()->getMaxConnections() > 1);
   std::string socket = anna::functions::socketLiteralAsString(getAddress(), getPort());
@@ -539,11 +624,12 @@ void ServerSession::finalize() throw() {
 
 
 
-void ServerSession::sendCEA()
-throw(anna::RuntimeException) {
+void ServerSession::sendCEA(const Engine *commEngine, const anna::DataBlock &cerDataBlock)
+noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendCEA", ANNA_FILE_LOCATION));
+
   anna::DataBlock cea(true);
-  a_engine->readCEA(cea, a_cer.getBody()); // Asume that CEA is valid ...
+  commEngine->readCEA(cea, cerDataBlock); // Asume that CEA is valid ...
   // If one peer sends a CER message to another Peer and receiver does not have support for
   //
   //  1) any common application then it must return the CEA with Result-Code Avp set to DIAMETER_NO_COMMON_APPLICATION
@@ -555,7 +641,7 @@ throw(anna::RuntimeException) {
 
   if(cea.isEmpty()) {
     LOGDEBUG(anna::Logger::debug("Empty CEA message. Remote client never will bound this connection at application level", ANNA_FILE_LOCATION));
-    LOGWARNING(anna::Logger::warning("Discarding received CER without sending CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
+    LOGWARNING(anna::Logger::warning("Discarding received CER: cannot send empty CEA (consider to send CEA with Result-Code DIAMETER_UNKNOWN_PEER)", ANNA_FILE_LOCATION));
     return;
   }
 
@@ -590,11 +676,11 @@ throw(anna::RuntimeException) {
   }
 }
 
-void ServerSession::sendDWA()
-throw(anna::RuntimeException) {
+void ServerSession::sendDWA(const Engine *commEngine, const anna::DataBlock &dwrDataBlock)
+noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "sendDWA", ANNA_FILE_LOCATION));
   anna::DataBlock dwa(true);
-  a_engine->readDWA(dwa, a_dwr.getBody()); // Asume that DWA is valid ...
+  commEngine->readDWA(dwa, dwrDataBlock); // Asume that DWA is valid ...
 
   if(dwa.isEmpty())
     throw anna::RuntimeException("This diameter agent defines an empty DWA message. Remote client never will validate this connection health", ANNA_FILE_LOCATION);
@@ -609,7 +695,7 @@ throw(anna::RuntimeException) {
 // Se invoca desde diameter::comm::Timer
 //-------------------------------------------------------------------------
 void ServerSession::expireResponse(diameter::comm::Response* response)
-throw() {
+{
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expireResponse", ANNA_FILE_LOCATION));
   Session::expireResponse(response);
   // OAM
@@ -622,7 +708,7 @@ throw() {
 }
 
 std::string ServerSession::asString() const
-throw() {
+{
   string result = Session::asString();
   result += " | Parent Local Server: ";
   result += anna::functions::socketLiteralAsString(getAddress(), getPort());
@@ -637,7 +723,7 @@ throw() {
 }
 
 anna::xml::Node* ServerSession::asXML(anna::xml::Node* parent) const
-throw() {
+{
   anna::xml::Node* result = Session::asXML(parent);
   parent->createChild("diameter.comm.ServerSession");
   result->createAttribute("ParentLocalServer", anna::functions::socketLiteralAsString(getAddress(), getPort()));
@@ -652,7 +738,7 @@ throw() {
 //------------------------------------------------------------------------------
 //------------------------------------------------------ ServerSession::expire()
 //------------------------------------------------------------------------------
-void ServerSession::expire(anna::timex::Engine *timeController) throw(anna::RuntimeException) {
+void ServerSession::expire(anna::timex::Engine *timeController) noexcept(false) {
   LOGMETHOD(anna::TraceMethod traceMethod(a_className, "expire (inactivity check timer)", ANNA_FILE_LOCATION));
   LOGWARNING(anna::Logger::warning("Detecting anomaly (too inactivity time) over server session. Resetting", ANNA_FILE_LOCATION));
   // OAM
@@ -671,14 +757,14 @@ void ServerSession::expire(anna::timex::Engine *timeController) throw(anna::Runt
   unbind(true /* always immediate */); // no delegamos en un planning o similar
 }
 
-void ServerSession::setAllowedInactivityTime(const anna::Millisecond & allowedInactivityTime) throw() {
+void ServerSession::setAllowedInactivityTime(const anna::Millisecond & allowedInactivityTime) {
   setTimeout(allowedInactivityTime);
 }
 
 //------------------------------------------------------------------------------
 //---------------------------------- ServerSession::updateIncomingActivityTime()
 //------------------------------------------------------------------------------
-void ServerSession::updateIncomingActivityTime() throw() {
+void ServerSession::updateIncomingActivityTime() {
   Session::updateIncomingActivityTime();
   a_parent->updateIncomingActivityTime();
 }
@@ -687,7 +773,7 @@ void ServerSession::updateIncomingActivityTime() throw() {
 //------------------------------------------------------------------------------
 //---------------------------------- ServerSession::updateOutgoingActivityTime()
 //------------------------------------------------------------------------------
-void ServerSession::updateOutgoingActivityTime(void) throw() {
+void ServerSession::updateOutgoingActivityTime(void) {
   Session::updateOutgoingActivityTime();
   a_parent->updateOutgoingActivityTime();
 }
@@ -696,8 +782,10 @@ void ServerSession::updateOutgoingActivityTime(void) throw() {
 //------------------------------------------------------------------------------
 //----------------------------------------------- ServerSession::countSendings()
 //------------------------------------------------------------------------------
-void ServerSession::countSendings(const diameter::CommandId & cid, bool ok)throw() {
+void ServerSession::countSendings(const diameter::CommandId & cid, unsigned int aid, bool ok){
   OamModule &oamModule = OamModule::instantiate();
+  ApplicationMessageOamModule &appMsgOamModule = ApplicationMessageOamModule::instantiate();
+
   bool isRequest = cid.second;
 
   if(ok) {
@@ -710,6 +798,10 @@ void ServerSession::countSendings(const diameter::CommandId & cid, bool ok)throw
     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentOK);  // not usual
     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentOK);
     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentOK);
+    // Application messages:
+    else {
+      appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentOK_AsServer : ApplicationMessageOamModule::Counter::Answer_SentOK_AsServer);
+    }
   } else {
     // Main counters:
     oamModule.count(isRequest ? OamModule::Counter::RequestSentNOK : OamModule::Counter::AnswerSentNOK);
@@ -720,6 +812,10 @@ void ServerSession::countSendings(const diameter::CommandId & cid, bool ok)throw
     else if(cid == helpers::base::COMMANDID__Device_Watchdog_Request) oamModule.count(OamModule::Counter::DWRSentNOK);  // not usual
     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Answer) oamModule.count(OamModule::Counter::DPASentNOK);
     else if(cid == helpers::base::COMMANDID__Disconnect_Peer_Request) oamModule.count(OamModule::Counter::DPRSentNOK);
+    // Application messages:
+    else {
+      appMsgOamModule.count(cid.first, -1 /* no result code */, aid, isRequest ? ApplicationMessageOamModule::Counter::Request_SentNOK_AsServer : ApplicationMessageOamModule::Counter::Answer_SentNOK_AsServer);
+    }
   }
 }