face: Moving idle UdpFace closing logic to UdpFace class

Change-Id: Ia7310fa18681f17d93c8214ce744da909fea7022
Refs: #1686
diff --git a/daemon/face/face-flags.hpp b/daemon/face/face-flags.hpp
new file mode 100644
index 0000000..1451a27
--- /dev/null
+++ b/daemon/face/face-flags.hpp
@@ -0,0 +1,48 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014  Regents of the University of California,
+ *                     Arizona Board of Regents,
+ *                     Colorado State University,
+ *                     University Pierre & Marie Curie, Sorbonne University,
+ *                     Washington University in St. Louis,
+ *                     Beijing Institute of Technology
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ **/
+
+#ifndef NFD_DAEMON_MGMT_FACE_FLAGS_HPP
+#define NFD_DAEMON_MGMT_FACE_FLAGS_HPP
+
+#include "face/face.hpp"
+#include <ndn-cxx/management/nfd-face-flags.hpp>
+
+namespace nfd {
+
+inline uint64_t
+getFaceFlags(const Face& face)
+{
+  uint64_t flags = 0;
+  if (face.isLocal()) {
+    flags |= ndn::nfd::FACE_IS_LOCAL;
+  }
+  if (face.isOnDemand()) {
+    flags |= ndn::nfd::FACE_IS_ON_DEMAND;
+  }
+  return flags;
+}
+
+} // namespace nfd
+
+#endif // NFD_DAEMON_MGMT_FACE_FLAGS_HPP
diff --git a/daemon/face/face.cpp b/daemon/face/face.cpp
index 9d2368b..ea08f5b 100644
--- a/daemon/face/face.cpp
+++ b/daemon/face/face.cpp
@@ -24,6 +24,7 @@
  **/
 
 #include "face.hpp"
+#include "face-flags.hpp"
 #include "core/logger.hpp"
 
 namespace nfd {
@@ -128,4 +129,23 @@
   this->onFail(reason);
 }
 
+ndn::nfd::FaceStatus
+Face::getFaceStatus() const
+{
+  const FaceCounters& counters = getCounters();
+
+  ndn::nfd::FaceStatus status;
+  status.setFaceId(getId())
+    .setRemoteUri(getRemoteUri().toString())
+    .setLocalUri(getLocalUri().toString())
+    .setFlags(getFaceFlags(*this))
+    .setNInInterests(counters.getNInInterests())
+    .setNInDatas(counters.getNInDatas())
+    .setNOutInterests(counters.getNOutInterests())
+    .setNOutDatas(counters.getNOutDatas());
+
+  return status;
+}
+
+
 } //namespace nfd
diff --git a/daemon/face/face.hpp b/daemon/face/face.hpp
index 987853e..2660045 100644
--- a/daemon/face/face.hpp
+++ b/daemon/face/face.hpp
@@ -31,6 +31,8 @@
 #include "core/face-uri.hpp"
 #include "face-counter.hpp"
 
+#include <ndn-cxx/management/nfd-face-status.hpp>
+
 namespace nfd {
 
 /** \class FaceId
@@ -149,6 +151,11 @@
   const FaceUri&
   getLocalUri() const;
 
+  /** \return FaceStatus data structure filled with the current Face status
+   */
+  virtual ndn::nfd::FaceStatus
+  getFaceStatus() const;
+
 protected:
   // this is a non-virtual method
   bool
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index 684879a..b01d699 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -61,15 +61,6 @@
   }
 
   this->setUri(FaceUri(localEndpoint));
-
-  //setting the timeout to close the idle faces
-  m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
-                                bind(&UdpChannel::closeIdleFaces, this));
-}
-
-UdpChannel::~UdpChannel()
-{
-  scheduler::cancel(m_closeIdleFaceEvent);
 }
 
 void
@@ -185,7 +176,7 @@
   ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
   if (faceMapPos == m_channelFaces.end())
     {
-      face = make_shared<UdpFace>(socket, isOnDemand);
+      face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
       face->onFail += bind(&UdpChannel::afterFaceFailed, this, remoteEndpoint);
 
       m_channelFaces[remoteEndpoint] = face;
@@ -263,26 +254,4 @@
   m_channelFaces.erase(endpoint);
 }
 
-void
-UdpChannel::closeIdleFaces()
-{
-  ChannelFaceMap::iterator next =  m_channelFaces.begin();
-
-  while (next != m_channelFaces.end()) {
-    ChannelFaceMap::iterator it = next;
-    next++;
-    if (it->second->isOnDemand() &&
-        !it->second->hasBeenUsedRecently()) {
-      //face has been idle since the last time closeIdleFaces
-      //has been called. Going to close it
-      NFD_LOG_DEBUG("Found idle face id: " << it->second->getId());
-      it->second->close();
-    } else {
-      it->second->resetRecentUsage();
-    }
-  }
-  m_closeIdleFaceEvent = scheduler::schedule(m_idleFaceTimeout,
-                                             bind(&UdpChannel::closeIdleFaces, this));
-}
-
 } // namespace nfd
diff --git a/daemon/face/udp-channel.hpp b/daemon/face/udp-channel.hpp
index 4db6e43..fed232a 100644
--- a/daemon/face/udp-channel.hpp
+++ b/daemon/face/udp-channel.hpp
@@ -27,7 +27,6 @@
 
 #include "channel.hpp"
 #include "core/global-io.hpp"
-#include "core/scheduler.hpp"
 #include "udp-face.hpp"
 
 namespace nfd {
@@ -65,9 +64,6 @@
   UdpChannel(const udp::Endpoint& localEndpoint,
              const time::seconds& timeout);
 
-  virtual
-  ~UdpChannel();
-
   /**
    * \brief Enable listening on the local endpoint, accept connections,
    *        and create faces when remote host makes a connection
@@ -137,9 +133,6 @@
                            const ConnectFailedCallback& onConnectFailed,
                            const shared_ptr<boost::asio::ip::udp::resolver>& resolver);
 
-  void
-  closeIdleFaces();
-
 private:
   udp::Endpoint m_localEndpoint;
 
@@ -178,9 +171,6 @@
    *        faces will be removed
    */
   time::seconds m_idleFaceTimeout;
-
-  EventId m_closeIdleFaceEvent;
-
 };
 
 } // namespace nfd
diff --git a/daemon/face/udp-face.cpp b/daemon/face/udp-face.cpp
index d2d7823..d4fe8cf 100644
--- a/daemon/face/udp-face.cpp
+++ b/daemon/face/udp-face.cpp
@@ -33,10 +33,14 @@
 
 NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(DatagramFace, UdpFace::protocol, "UdpFace");
 
-UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket, bool isOnDemand)
+UdpFace::UdpFace(const shared_ptr<UdpFace::protocol::socket>& socket,
+                 bool isOnDemand,
+                 const time::seconds& idleTimeout)
   : DatagramFace<protocol>(FaceUri(socket->remote_endpoint()),
                            FaceUri(socket->local_endpoint()),
                            socket, isOnDemand)
+  , m_idleTimeout(idleTimeout)
+  , m_lastIdleCheck(time::steady_clock::now())
 {
 #ifdef __linux__
   //
@@ -60,6 +64,16 @@
                    << "] Failed to disable path MTU discovery");
     }
 #endif
+
+  if (isOnDemand && m_idleTimeout > time::seconds::zero()) {
+    m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                             bind(&UdpFace::closeIfIdle, this));
+  }
+}
+
+UdpFace::~UdpFace()
+{
+  scheduler::cancel(m_closeIfIdleEvent);
 }
 
 void
@@ -84,4 +98,49 @@
   receiveDatagram(buffer, nBytesReceived, error);
 }
 
+ndn::nfd::FaceStatus
+UdpFace::getFaceStatus() const
+{
+  ndn::nfd::FaceStatus status = Face::getFaceStatus();
+  if (isOnDemand()) {
+    time::milliseconds left = time::duration_cast<time::milliseconds>(
+      time::steady_clock::now() - m_lastIdleCheck);
+
+    if (hasBeenUsedRecently()) {
+      status.setExpirationPeriod(left + m_idleTimeout);
+    }
+    else {
+      status.setExpirationPeriod(left);
+    }
+  }
+  return status;
+}
+
+void
+UdpFace::closeIfIdle()
+{
+  // Face can be switched from on-demand to non-on-demand mode
+  // (non-on-demand -> on-demand transition is not allowed)
+  if (isOnDemand()) {
+    if (!hasBeenUsedRecently()) {
+      // face has been idle since the last time closeIfIdle
+      // has been called. Going to close it
+      NFD_LOG_DEBUG("Found idle face id: " << getId());
+
+      NFD_LOG_INFO("[id:" << this->getId()
+                   << ",endpoint:" << m_socket->local_endpoint()
+                   << "] Idle for more than " << m_idleTimeout << ", closing");
+      close();
+    }
+    else {
+      resetRecentUsage();
+
+      m_lastIdleCheck = time::steady_clock::now();
+      m_closeIfIdleEvent = scheduler::schedule(m_idleTimeout,
+                                               bind(&UdpFace::closeIfIdle, this));
+    }
+  }
+  // else do nothing and do not reschedule the event
+}
+
 } // namespace nfd
diff --git a/daemon/face/udp-face.hpp b/daemon/face/udp-face.hpp
index ec11682..2751517 100644
--- a/daemon/face/udp-face.hpp
+++ b/daemon/face/udp-face.hpp
@@ -26,6 +26,7 @@
 #define NFD_DAEMON_FACE_UDP_FACE_HPP
 
 #include "datagram-face.hpp"
+#include "core/scheduler.hpp"
 
 namespace nfd {
 
@@ -37,7 +38,11 @@
 {
 public:
   UdpFace(const shared_ptr<protocol::socket>& socket,
-          bool isOnDemand);
+          bool isOnDemand,
+          const time::seconds& idleTimeout);
+
+  virtual
+  ~UdpFace();
 
   /**
    * \brief Manages the first datagram received by the UdpChannel socket set on listening
@@ -46,6 +51,18 @@
   handleFirstReceive(const uint8_t* buffer,
                      std::size_t nBytesReceived,
                      const boost::system::error_code& error);
+
+  virtual ndn::nfd::FaceStatus
+  getFaceStatus() const;
+
+private:
+  void
+  closeIfIdle();
+
+private:
+  EventId m_closeIfIdleEvent;
+  time::seconds m_idleTimeout;
+  time::steady_clock::TimePoint m_lastIdleCheck;
 };
 
 } // namespace nfd