mgmt+face: move protocol-specific face creation checks into protocol factories

Also brought implementation of faces/create in line with design

refs #3731

Change-Id: I4f48079136b42c7fdbd4fdfba37116d2565f9dc1
diff --git a/daemon/face/channel.hpp b/daemon/face/channel.hpp
index 640395a..19beb7f 100644
--- a/daemon/face/channel.hpp
+++ b/daemon/face/channel.hpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -41,7 +41,7 @@
  * \brief Prototype for the callback that is invoked when the face
  *        fails to be created
  */
-typedef function<void(const std::string& reason)> FaceCreationFailedCallback;
+typedef function<void(uint32_t status, const std::string& reason)> FaceCreationFailedCallback;
 
 
 class Channel : noncopyable
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index dd9d034..85f00f1 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -59,9 +59,9 @@
 EthernetFactory::createFace(const FaceUri& uri,
                             ndn::nfd::FacePersistency persistency,
                             const FaceCreatedCallback& onCreated,
-                            const FaceCreationFailedCallback& onConnectFailed)
+                            const FaceCreationFailedCallback& onFailure)
 {
-  BOOST_THROW_EXCEPTION(Error("EthernetFactory does not support 'createFace' operation"));
+  onFailure(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index b44c2b7..f29e09a 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -80,7 +80,7 @@
   createFace(const FaceUri& uri,
              ndn::nfd::FacePersistency persistency,
              const FaceCreatedCallback& onCreated,
-             const FaceCreationFailedCallback& onConnectFailed) override;
+             const FaceCreationFailedCallback& onFailure) override;
 
   virtual std::vector<shared_ptr<const Channel>>
   getChannels() const override;
diff --git a/daemon/face/tcp-channel.cpp b/daemon/face/tcp-channel.cpp
index 21943c8..163b0e9 100644
--- a/daemon/face/tcp-channel.cpp
+++ b/daemon/face/tcp-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -151,7 +151,7 @@
 
     NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
     if (onAcceptFailed)
-      onAcceptFailed(error.message());
+      onAcceptFailed(500, "Accept failed: " + error.message());
     return;
   }
 
@@ -189,7 +189,7 @@
     socket->close();
 
     if (onConnectFailed)
-      onConnectFailed(error.message());
+      onConnectFailed(504, "Connect failed: " + error.message());
     return;
   }
 
@@ -209,7 +209,7 @@
   socket->close(error);
 
   if (onConnectFailed)
-    onConnectFailed("Connect to remote endpoint timed out");
+    onConnectFailed(504, "Connect to remote endpoint timed out");
 }
 
 } // namespace nfd
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index a0fad93..5c96492 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -105,15 +105,24 @@
   BOOST_ASSERT(uri.isCanonical());
 
   if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
-    BOOST_THROW_EXCEPTION(Error("TcpFactory::createFace supports only FACE_PERSISTENCY_PERSISTENT"));
+    NFD_LOG_TRACE("createFace only supports FACE_PERSISTENCY_PERSISTENT");
+    onConnectFailed(406, "Outgoing TCP faces only support persistent persistency");
+    return;
   }
 
   tcp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
                          boost::lexical_cast<uint16_t>(uri.getPort()));
 
+  if (endpoint.address().is_multicast()) {
+   NFD_LOG_TRACE("createFace cannot create multicast faces");
+   onConnectFailed(406, "Cannot create multicast TCP faces");
+   return;
+  }
+
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
-    onConnectFailed("Requested endpoint is prohibited "
-                    "(reserved by this NFD or disallowed by face management protocol)");
+    NFD_LOG_TRACE("Requested endpoint is prohibited "
+                  "(reserved by NFD or disallowed by face management protocol)");
+    onConnectFailed(406, "Requested endpoint is prohibited");
     return;
   }
 
@@ -126,7 +135,8 @@
     }
   }
 
-  onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  onConnectFailed(504, "No channels available to connect");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index e6d65df..9b66b0d 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -62,7 +62,7 @@
   catch (const boost::system::system_error& e) {
     NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << e.what());
     if (onConnectFailed)
-      onConnectFailed(e.what());
+      onConnectFailed(504, std::string("Connect failed: ") + e.what());
     return;
   }
 
@@ -113,7 +113,7 @@
 
     NFD_LOG_DEBUG("[" << m_localEndpoint << "] Receive failed: " << error.message());
     if (onReceiveFailed)
-      onReceiveFailed(error.message());
+      onReceiveFailed(500, "Receive failed: " + error.message());
     return;
   }
 
@@ -128,7 +128,7 @@
     NFD_LOG_WARN("[" << m_localEndpoint << "] Failed to create face for peer "
                  << m_remoteEndpoint << ": " << e.what());
     if (onReceiveFailed)
-      onReceiveFailed(e.what());
+      onReceiveFailed(504, "Failed to create face for peer");
     return;
   }
 
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index e905a0b..499c72c 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -239,20 +239,24 @@
   BOOST_ASSERT(uri.isCanonical());
 
   if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
-    BOOST_THROW_EXCEPTION(Error("UdpFactory::createFace does not support FACE_PERSISTENCY_ON_DEMAND"));
+    NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
+    onConnectFailed(406, "Outgoing unicast UDP faces do not support on-demand persistency");
+    return;
   }
 
   udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
                          boost::lexical_cast<uint16_t>(uri.getPort()));
 
   if (endpoint.address().is_multicast()) {
-    onConnectFailed("The provided address is multicast. Please use createMulticastFace method");
+    NFD_LOG_TRACE("createFace does not support multicast faces");
+    onConnectFailed(406, "Cannot create multicast UDP faces");
     return;
   }
 
   if (m_prohibitedEndpoints.find(endpoint) != m_prohibitedEndpoints.end()) {
-    onConnectFailed("Requested endpoint is prohibited "
-                    "(reserved by this NFD or disallowed by face management protocol)");
+    NFD_LOG_TRACE("Requested endpoint is prohibited "
+                  "(reserved by this NFD or disallowed by face management protocol)");
+    onConnectFailed(406, "Requested endpoint is prohibited");
     return;
   }
 
@@ -265,7 +269,8 @@
     }
   }
 
-  onConnectFailed("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  NFD_LOG_TRACE("No channels available to connect to " + boost::lexical_cast<std::string>(endpoint));
+  onConnectFailed(504, "No channels available to connect");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/unix-stream-channel.cpp b/daemon/face/unix-stream-channel.cpp
index 5b56c6a..85416fd 100644
--- a/daemon/face/unix-stream-channel.cpp
+++ b/daemon/face/unix-stream-channel.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -126,7 +126,7 @@
 
     NFD_LOG_DEBUG("[" << m_endpoint << "] Accept failed: " << error.message());
     if (onAcceptFailed)
-      onAcceptFailed(error.message());
+      onAcceptFailed(500, "Accept failed: " + error.message());
     return;
   }
 
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index 3de85b4..b85953e 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -51,7 +51,7 @@
                               const FaceCreatedCallback& onCreated,
                               const FaceCreationFailedCallback& onConnectFailed)
 {
-  BOOST_THROW_EXCEPTION(Error("UnixStreamFactory does not support 'createFace' operation"));
+  onConnectFailed(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index bcb3970..cea0078 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -1,6 +1,6 @@
 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
 /**
- * Copyright (c) 2014-2015,  Regents of the University of California,
+ * Copyright (c) 2014-2016,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -56,7 +56,7 @@
                              const FaceCreatedCallback& onCreated,
                              const FaceCreationFailedCallback& onConnectFailed)
 {
-  BOOST_THROW_EXCEPTION(Error("WebSocketFactory does not support 'createFace' operation"));
+  onConnectFailed(406, "Unsupported protocol");
 }
 
 std::vector<shared_ptr<const Channel>>
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 8b7057c..9cd0e07 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -91,17 +91,21 @@
   FaceUri uri;
   if (!uri.parse(parameters.getUri())) {
     NFD_LOG_TRACE("failed to parse URI");
-    return done(ControlResponse(400, "Malformed command"));
+    done(ControlResponse(400, "Malformed command"));
+    return;
   }
 
   if (!uri.isCanonical()) {
     NFD_LOG_TRACE("received non-canonical URI");
-    return done(ControlResponse(400, "Non-canonical URI"));
+    done(ControlResponse(400, "Non-canonical URI"));
+    return;
   }
 
   auto factory = m_factories.find(uri.getScheme());
   if (factory == m_factories.end()) {
-    return done(ControlResponse(501, "Unsupported protocol"));
+    NFD_LOG_TRACE("received create request for unsupported protocol");
+    done(ControlResponse(406, "Unsupported protocol"));
+    return;
   }
 
   try {
@@ -110,35 +114,50 @@
                                 bind(&FaceManager::afterCreateFaceSuccess,
                                      this, parameters, _1, done),
                                 bind(&FaceManager::afterCreateFaceFailure,
-                                     this, _1, done));
+                                     this, _1, _2, done));
   }
   catch (const std::runtime_error& error) {
-    std::string errorMessage = "Face creation failed: ";
-    errorMessage += error.what();
-
-    NFD_LOG_ERROR(errorMessage);
-    return done(ControlResponse(500, errorMessage));
+    NFD_LOG_ERROR("Face creation failed: " << error.what());
+    done(ControlResponse(500, "Face creation failed due to internal error"));
+    return;
   }
   catch (const std::logic_error& error) {
-    std::string errorMessage = "Face creation failed: ";
-    errorMessage += error.what();
-
-    NFD_LOG_ERROR(errorMessage);
-    return done(ControlResponse(500, errorMessage));
+    NFD_LOG_ERROR("Face creation failed: " << error.what());
+    done(ControlResponse(500, "Face creation failed due to internal error"));
+    return;
   }
 }
 
+/**
+ * \todo #3232
+ *       If the creation of this face would conflict with an existing face (e.g. same underlying
+ *       protocol and remote address, or a NIC-associated permanent face), the command will fail
+ *       with StatusCode 409.
+ */
 void
-FaceManager::afterCreateFaceSuccess(ControlParameters& parameters,
+FaceManager::afterCreateFaceSuccess(const ControlParameters& parameters,
                                     const shared_ptr<Face>& newFace,
                                     const ndn::mgmt::CommandContinuation& done)
 {
-  m_faceTable.add(newFace);
-  parameters.setFaceId(newFace->getId());
-  parameters.setUri(newFace->getRemoteUri().toString());
-  parameters.setFacePersistency(newFace->getPersistency());
+  ControlParameters response;
 
-  done(ControlResponse(200, "OK").setBody(parameters.wireEncode()));
+  m_faceTable.add(newFace);
+
+  // Set ControlResponse parameters
+  response.setFaceId(newFace->getId());
+  response.setFacePersistency(newFace->getPersistency());
+
+  done(ControlResponse(200, "OK").setBody(response.wireEncode()));
+}
+
+void
+FaceManager::afterCreateFaceFailure(uint32_t status,
+                                    const std::string& reason,
+                                    const ndn::mgmt::CommandContinuation& done)
+{
+  NFD_LOG_DEBUG("Face creation failed: " << reason);
+
+  done(ControlResponse(status, reason));
 }
 
 void
@@ -155,15 +174,6 @@
 }
 
 void
-FaceManager::afterCreateFaceFailure(const std::string& reason,
-                                    const ndn::mgmt::CommandContinuation& done)
-{
-  NFD_LOG_DEBUG("Failed to create face: " << reason);
-
-  done(ControlResponse(408, "Failed to create face: " + reason));
-}
-
-void
 FaceManager::enableLocalControl(const Name& topPrefix, const Interest& interest,
                                 const ControlParameters& parameters,
                                 const ndn::mgmt::CommandContinuation& done)
diff --git a/daemon/mgmt/face-manager.hpp b/daemon/mgmt/face-manager.hpp
index f31f04b..094f288 100644
--- a/daemon/mgmt/face-manager.hpp
+++ b/daemon/mgmt/face-manager.hpp
@@ -77,12 +77,13 @@
 
 PUBLIC_WITH_TESTS_ELSE_PRIVATE: // helpers for ControlCommand
   void
-  afterCreateFaceSuccess(ControlParameters& parameters,
+  afterCreateFaceSuccess(const ControlParameters& parameters,
                          const shared_ptr<Face>& newFace,
                          const ndn::mgmt::CommandContinuation& done);
 
   void
-  afterCreateFaceFailure(const std::string& reason,
+  afterCreateFaceFailure(uint32_t status,
+                         const std::string& reason,
                          const ndn::mgmt::CommandContinuation& done);
 
   Face*