face+mgmt: accept LocalUri in ProtocolFactory and FaceManager

refs #4016

Change-Id: I3e192e6d3982ae8e6ced1dbfbaa62f1c993f799e
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index 1b348fc..fef7e96 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -133,7 +133,8 @@
 }
 
 void
-EthernetFactory::createFace(const FaceUri& uri,
+EthernetFactory::createFace(const FaceUri& remoteUri,
+                            const ndn::optional<FaceUri>& localUri,
                             ndn::nfd::FacePersistency persistency,
                             bool wantLocalFieldsEnabled,
                             const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index 9facd71..856116f 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -50,7 +50,8 @@
   /** \brief unicast face creation is not supported and will always fail
    */
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index 72018f0..4535d3f 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -116,19 +116,17 @@
 
   /** \brief Try to create Face using the supplied FaceUri
    *
-   * This method should automatically choose channel, based on supplied FaceUri
-   * and create face.
-   *
-   * \param uri remote URI of the new face
+   * \param remoteUri remote URI of the new face
+   * \param localUri local URI of the new face
    * \param persistency persistency of the new face
    * \param wantLocalFieldsEnabled whether local fields should be enabled on the face
-   * \param onCreated callback if face creation succeeds
-   *                  If a face with the same remote URI already exists, its persistency and
-   *                  LocalFieldsEnabled setting will not be modified.
+   * \param onCreated callback if face creation succeeds or face already exists;
+   *                  persistency and local fields settings are not updated on an existing face
    * \param onFailure callback if face creation fails
    */
   virtual void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index a273e42..f87fbb6 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -121,13 +121,20 @@
 }
 
 void
-TcpFactory::createFace(const FaceUri& uri,
+TcpFactory::createFace(const FaceUri& remoteUri,
+                       const ndn::optional<FaceUri>& localUri,
                        ndn::nfd::FacePersistency persistency,
                        bool wantLocalFieldsEnabled,
                        const FaceCreatedCallback& onCreated,
                        const FaceCreationFailedCallback& onFailure)
 {
-  BOOST_ASSERT(uri.isCanonical());
+  BOOST_ASSERT(remoteUri.isCanonical());
+
+  if (localUri) {
+    NFD_LOG_TRACE("Cannot create unicast TCP face with LocalUri");
+    onFailure(406, "Unicast TCP faces cannot be created with a LocalUri");
+    return;
+  }
 
   if (persistency != ndn::nfd::FACE_PERSISTENCY_PERSISTENT) {
     NFD_LOG_TRACE("createFace only supports FACE_PERSISTENCY_PERSISTENT");
@@ -135,8 +142,8 @@
     return;
   }
 
-  tcp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
-                         boost::lexical_cast<uint16_t>(uri.getPort()));
+  tcp::Endpoint endpoint(ip::address::from_string(remoteUri.getHost()),
+                         boost::lexical_cast<uint16_t>(remoteUri.getPort()));
 
   if (endpoint.address().is_multicast()) {
    NFD_LOG_TRACE("createFace cannot create multicast faces");
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index 059c8e1..35a51d7 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -47,7 +47,8 @@
                 FaceSystem::ConfigContext& context) override;
 
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/udp-factory.cpp b/daemon/face/udp-factory.cpp
index d141e58..3cc1818 100644
--- a/daemon/face/udp-factory.cpp
+++ b/daemon/face/udp-factory.cpp
@@ -202,13 +202,20 @@
 }
 
 void
-UdpFactory::createFace(const FaceUri& uri,
+UdpFactory::createFace(const FaceUri& remoteUri,
+                       const ndn::optional<FaceUri>& localUri,
                        ndn::nfd::FacePersistency persistency,
                        bool wantLocalFieldsEnabled,
                        const FaceCreatedCallback& onCreated,
                        const FaceCreationFailedCallback& onFailure)
 {
-  BOOST_ASSERT(uri.isCanonical());
+  BOOST_ASSERT(remoteUri.isCanonical());
+
+  if (localUri) {
+    NFD_LOG_TRACE("Cannot create unicast UDP face with LocalUri");
+    onFailure(406, "Unicast UDP faces cannot be created with a LocalUri");
+    return;
+  }
 
   if (persistency == ndn::nfd::FACE_PERSISTENCY_ON_DEMAND) {
     NFD_LOG_TRACE("createFace does not support FACE_PERSISTENCY_ON_DEMAND");
@@ -216,8 +223,8 @@
     return;
   }
 
-  udp::Endpoint endpoint(ip::address::from_string(uri.getHost()),
-                         boost::lexical_cast<uint16_t>(uri.getPort()));
+  udp::Endpoint endpoint(ip::address::from_string(remoteUri.getHost()),
+                         boost::lexical_cast<uint16_t>(remoteUri.getPort()));
 
   if (endpoint.address().is_multicast()) {
     NFD_LOG_TRACE("createFace does not support multicast faces");
diff --git a/daemon/face/udp-factory.hpp b/daemon/face/udp-factory.hpp
index e662e6a..ce6d2e5 100644
--- a/daemon/face/udp-factory.hpp
+++ b/daemon/face/udp-factory.hpp
@@ -63,7 +63,8 @@
                 FaceSystem::ConfigContext& context) override;
 
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index 19a39dc..33a488f 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -81,7 +81,8 @@
 }
 
 void
-UnixStreamFactory::createFace(const FaceUri& uri,
+UnixStreamFactory::createFace(const FaceUri& remoteUri,
+                              const ndn::optional<FaceUri>& localUri,
                               ndn::nfd::FacePersistency persistency,
                               bool wantLocalFieldsEnabled,
                               const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/unix-stream-factory.hpp b/daemon/face/unix-stream-factory.hpp
index 43a6733..3061abd 100644
--- a/daemon/face/unix-stream-factory.hpp
+++ b/daemon/face/unix-stream-factory.hpp
@@ -47,7 +47,8 @@
                 FaceSystem::ConfigContext& context) override;
 
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index 86ca95e..3b813c0 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -115,7 +115,8 @@
 }
 
 void
-WebSocketFactory::createFace(const FaceUri& uri,
+WebSocketFactory::createFace(const FaceUri& remoteUri,
+                             const ndn::optional<FaceUri>& localUri,
                              ndn::nfd::FacePersistency persistency,
                              bool wantLocalFieldsEnabled,
                              const FaceCreatedCallback& onCreated,
diff --git a/daemon/face/websocket-factory.hpp b/daemon/face/websocket-factory.hpp
index 60f0a69..b7b7b02 100644
--- a/daemon/face/websocket-factory.hpp
+++ b/daemon/face/websocket-factory.hpp
@@ -49,7 +49,8 @@
   /** \brief unicast face creation is not supported and will always fail
    */
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/daemon/mgmt/face-manager.cpp b/daemon/mgmt/face-manager.cpp
index 9be7c94..d9b2a03 100644
--- a/daemon/mgmt/face-manager.cpp
+++ b/daemon/mgmt/face-manager.cpp
@@ -78,28 +78,46 @@
                         const ControlParameters& parameters,
                         const ndn::mgmt::CommandContinuation& done)
 {
-  FaceUri uri;
-  if (!uri.parse(parameters.getUri())) {
-    NFD_LOG_TRACE("failed to parse URI");
+  FaceUri remoteUri;
+  if (!remoteUri.parse(parameters.getUri())) {
+    NFD_LOG_TRACE("failed to parse remote URI: " << parameters.getUri());
     done(ControlResponse(400, "Malformed command"));
     return;
   }
 
-  if (!uri.isCanonical()) {
-    NFD_LOG_TRACE("received non-canonical URI");
-    done(ControlResponse(400, "Non-canonical URI"));
+  if (!remoteUri.isCanonical()) {
+    NFD_LOG_TRACE("received non-canonical remote URI: " << remoteUri.toString());
+    done(ControlResponse(400, "Non-canonical remote URI"));
     return;
   }
 
-  ProtocolFactory* factory = m_faceSystem.getFactoryByScheme(uri.getScheme());
+  ndn::optional<FaceUri> localUri;
+  if (parameters.hasLocalUri()) {
+    localUri = FaceUri{};
+
+    if (!localUri->parse(parameters.getLocalUri())) {
+      NFD_LOG_TRACE("failed to parse local URI: " << parameters.getLocalUri());
+      done(ControlResponse(400, "Malformed command"));
+      return;
+    }
+
+    if (!localUri->isCanonical()) {
+      NFD_LOG_TRACE("received non-canonical local URI: " << localUri->toString());
+      done(ControlResponse(400, "Non-canonical local URI"));
+      return;
+    }
+  }
+
+  ProtocolFactory* factory = m_faceSystem.getFactoryByScheme(remoteUri.getScheme());
   if (factory == nullptr) {
-    NFD_LOG_TRACE("received create request for unsupported protocol");
+    NFD_LOG_TRACE("received create request for unsupported protocol: " << remoteUri.getScheme());
     done(ControlResponse(406, "Unsupported protocol"));
     return;
   }
 
   try {
-    factory->createFace(uri,
+    factory->createFace(remoteUri,
+      localUri,
       parameters.getFacePersistency(),
       parameters.hasFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED) &&
         parameters.getFlagBit(ndn::nfd::BIT_LOCAL_FIELDS_ENABLED),
diff --git a/tests/daemon/face/ethernet-factory.t.cpp b/tests/daemon/face/ethernet-factory.t.cpp
index 56373d0..b8b07d1 100644
--- a/tests/daemon/face/ethernet-factory.t.cpp
+++ b/tests/daemon/face/ethernet-factory.t.cpp
@@ -327,18 +327,21 @@
 
   createFace(factory,
              FaceUri("ether://[08:00:27:01:01:01]"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ether://[08:00:27:01:01:01]"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ether://[08:00:27:01:01:01]"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
diff --git a/tests/daemon/face/face-system.t.cpp b/tests/daemon/face/face-system.t.cpp
index bae735b..8e69441 100644
--- a/tests/daemon/face/face-system.t.cpp
+++ b/tests/daemon/face/face-system.t.cpp
@@ -53,7 +53,8 @@
   }
 
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,
diff --git a/tests/daemon/face/factory-test-common.hpp b/tests/daemon/face/factory-test-common.hpp
index 8622d06..20ccaf3 100644
--- a/tests/daemon/face/factory-test-common.hpp
+++ b/tests/daemon/face/factory-test-common.hpp
@@ -43,12 +43,13 @@
 
 inline void
 createFace(ProtocolFactory& factory,
-           const FaceUri& uri,
+           const FaceUri& remoteUri,
+           const ndn::optional<FaceUri>& localUri,
            ndn::nfd::FacePersistency persistency,
            bool wantLocalFieldsEnabled,
            const CreateFaceExpectedResult& expected)
 {
-  factory.createFace(uri, persistency, wantLocalFieldsEnabled,
+  factory.createFace(remoteUri, localUri, persistency, wantLocalFieldsEnabled,
                      [expected] (const shared_ptr<Face>&) {
                        BOOST_CHECK_EQUAL(CreateFaceExpectedResult::SUCCESS, expected.result);
                      },
diff --git a/tests/daemon/face/tcp-factory.t.cpp b/tests/daemon/face/tcp-factory.t.cpp
index 9f5e7c1..31e8918 100644
--- a/tests/daemon/face/tcp-factory.t.cpp
+++ b/tests/daemon/face/tcp-factory.t.cpp
@@ -172,6 +172,7 @@
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:6363"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
@@ -179,7 +180,8 @@
   factory.createChannel("127.0.0.1", "20071");
 
   createFace(factory,
-             FaceUri("tcp4://127.0.0.1:20070"),
+             FaceUri("tcp4://127.0.0.1:6363"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
@@ -191,9 +193,11 @@
 
   factory.createChannel("127.0.0.1", "20070");
   factory.createChannel("127.0.0.1", "20071");
+  factory.createChannel("127.0.0.1", "20072");
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406,
@@ -201,10 +205,19 @@
 
   createFace(factory,
              FaceUri("tcp4://127.0.0.1:20071"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
              false,
              {CreateFaceExpectedResult::FAILURE, 406,
                "Outgoing TCP faces only support persistent persistency"});
+
+  createFace(factory,
+             FaceUri("tcp4://127.0.0.1:20072"),
+             FaceUri("udp4://127.0.0.1:20073"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             false,
+             {CreateFaceExpectedResult::FAILURE, 406,
+               "Unicast TCP faces cannot be created with a LocalUri"});
 }
 
 class FaceCreateTimeoutFixture : public BaseFixture
@@ -240,6 +253,7 @@
   shared_ptr<TcpChannel> channel = factory.createChannel("0.0.0.0", "20070");
 
   factory.createFace(FaceUri("tcp4://192.0.2.1:20070"),
+                     {},
                      ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
                      false,
                      bind(&FaceCreateTimeoutFixture::onFaceCreated, this, _1),
diff --git a/tests/daemon/face/udp-factory.t.cpp b/tests/daemon/face/udp-factory.t.cpp
index 14a0aee..e658248 100644
--- a/tests/daemon/face/udp-factory.t.cpp
+++ b/tests/daemon/face/udp-factory.t.cpp
@@ -603,6 +603,7 @@
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:6363"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 504, "No channels available to connect"});
@@ -611,18 +612,21 @@
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
-  //test the upgrade
+
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20072"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::SUCCESS, 0, ""});
@@ -633,13 +637,23 @@
   UdpFactory factory;
 
   factory.createChannel("127.0.0.1", "20070");
+  factory.createChannel("127.0.0.1", "20071");
 
   createFace(factory,
              FaceUri("udp4://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
              false,
              {CreateFaceExpectedResult::FAILURE, 406,
                "Outgoing unicast UDP faces do not support on-demand persistency"});
+
+  createFace(factory,
+             FaceUri("udp4://127.0.0.1:20071"),
+             FaceUri("udp4://127.0.0.1:20073"),
+             ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
+             false,
+             {CreateFaceExpectedResult::FAILURE, 406,
+               "Unicast UDP faces cannot be created with a LocalUri"});
 }
 
 class FakeNetworkInterfaceFixture : public BaseFixture
diff --git a/tests/daemon/face/unix-stream-factory.t.cpp b/tests/daemon/face/unix-stream-factory.t.cpp
index b0d48c7..7cdd741 100644
--- a/tests/daemon/face/unix-stream-factory.t.cpp
+++ b/tests/daemon/face/unix-stream-factory.t.cpp
@@ -137,18 +137,21 @@
 
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("unix:///var/run/nfd.sock"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
diff --git a/tests/daemon/face/websocket-factory.t.cpp b/tests/daemon/face/websocket-factory.t.cpp
index 78d2321..95fd7d8 100644
--- a/tests/daemon/face/websocket-factory.t.cpp
+++ b/tests/daemon/face/websocket-factory.t.cpp
@@ -199,18 +199,21 @@
 
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERMANENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_ON_DEMAND,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
 
   createFace(factory,
              FaceUri("ws://127.0.0.1:20070"),
+             {},
              ndn::nfd::FACE_PERSISTENCY_PERSISTENT,
              false,
              {CreateFaceExpectedResult::FAILURE, 406, "Unsupported protocol"});
diff --git a/tests/daemon/mgmt/face-manager.t.cpp b/tests/daemon/mgmt/face-manager.t.cpp
index 9e4228b..c584538 100644
--- a/tests/daemon/mgmt/face-manager.t.cpp
+++ b/tests/daemon/mgmt/face-manager.t.cpp
@@ -259,7 +259,8 @@
   }
 
   void
-  createFace(const FaceUri& uri,
+  createFace(const FaceUri& remoteUri,
+             const ndn::optional<FaceUri>& localUri,
              ndn::nfd::FacePersistency persistency,
              bool wantLocalFieldsEnabled,
              const FaceCreatedCallback& onCreated,