face: Finalizing separation between registerPrefix and setInterstFilter

New registerPrefix and setInterestFilter methods allow distinct
operations of registering with local NDN forwarder (e.g., using RIB
management protocol) and setting up application-specific OnInterest call
dispatch using InterestFilters.

Change-Id: I7e4f069da131a21574a2fd9cd20e8703c34e7008
Refs: #1275
diff --git a/src/detail/interest-filter-record.hpp b/src/detail/interest-filter-record.hpp
index cf5f253..d0c2f9c 100644
--- a/src/detail/interest-filter-record.hpp
+++ b/src/detail/interest-filter-record.hpp
@@ -76,7 +76,7 @@
   }
 
   bool
-  operator()(const shared_ptr<InterestFilter>& interestFilterId) const
+  operator()(const shared_ptr<InterestFilterRecord>& interestFilterId) const
   {
     return (reinterpret_cast<const InterestFilterId*>(interestFilterId.get()) == m_id);
   }
diff --git a/src/face.cpp b/src/face.cpp
index b1d34fd..ee35f28 100644
--- a/src/face.cpp
+++ b/src/face.cpp
@@ -226,10 +226,11 @@
 
 template<class SignatureGenerator>
 const RegisteredPrefixId*
-Face::setInterestFilterImpl(const InterestFilter& interestFilter,
-                            const OnInterest& onInterest,
-                            const OnSetInterestFilterFailed& onSetInterestFilterFailed,
-                            const SignatureGenerator& signatureGenerator)
+Face::registerPrefixImpl(const Name& prefix,
+                         const shared_ptr<InterestFilterRecord>& filter,
+                         const RegisterPrefixSuccessCallback& onSuccess,
+                         const RegisterPrefixFailureCallback& onFailure,
+                         const SignatureGenerator& signatureGenerator)
 {
   typedef void (nfd::Controller::*Registrator)
     (const nfd::ControlParameters&,
@@ -248,11 +249,8 @@
     unregistrator = static_cast<Registrator>(&nfd::Controller::start<nfd::FibRemoveNextHopCommand>);
   }
 
-  shared_ptr<InterestFilterRecord> filter =
-    make_shared<InterestFilterRecord>(interestFilter, onInterest);
-
   nfd::ControlParameters parameters;
-  parameters.setName(interestFilter.getPrefix());
+  parameters.setName(prefix);
 
   RegisteredPrefix::Unregistrator bindedUnregistrator =
     bind(unregistrator, m_nfdController, parameters, _1, _2,
@@ -260,11 +258,11 @@
          m_nfdController->getDefaultCommandTimeout());
 
   shared_ptr<RegisteredPrefix> prefixToRegister =
-    ndn::make_shared<RegisteredPrefix>(interestFilter.getPrefix(), filter, bindedUnregistrator);
+    ndn::make_shared<RegisteredPrefix>(prefix, filter, bindedUnregistrator);
 
   (m_nfdController->*registrator)(parameters,
-    bind(&Face::afterPrefixRegistered, this, prefixToRegister),
-    bind(onSetInterestFilterFailed, prefixToRegister->getPrefix(), _2),
+    bind(&Face::afterPrefixRegistered, this, prefixToRegister, onSuccess),
+    bind(onFailure, prefixToRegister->getPrefix(), _2),
     signatureGenerator,
     m_nfdController->getDefaultCommandTimeout());
 
@@ -274,53 +272,154 @@
 const RegisteredPrefixId*
 Face::setInterestFilter(const InterestFilter& interestFilter,
                         const OnInterest& onInterest,
-                        const OnSetInterestFilterFailed& onSetInterestFilterFailed)
-{
-  return setInterestFilterImpl(interestFilter, onInterest, onSetInterestFilterFailed,
-                               IdentityCertificate());
-}
-
-const RegisteredPrefixId*
-Face::setInterestFilter(const InterestFilter& interestFilter,
-                        const OnInterest& onInterest,
-                        const OnSetInterestFilterFailed& onSetInterestFilterFailed,
+                        const RegisterPrefixSuccessCallback& onSuccess,
+                        const RegisterPrefixFailureCallback& onFailure,
                         const IdentityCertificate& certificate)
 {
-  return setInterestFilterImpl(interestFilter, onInterest, onSetInterestFilterFailed,
-                               certificate);
+  shared_ptr<InterestFilterRecord> filter =
+    make_shared<InterestFilterRecord>(interestFilter, onInterest);
+
+  return registerPrefixImpl(interestFilter.getPrefix(), filter,
+                            onSuccess, onFailure,
+                            certificate);
 }
 
 const RegisteredPrefixId*
 Face::setInterestFilter(const InterestFilter& interestFilter,
                         const OnInterest& onInterest,
-                        const OnSetInterestFilterFailed& onSetInterestFilterFailed,
+                        const RegisterPrefixFailureCallback& onFailure,
+                        const IdentityCertificate& certificate)
+{
+  shared_ptr<InterestFilterRecord> filter =
+    make_shared<InterestFilterRecord>(interestFilter, onInterest);
+
+  return registerPrefixImpl(interestFilter.getPrefix(), filter,
+                            RegisterPrefixSuccessCallback(), onFailure,
+                            certificate);
+}
+
+const RegisteredPrefixId*
+Face::setInterestFilter(const InterestFilter& interestFilter,
+                        const OnInterest& onInterest,
+                        const RegisterPrefixSuccessCallback& onSuccess,
+                        const RegisterPrefixFailureCallback& onFailure,
                         const Name& identity)
 {
-  return setInterestFilterImpl(interestFilter, onInterest, onSetInterestFilterFailed,
-                               identity);
+  shared_ptr<InterestFilterRecord> filter =
+    make_shared<InterestFilterRecord>(interestFilter, onInterest);
+
+  return registerPrefixImpl(interestFilter.getPrefix(), filter,
+                            onSuccess, onFailure,
+                            identity);
+}
+
+const RegisteredPrefixId*
+Face::setInterestFilter(const InterestFilter& interestFilter,
+                        const OnInterest& onInterest,
+                        const RegisterPrefixFailureCallback& onFailure,
+                        const Name& identity)
+{
+  shared_ptr<InterestFilterRecord> filter =
+    make_shared<InterestFilterRecord>(interestFilter, onInterest);
+
+  return registerPrefixImpl(interestFilter.getPrefix(), filter,
+                            RegisterPrefixSuccessCallback(), onFailure,
+                            identity);
+}
+
+
+const InterestFilterId*
+Face::setInterestFilter(const InterestFilter& interestFilter,
+                        const OnInterest& onInterest)
+{
+  shared_ptr<InterestFilterRecord> filter =
+    make_shared<InterestFilterRecord>(interestFilter, onInterest);
+
+  getIoService().post(bind(&Face::asyncSetInterestFilter, this, filter));
+
+  return reinterpret_cast<const InterestFilterId*>(filter.get());
 }
 
 void
-Face::afterPrefixRegistered(const shared_ptr<RegisteredPrefix>& registeredPrefix)
+Face::asyncSetInterestFilter(const shared_ptr<InterestFilterRecord>& interestFilterRecord)
+{
+  m_interestFilterTable.push_back(interestFilterRecord);
+}
+
+const RegisteredPrefixId*
+Face::registerPrefix(const Name& prefix,
+                     const RegisterPrefixSuccessCallback& onSuccess,
+                     const RegisterPrefixFailureCallback& onFailure,
+                     const IdentityCertificate& certificate)
+{
+  return registerPrefixImpl(prefix, shared_ptr<InterestFilterRecord>(),
+                            onSuccess, onFailure,
+                            certificate);
+}
+
+const RegisteredPrefixId*
+Face::registerPrefix(const Name& prefix,
+                     const RegisterPrefixSuccessCallback& onSuccess,
+                     const RegisterPrefixFailureCallback& onFailure,
+                     const Name& identity)
+{
+  return registerPrefixImpl(prefix, shared_ptr<InterestFilterRecord>(),
+                            onSuccess, onFailure,
+                            identity);
+}
+
+
+void
+Face::afterPrefixRegistered(const shared_ptr<RegisteredPrefix>& registeredPrefix,
+                            const RegisterPrefixSuccessCallback& onSuccess)
 {
   m_registeredPrefixTable.push_back(registeredPrefix);
 
-  if (static_cast<bool>(registeredPrefix->getFilter()))
-    {
-      // it was a combined operation
-      m_interestFilterTable.push_back(registeredPrefix->getFilter());
-    }
+  if (static_cast<bool>(registeredPrefix->getFilter())) {
+    // it was a combined operation
+    m_interestFilterTable.push_back(registeredPrefix->getFilter());
+  }
+
+  if (static_cast<bool>(onSuccess)) {
+    onSuccess(registeredPrefix->getPrefix());
+  }
 }
 
 void
 Face::unsetInterestFilter(const RegisteredPrefixId* registeredPrefixId)
 {
-  m_ioService->post(bind(&Face::asyncUnsetInterestFilter, this, registeredPrefixId));
+  m_ioService->post(bind(&Face::asyncUnregisterPrefix, this, registeredPrefixId,
+                         UnregisterPrefixSuccessCallback(), UnregisterPrefixFailureCallback()));
 }
 
 
 void
-Face::asyncUnsetInterestFilter(const RegisteredPrefixId* registeredPrefixId)
+Face::unregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
+                       const UnregisterPrefixSuccessCallback& onSuccess,
+                       const UnregisterPrefixFailureCallback& onFailure)
+{
+  m_ioService->post(bind(&Face::asyncUnregisterPrefix, this, registeredPrefixId,
+                         onSuccess, onFailure));
+}
+
+
+void
+Face::asyncUnsetInterestFilter(const InterestFilterId* interestFilterId)
+{
+  InterestFilterTable::iterator i = std::find_if(m_interestFilterTable.begin(),
+                                                 m_interestFilterTable.end(),
+                                                 MatchInterestFilterId(interestFilterId));
+  if (i != m_interestFilterTable.end())
+    {
+      m_interestFilterTable.erase(i);
+    }
+}
+
+
+void
+Face::asyncUnregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
+                            const UnregisterPrefixSuccessCallback& onSuccess,
+                            const UnregisterPrefixFailureCallback& onFailure)
 {
   RegisteredPrefixTable::iterator i = std::find_if(m_registeredPrefixTable.begin(),
                                                    m_registeredPrefixTable.end(),
@@ -334,15 +433,18 @@
           m_interestFilterTable.remove(filter);
         }
 
-      (*i)->unregister(bind(&Face::finalizeUnregisterPrefix, this, i),
-                       RegisteredPrefix::FailureCallback());
+      (*i)->unregister(bind(&Face::finalizeUnregisterPrefix, this, i, onSuccess),
+                       bind(onFailure, _2));
     }
+  else
+    onFailure("Unrecognized PrefixId");
 
   // there cannot be two registered prefixes with the same id
 }
 
 void
-Face::finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item)
+Face::finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item,
+                               const UnregisterPrefixSuccessCallback& onSuccess)
 {
   m_registeredPrefixTable.erase(item);
 
@@ -353,6 +455,7 @@
         m_processEventsTimeoutTimer->cancel();
       }
     }
+  onSuccess();
 }
 
 void
@@ -408,7 +511,9 @@
   m_pendingInterestTable.clear();
   m_registeredPrefixTable.clear();
 
-  m_transport->close();
+  if (m_transport->isConnected())
+    m_transport->close();
+
   m_pitTimeoutCheckTimer->cancel();
   m_processEventsTimeoutTimer->cancel();
   m_pitTimeoutCheckTimerActive = false;
diff --git a/src/face.hpp b/src/face.hpp
index e524b8b..11800c8 100644
--- a/src/face.hpp
+++ b/src/face.hpp
@@ -18,6 +18,7 @@
 #include "common.hpp"
 #include "interest.hpp"
 #include "data.hpp"
+#include "security/identity-certificate.hpp"
 
 #include "transport/transport.hpp"
 #include "transport/unix-transport.hpp"
@@ -32,28 +33,41 @@
 namespace nfd {
 class Controller;
 }
-class IdentityCertificate;
 
 /**
- * An OnData function object is used to pass a callback to expressInterest.
+ * @brief Callback called when expressed Interest gets satisfied with Data packet
  */
 typedef function<void(const Interest&, Data&)> OnData;
 
 /**
- * An OnTimeout function object is used to pass a callback to expressInterest.
+ * @brief Callback called when expressed Interest times out
  */
 typedef function<void(const Interest&)> OnTimeout;
 
 /**
- * An OnInterest function object is used to pass a callback to registerPrefix.
+ * @brief Callback called when incoming Interest matches the specified InterestFilter
  */
 typedef function<void (const InterestFilter&, const Interest&)> OnInterest;
 
 /**
- * An OnRegisterFailed function object is used to report when registerPrefix fails.
+ * @brief Callback called when registerPrefix or setInterestFilter command succeeds
  */
-typedef function<void(const InterestFilter&, const std::string&)> OnSetInterestFilterFailed;
+typedef function<void(const Name&)> RegisterPrefixSuccessCallback;
 
+/**
+ * @brief Callback called when registerPrefix or setInterestFilter command fails
+ */
+typedef function<void(const Name&, const std::string&)> RegisterPrefixFailureCallback;
+
+/**
+ * @brief Callback called when unregisterPrefix or unsetInterestFilter command succeeds
+ */
+typedef function<void()> UnregisterPrefixSuccessCallback;
+
+/**
+ * @brief Callback called when unregisterPrefix or unsetInterestFilter command fails
+ */
+typedef function<void(const std::string&)> UnregisterPrefixFailureCallback;
 
 
 /**
@@ -126,15 +140,18 @@
   /**
    * @brief Create a new Face using TcpTransport
    *
-   * @param host The host of the NDN hub.
-   * @param port The port or service name of the NDN hub. If omitted. use 6363.
+   * @param host The host of the NDN forwarder
+   * @param port (optional) The port or service name of the NDN forwarder (**default**: "6363")
+   *
    * @throws Face::Error on unsupported protocol
    */
   Face(const std::string& host, const std::string& port = "6363");
 
   /**
    * @brief Create a new Face using the given Transport
-   * @param transport A shared_ptr to a Transport object used for communication.
+   *
+   * @param transport A shared_ptr to a Transport object used for communication
+   *
    * @throws Face::Error on unsupported protocol
    */
   explicit
@@ -156,12 +173,11 @@
   /**
    * @brief Express Interest
    *
-   * @param interest  A reference to the Interest.  This copies the Interest.
-   * @param onData    A function object to call when a matching data packet is received.
-   * @param onTimeout A function object to call if the interest times out.
-   *                  If onTimeout is an empty OnTimeout(), this does not use it.
+   * @param interest  An Interest to be expressed
+   * @param onData    Callback to be called when a matching data packet is received
+   * @param onTimeout (optional) A function object to call if the interest times out
    *
-   * @return The pending interest ID which can be used with removePendingInterest.
+   * @return The pending interest ID which can be used with removePendingInterest
    */
   const PendingInterestId*
   expressInterest(const Interest& interest,
@@ -172,11 +188,10 @@
    *
    * @param name      Name of the Interest
    * @param tmpl      Interest template to fill parameters
-   * @param onData    A callback to call when a matching data packet is received.
-   * @param onTimeout A callback to call if the interest times out.
-   *                  If onTimeout is an empty OnTimeout(), this does not use it.
+   * @param onData    Callback to be called when a matching data packet is received
+   * @param onTimeout (optional) A function object to call if the interest times out
    *
-   * @return The pending interest ID which can be used with removePendingInterest.
+   * @return Opaque pending interest ID which can be used with removePendingInterest
    */
   const PendingInterestId*
   expressInterest(const Name& name,
@@ -184,12 +199,7 @@
                   const OnData& onData, const OnTimeout& onTimeout = OnTimeout());
 
   /**
-   * @brief Remove the pending interest entry with the pendingInterestId from the pending
-   * interest table.
-   *
-   * This does not affect another pending interest with a different pendingInterestId,
-   * even it if has the same interest name.  If there is no entry with the
-   * pendingInterestId, do nothing.
+   * @brief Cancel previously expressed Interest
    *
    * @param pendingInterestId The ID returned from expressInterest.
    */
@@ -197,71 +207,140 @@
   removePendingInterest(const PendingInterestId* pendingInterestId);
 
   /**
-   * @brief Register prefix with the connected NDN hub and call onInterest when a matching
-   *        interest is received.
+   * @brief Set InterestFilter to dispatch incoming matching interest to onInterest
+   * callback and register the filtered prefix with the connected NDN forwarder
+   *
+   * This version of setInterestFilter combines setInterestFilter and registerPrefix
+   * operations and is intended to be used when only one filter for the same prefix needed
+   * to be set.  When multiple names sharing the same prefix should be dispatched to
+   * different callbacks, use one registerPrefix call, followed (in onSuccess callback) by
+   * a series of setInterestFilter calls.
    *
    * @param interestFilter Interest filter (prefix part will be registered with the forwarder)
-   * @param onInterest A function object to call when a matching interest is received
+   * @param onInterest     A callback to be called when a matching interest is received
+   * @param onSuccess      A callback to be called when prefixRegister command succeeds
+   * @param onFailure      A callback to be called when prefixRegister command fails
+   * @param certificate    (optional) A certificate under which the prefix registration
+   *                       command interest is signed.  When omitted, a default certificate
+   *                       of the default identity is used to sign the registration command
    *
-   * @param onRegisterFailed A function object to call if failed to retrieve the connected
-   *                         hub’s ID or failed to register the prefix.  This calls
-   *                         onRegisterFailed(prefix) where prefix is the prefix given to
-   *                         registerPrefix.
-   *
-   * @return The registered prefix ID which can be used with removeRegisteredPrefix.
+   * @return Opaque registered prefix ID which can be used with unsetInterestFilter or
+   *         removeRegisteredPrefix
    */
   const RegisteredPrefixId*
   setInterestFilter(const InterestFilter& interestFilter,
                     const OnInterest& onInterest,
-                    const OnSetInterestFilterFailed& onSetInterestFilterFailed);
+                    const RegisterPrefixSuccessCallback& onSuccess,
+                    const RegisterPrefixFailureCallback& onFailure,
+                    const IdentityCertificate& certificate = IdentityCertificate());
 
   /**
-   * @brief Register prefix with the connected NDN hub and call onInterest when a matching
-   *        interest is received.
-   *
-   * @param interestFilter Interest filter (prefix part will be registered with the forwarder)
-   * @param onInterest A function object to call when a matching interest is received
-   *
-   * @param onRegisterFailed A function object to call if failed to retrieve the connected
-   *                         hub’s ID or failed to register the prefix.  This calls
-   *                         onRegisterFailed(prefix) where prefix is the prefix given to
-   *                         registerPrefix.
-   *
-   * @param certificate A certificate under which the prefix registration command interest
-   *                    is signed.
-   *
-   * @return The registered prefix ID which can be used with removeRegisteredPrefix.
+   * @deprecated Use the other overload
    */
   const RegisteredPrefixId*
   setInterestFilter(const InterestFilter& interestFilter,
                     const OnInterest& onInterest,
-                    const OnSetInterestFilterFailed& onSetInterestFilterFailed,
-                    const IdentityCertificate& certificate);
+                    const RegisterPrefixFailureCallback& onFailure,
+                    const IdentityCertificate& certificate = IdentityCertificate());
 
   /**
-   * @brief Register prefix with the connected NDN hub and call onInterest when a matching
-   *        interest is received.
+   * @brief Set InterestFilter to dispatch incoming matching interest to onInterest
+   * callback and register the filtered prefix with the connected NDN forwarder
+   *
+   * This version of setInterestFilter combines setInterestFilter and registerPrefix
+   * operations and is intended to be used when only one filter for the same prefix needed
+   * to be set.  When multiple names sharing the same prefix should be dispatched to
+   * different callbacks, use one registerPrefix call, followed (in onSuccess callback) by
+   * a series of setInterestFilter calls.
    *
    * @param interestFilter Interest filter (prefix part will be registered with the forwarder)
-   * @param onInterest A function object to call when a matching interest is received
+   * @param onInterest     A callback to be called when a matching interest is received
+   * @param onSuccess      A callback to be called when prefixRegister command succeeds
+   * @param onFailure      A callback to be called when prefixRegister command fails
+   * @param identity       A signing identity. A command interest is signed under the default
+   *                       certificate of this identity
    *
-   * @param onRegisterFailed A function object to call if failed to retrieve the connected
-   *                         hub’s ID or failed to register the prefix.  This calls
-   *                         onRegisterFailed(prefix) where prefix is the prefix given to
-   *                         registerPrefix.
-   *
-   * @param identity A signing identity. A command interest is signed under the default
-   *                 certificate of this identity.
-   *
-   * @return The registered prefix ID which can be used with removeRegisteredPrefix.
+   * @return Opaque registered prefix ID which can be used with removeRegisteredPrefix
    */
   const RegisteredPrefixId*
   setInterestFilter(const InterestFilter& interestFilter,
                     const OnInterest& onInterest,
-                    const OnSetInterestFilterFailed& onSetInterestFilterFailed,
+                    const RegisterPrefixSuccessCallback& onSuccess,
+                    const RegisterPrefixFailureCallback& onFailure,
                     const Name& identity);
 
   /**
+   * @deprecated Use the other overload
+   */
+  const RegisteredPrefixId*
+  setInterestFilter(const InterestFilter& interestFilter,
+                    const OnInterest& onInterest,
+                    const RegisterPrefixFailureCallback& onFailure,
+                    const Name& identity);
+
+  /**
+   * @brief Set InterestFilter to dispatch incoming matching interest to onInterest callback
+   *
+   * @param interestFilter Interest
+   * @param onInterest A callback to be called when a matching interest is received
+   *
+   * This method modifies library's FIB only, and does not register the prefix with the
+   * forwarder.  It will always succeed.  To register prefix with the forwarder, use
+   * registerPrefix, or use the setInterestFilter overload taking two callbacks.
+   *
+   * @return Opaque interest filter ID which can be used with unsetInterestFilter
+   */
+  const InterestFilterId*
+  setInterestFilter(const InterestFilter& interestFilter,
+                    const OnInterest& onInterest);
+
+
+  /**
+   * @brief Register prefix with the connected NDN forwarder
+   *
+   * This method only modifies forwarder's RIB (or FIB) and does not associate any
+   * onInterest callbacks.  Use setInterestFilter method to dispatch incoming Interests to
+   * the right callbacks.
+   *
+   * @param prefix      A prefix to register with the connected NDN forwarder
+   * @param onSuccess   A callback to be called when prefixRegister command succeeds
+   * @param onFailure   A callback to be called when prefixRegister command fails
+   * @param certificate (optional) A certificate under which the prefix registration
+   *                    command interest is signed.  When omitted, a default certificate
+   *                    of the default identity is used to sign the registration command
+   *
+   * @return The registered prefix ID which can be used with unregisterPrefix
+   */
+  const RegisteredPrefixId*
+  registerPrefix(const Name& prefix,
+                 const RegisterPrefixSuccessCallback& onSuccess,
+                 const RegisterPrefixFailureCallback& onFailure,
+                 const IdentityCertificate& certificate = IdentityCertificate());
+
+  /**
+   * @brief Register prefix with the connected NDN forwarder and call onInterest when a matching
+   *        interest is received.
+   *
+   * This method only modifies forwarder's RIB (or FIB) and does not associate any
+   * onInterest callbacks.  Use setInterestFilter method to dispatch incoming Interests to
+   * the right callbacks.
+   *
+   * @param prefix    A prefix to register with the connected NDN forwarder
+   * @param onSuccess A callback to be called when prefixRegister command succeeds
+   * @param onFailure A callback to be called when prefixRegister command fails
+   * @param identity  A signing identity. A command interest is signed under the default
+   *                  certificate of this identity
+   *
+   * @return The registered prefix ID which can be used with unregisterPrefix
+   */
+  const RegisteredPrefixId*
+  registerPrefix(const Name& prefix,
+                 const RegisterPrefixSuccessCallback& onSuccess,
+                 const RegisterPrefixFailureCallback& onFailure,
+                 const Name& identity);
+
+
+  /**
    * @brief Remove the registered prefix entry with the registeredPrefixId from the
    *        pending interest table.
    *
@@ -272,12 +351,39 @@
    * unsetInterestFilter will use the same credentials as original
    * setInterestFilter/registerPrefix command
    *
-   * @param registeredPrefixId The ID returned from registerPrefix.
+   * @param registeredPrefixId The ID returned from registerPrefix
    */
   void
   unsetInterestFilter(const RegisteredPrefixId* registeredPrefixId);
 
   /**
+   * @brief Remove previously set InterestFilter from library's FIB
+   *
+   * This method always succeeds and will **NOT** send any request to the connected
+   * forwarder.
+   *
+   * @param interestFilterId The ID returned from setInterestFilter.
+   */
+  void
+  unsetInterestFilter(const InterestFilterId* interestFilterId);
+
+  /**
+   * @brief Deregister prefix from RIB (or FIB)
+   *
+   * unregisterPrefix will use the same credentials as original
+   * setInterestFilter/registerPrefix command
+   *
+   * If registeredPrefixId was obtained using setInterestFilter, the corresponding
+   * InterestFilter will be unset too.
+   *
+   * @param registeredPrefixId The ID returned from registerPrefix.
+   */
+  void
+  unregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
+                   const UnregisterPrefixSuccessCallback& onSuccess,
+                   const UnregisterPrefixFailureCallback& onFailure);
+
+  /**
    * @brief (FOR DEBUG PURPOSES ONLY) Request direct NFD FIB management
    */
   void
@@ -305,7 +411,7 @@
    * If negative timeout is specified, then processEvents will not block and process only
    * pending events.
    *
-   * @param timeout     maximum time to block the thread.
+   * @param timeout     maximum time to block the thread
    * @param keepThread  Keep thread in a blocked state (in event processing), even when
    *                    there are no outstanding events (e.g., no Interest/Data is expected)
    *
@@ -379,13 +485,23 @@
   asyncRemovePendingInterest(const PendingInterestId* pendingInterestId);
 
   void
-  afterPrefixRegistered(const shared_ptr<RegisteredPrefix>& registeredPrefix);
+  afterPrefixRegistered(const shared_ptr<RegisteredPrefix>& registeredPrefix,
+                        const RegisterPrefixSuccessCallback& onSuccess);
 
   void
-  asyncUnsetInterestFilter(const RegisteredPrefixId* registeredPrefixId);
+  asyncSetInterestFilter(const shared_ptr<InterestFilterRecord>& interestFilterRecord);
 
   void
-  finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item);
+  asyncUnsetInterestFilter(const InterestFilterId* interestFilterId);
+
+  void
+  asyncUnregisterPrefix(const RegisteredPrefixId* registeredPrefixId,
+                        const UnregisterPrefixSuccessCallback& onSuccess,
+                        const UnregisterPrefixFailureCallback& onFailure);
+
+  void
+  finalizeUnregisterPrefix(RegisteredPrefixTable::iterator item,
+                           const UnregisterPrefixSuccessCallback& onSuccess);
 
   void
   onReceiveElement(const Block& wire);
@@ -407,10 +523,11 @@
 
   template<class SignatureGenerator>
   const RegisteredPrefixId*
-  setInterestFilterImpl(const InterestFilter& interestFilter,
-                        const OnInterest& onInterest,
-                        const OnSetInterestFilterFailed& onSetInterestFilterFailed,
-                        const SignatureGenerator& signatureGenerator);
+  registerPrefixImpl(const Name& prefix,
+                     const shared_ptr<InterestFilterRecord>& filter,
+                     const RegisterPrefixSuccessCallback& onSuccess,
+                     const RegisterPrefixFailureCallback& onFailure,
+                     const SignatureGenerator& signatureGenerator);
 
 private:
   shared_ptr<boost::asio::io_service> m_ioService;
diff --git a/src/transport/unix-transport.cpp b/src/transport/unix-transport.cpp
index a7acd44..1626b7f 100644
--- a/src/transport/unix-transport.cpp
+++ b/src/transport/unix-transport.cpp
@@ -86,30 +86,35 @@
 void
 UnixTransport::send(const Block& wire)
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->send(wire);
 }
 
 void
 UnixTransport::send(const Block& header, const Block& payload)
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->send(header, payload);
 }
 
 void
 UnixTransport::close()
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->close();
 }
 
 void
 UnixTransport::pause()
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->pause();
 }
 
 void
 UnixTransport::resume()
 {
+  BOOST_ASSERT(static_cast<bool>(m_impl));
   m_impl->resume();
 }
 
diff --git a/tests-integrated/test-faces.cpp b/tests-integrated/test-faces.cpp
index 2f6d7ec..1c2ef29 100644
--- a/tests-integrated/test-faces.cpp
+++ b/tests-integrated/test-faces.cpp
@@ -302,8 +302,9 @@
                           bind(&FacesFixture2::checkPrefix, this, true));
 
   scheduler.scheduleEvent(time::seconds(1),
-                          bind(&Face::unsetInterestFilter, &face,
-                               regPrefixId)); // shouldn't match
+    bind(static_cast<void(Face::*)(const RegisteredPrefixId*)>(&Face::unsetInterestFilter),
+         &face,
+    regPrefixId)); // shouldn't match
 
   scheduler.scheduleEvent(time::milliseconds(1500),
                           bind(&FacesFixture2::checkPrefix, this, false));
@@ -311,6 +312,151 @@
   BOOST_REQUIRE_NO_THROW(face.processEvents());
 }
 
+
+class FacesFixture3 : public FacesFixture2
+{
+public:
+  FacesFixture3()
+    : nRegSuccesses(0)
+    , nUnregSuccesses(0)
+    , nUnregFailures(0)
+  {
+  }
+
+  void
+  onRegSucceeded()
+  {
+    ++nRegSuccesses;
+  }
+
+  void
+  onUnregSucceeded()
+  {
+    ++nUnregSuccesses;
+  }
+
+  void
+  onUnregFailed()
+  {
+    ++nUnregFailures;
+  }
+
+public:
+  uint64_t nRegSuccesses;
+  uint64_t nUnregSuccesses;
+  uint64_t nUnregFailures;
+};
+
+BOOST_FIXTURE_TEST_CASE(RegisterPrefix, FacesFixture3)
+{
+  Face face;
+  Face face2(face.ioService());
+  Scheduler scheduler(*face.ioService());
+  scheduler.scheduleEvent(time::seconds(2),
+                          bind(&FacesFixture::terminate, this, ref(face)));
+
+  scheduler.scheduleEvent(time::milliseconds(500),
+                          bind(&FacesFixture2::checkPrefix, this, true));
+
+  regPrefixId = face.registerPrefix("/Hello/World",
+                                    bind(&FacesFixture3::onRegSucceeded, this),
+                                    bind(&FacesFixture3::onRegFailed, this));
+
+  scheduler.scheduleEvent(time::seconds(1),
+    bind(&Face::unregisterPrefix, &face,
+         regPrefixId,
+         static_cast<UnregisterPrefixSuccessCallback>(bind(&FacesFixture3::onUnregSucceeded, this)),
+         static_cast<UnregisterPrefixFailureCallback>(bind(&FacesFixture3::onUnregFailed, this))));
+
+  scheduler.scheduleEvent(time::milliseconds(1500),
+                          bind(&FacesFixture2::checkPrefix, this, false));
+
+  BOOST_REQUIRE_NO_THROW(face.processEvents());
+
+  BOOST_CHECK_EQUAL(nRegFailures, 0);
+  BOOST_CHECK_EQUAL(nRegSuccesses, 1);
+
+  BOOST_CHECK_EQUAL(nUnregFailures, 0);
+  BOOST_CHECK_EQUAL(nUnregSuccesses, 1);
+}
+
+BOOST_AUTO_TEST_CASE(SetRegexFilterButNoRegister)
+{
+  Face face;
+  Face face2(face.getIoService());
+  Scheduler scheduler(*face.ioService());
+  scheduler.scheduleEvent(time::seconds(2),
+                          bind(&FacesFixture::terminate, this, ref(face)));
+
+  face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+                         bind(&FacesFixture::onInterestRegex, this,
+                              ref(face), _1, _2));
+
+  scheduler.scheduleEvent(time::milliseconds(200),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a"))); // shouldn't match
+
+  scheduler.scheduleEvent(time::milliseconds(300),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b"))); // should match
+
+  scheduler.scheduleEvent(time::milliseconds(400),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b/c"))); // should match
+
+  scheduler.scheduleEvent(time::milliseconds(500),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b/d"))); // should not match
+
+  BOOST_REQUIRE_NO_THROW(face.processEvents());
+
+  BOOST_CHECK_EQUAL(nRegFailures, 0);
+  BOOST_CHECK_EQUAL(nInInterests, 4);
+  BOOST_CHECK_EQUAL(nTimeouts, 4);
+  BOOST_CHECK_EQUAL(nData, 0);
+}
+
+
+BOOST_FIXTURE_TEST_CASE(SetRegexFilterAndRegister, FacesFixture3)
+{
+  Face face;
+  Face face2(face.getIoService());
+  Scheduler scheduler(*face.ioService());
+  scheduler.scheduleEvent(time::seconds(2),
+                          bind(&FacesFixture::terminate, this, ref(face)));
+
+  face.setInterestFilter(InterestFilter("/Hello/World", "<><b><c>?"),
+                         bind(&FacesFixture::onInterestRegex, this,
+                              ref(face), _1, _2));
+
+  face.registerPrefix("/Hello/World",
+                      bind(&FacesFixture3::onRegSucceeded, this),
+                      bind(&FacesFixture3::onRegFailed, this));
+
+  scheduler.scheduleEvent(time::milliseconds(200),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a"))); // shouldn't match
+
+  scheduler.scheduleEvent(time::milliseconds(300),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b"))); // should match
+
+  scheduler.scheduleEvent(time::milliseconds(400),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b/c"))); // should match
+
+  scheduler.scheduleEvent(time::milliseconds(500),
+                          bind(&FacesFixture::expressInterest, this,
+                               ref(face2), Name("/Hello/World/a/b/d"))); // should not match
+
+  BOOST_REQUIRE_NO_THROW(face.processEvents());
+
+  BOOST_CHECK_EQUAL(nRegFailures, 0);
+  BOOST_CHECK_EQUAL(nRegSuccesses, 1);
+
+  BOOST_CHECK_EQUAL(nInInterests, 2);
+  BOOST_CHECK_EQUAL(nTimeouts, 4);
+}
 BOOST_AUTO_TEST_SUITE_END()
 
 } // namespace ndn