face: Adding new 'createFace' method in all Factories

This method accepts parsed URI object and attempts to create Face on the
right channel (e.g., if hostname in URI resolved to IPv6, IPv6 TCP channel
will be used).

Factories that do not support 'createFace' operation will throw an exception.

As of this commit, *Factory::create methods are renamed to more explicit
*Factory::createChannel, and *Factory::connect to *Factory::createFace.
All other Factory-specific methods should denote what exactly is
created: channel, face, or multicast face.

Change-Id: I6396dc84c4cd8bbcdde9f55cfac90635d99e93e9
Refs: #1195
diff --git a/daemon/face/ethernet-factory.cpp b/daemon/face/ethernet-factory.cpp
index c8dbcf9..78adf6f 100644
--- a/daemon/face/ethernet-factory.cpp
+++ b/daemon/face/ethernet-factory.cpp
@@ -15,8 +15,8 @@
 NFD_LOG_INIT("EthernetFactory")
 
 shared_ptr<EthernetFace>
-EthernetFactory::createMulticast(const ethernet::Endpoint& interface,
-                                 const ethernet::Address& address)
+EthernetFactory::createMulticastFace(const ethernet::Endpoint& interface,
+                                     const ethernet::Address& address)
 {
   std::vector<ethernet::Endpoint> ifs = findAllInterfaces();
   if (std::find(ifs.begin(), ifs.end(), interface) == ifs.end())
@@ -25,7 +25,7 @@
   if (!address.isMulticast())
     throw Error(address.toString() + " is not a multicast address");
 
-  shared_ptr<EthernetFace> face = findMulticast(interface, address);
+  shared_ptr<EthernetFace> face = findMulticastFace(interface, address);
   if (face)
     return face;
 
@@ -92,8 +92,8 @@
 }
 
 shared_ptr<EthernetFace>
-EthernetFactory::findMulticast(const ethernet::Endpoint& interface,
-                               const ethernet::Address& address) const
+EthernetFactory::findMulticastFace(const ethernet::Endpoint& interface,
+                                   const ethernet::Address& address) const
 {
   MulticastFacesMap::const_iterator i = m_multicastFaces.find(std::make_pair(interface, address));
   if (i != m_multicastFaces.end())
@@ -102,4 +102,12 @@
     return shared_ptr<EthernetFace>();
 }
 
+void
+EthernetFactory::createFace(const FaceUri& uri,
+                            const FaceCreatedCallback& onCreated,
+                            const FaceConnectFailedCallback& onConnectFailed)
+{
+  throw Error("EthernetFactory does not support 'createFace' operation");
+}
+
 } // namespace nfd
diff --git a/daemon/face/ethernet-factory.hpp b/daemon/face/ethernet-factory.hpp
index d676f47..2eeca7b 100644
--- a/daemon/face/ethernet-factory.hpp
+++ b/daemon/face/ethernet-factory.hpp
@@ -39,8 +39,8 @@
    * \throws EthernetFactory::Error or EthernetFace::Error
    */
   shared_ptr<EthernetFace>
-  createMulticast(const ethernet::Endpoint& interface,
-                  const ethernet::Address& address);
+  createMulticastFace(const ethernet::Endpoint& interface,
+                      const ethernet::Address& address);
 
   /**
    * \brief Get a list of devices that can be opened for a live capture
@@ -50,6 +50,13 @@
   static std::vector<ethernet::Endpoint>
   findAllInterfaces();
 
+  // from Factory
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
+
 private:
   void
   afterFaceFailed(const ethernet::Endpoint& endpoint,
@@ -64,8 +71,8 @@
    * \throws never
    */
   shared_ptr<EthernetFace>
-  findMulticast(const ethernet::Endpoint& interface,
-                const ethernet::Address& address) const;
+  findMulticastFace(const ethernet::Endpoint& interface,
+                    const ethernet::Address& address) const;
 
 private:
   typedef std::map< std::pair<ethernet::Endpoint, ethernet::Address>,
diff --git a/daemon/face/protocol-factory.hpp b/daemon/face/protocol-factory.hpp
index f02941e..8a00bde 100644
--- a/daemon/face/protocol-factory.hpp
+++ b/daemon/face/protocol-factory.hpp
@@ -8,9 +8,26 @@
 #define NFD_FACE_PROTOCOL_FACTORY_HPP
 
 #include "common.hpp"
+#include "core/face-uri.hpp"
 
 namespace nfd {
 
+class Face;
+
+/**
+ * \brief Prototype for the callback called when face is created
+ *        (as a response to incoming connection or after connection
+ *        is established)
+ */
+typedef function<void(const shared_ptr<Face>& newFace)> FaceCreatedCallback;
+
+/**
+ * \brief Prototype for the callback that is called when face is failed to
+ *        get created
+ */
+typedef function<void(const std::string& reason)> FaceConnectFailedCallback;
+
+
 class ProtocolFactory
 {
 public:
@@ -21,6 +38,18 @@
   {
     Error(const std::string& what) : std::runtime_error(what) {}
   };
+
+  /** \brief Try to create Face using the supplied Face URI
+   *
+   * This method should automatically choose channel, based on supplied Face URI
+   * and create face.
+   *
+   * \throws Factory::Error if Factory does not support connect operation
+   */
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed) = 0;
 };
 
 } // namespace nfd
diff --git a/daemon/face/tcp-factory.cpp b/daemon/face/tcp-factory.cpp
index 072ce2e..45b1a48 100644
--- a/daemon/face/tcp-factory.cpp
+++ b/daemon/face/tcp-factory.cpp
@@ -6,13 +6,19 @@
 
 #include "tcp-factory.hpp"
 #include "core/global-io.hpp"
+#include "core/resolver.hpp"
 
 namespace nfd {
 
-shared_ptr<TcpChannel>
-TcpFactory::create(const tcp::Endpoint& endpoint)
+TcpFactory::TcpFactory(const std::string& defaultPort/* = "6363"*/)
+  : m_defaultPort(defaultPort)
 {
-  shared_ptr<TcpChannel> channel = find(endpoint);
+}
+
+shared_ptr<TcpChannel>
+TcpFactory::createChannel(const tcp::Endpoint& endpoint)
+{
+  shared_ptr<TcpChannel> channel = findChannel(endpoint);
   if(static_cast<bool>(channel))
     return channel;
 
@@ -22,7 +28,7 @@
 }
 
 shared_ptr<TcpChannel>
-TcpFactory::create(const std::string& localHost, const std::string& localPort)
+TcpFactory::createChannel(const std::string& localHost, const std::string& localPort)
 {
   using boost::asio::ip::tcp;
 
@@ -34,11 +40,11 @@
   if (i == end)
     return shared_ptr<TcpChannel>();
 
-  return create(*i);
+  return createChannel(*i);
 }
 
 shared_ptr<TcpChannel>
-TcpFactory::find(const tcp::Endpoint& localEndpoint)
+TcpFactory::findChannel(const tcp::Endpoint& localEndpoint)
 {
   ChannelMap::iterator i = m_channels.find(localEndpoint);
   if (i != m_channels.end())
@@ -47,4 +53,47 @@
     return shared_ptr<TcpChannel>();
 }
 
+void
+TcpFactory::createFace(const FaceUri& uri,
+                       const FaceCreatedCallback& onCreated,
+                       const FaceConnectFailedCallback& onConnectFailed)
+{
+  resolver::AddressSelector addressSelector = resolver::AnyAddress();
+  if (uri.getScheme() == "tcp4")
+    addressSelector = resolver::Ipv4Address();
+  else if (uri.getScheme() == "tcp6")
+    addressSelector = resolver::Ipv6Address();
+
+  using boost::asio::ip::tcp;
+  Resolver<tcp>::asyncResolve(uri.getDomain(),
+                              uri.getPort().empty() ? m_defaultPort : uri.getPort(),
+                              bind(&TcpFactory::continueCreateFaceAfterResolve, this, _1,
+                                   onCreated, onConnectFailed),
+                              onConnectFailed,
+                              addressSelector);
+}
+
+void
+TcpFactory::continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
+                                           const FaceCreatedCallback& onCreated,
+                                           const FaceConnectFailedCallback& onConnectFailed)
+{
+  // very simple logic for now
+
+  for (ChannelMap::iterator channel = m_channels.begin();
+       channel != m_channels.end();
+       ++channel)
+    {
+      if ((channel->first.address().is_v4() && endpoint.address().is_v4()) ||
+          (channel->first.address().is_v6() && endpoint.address().is_v6()))
+        {
+          channel->second->connect(endpoint, onCreated, onConnectFailed);
+          return;
+        }
+    }
+  onConnectFailed("No channels available to connect to "
+                  + boost::lexical_cast<std::string>(endpoint));
+}
+
+
 } // namespace nfd
diff --git a/daemon/face/tcp-factory.hpp b/daemon/face/tcp-factory.hpp
index fbda795..1886f24 100644
--- a/daemon/face/tcp-factory.hpp
+++ b/daemon/face/tcp-factory.hpp
@@ -23,6 +23,9 @@
     Error(const std::string& what) : ProtocolFactory::Error(what) {}
   };
 
+  explicit
+  TcpFactory(const std::string& defaultPort = "6363");
+
   /**
    * \brief Create TCP-based channel using tcp::Endpoint
    *
@@ -41,7 +44,7 @@
    *      for details on ways to create tcp::Endpoint
    */
   shared_ptr<TcpChannel>
-  create(const tcp::Endpoint& localEndpoint);
+  createChannel(const tcp::Endpoint& localEndpoint);
 
   /**
    * \brief Create TCP-based channel using specified host and port number
@@ -54,7 +57,14 @@
    * \throws TcpFactory::Error
    */
   shared_ptr<TcpChannel>
-  create(const std::string& localHost, const std::string& localPort);
+  createChannel(const std::string& localHost, const std::string& localPort);
+
+  // from Factory
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
 
 private:
   /**
@@ -66,11 +76,18 @@
    * \throws never
    */
   shared_ptr<TcpChannel>
-  find(const tcp::Endpoint& localEndpoint);
+  findChannel(const tcp::Endpoint& localEndpoint);
+
+  void
+  continueCreateFaceAfterResolve(const tcp::Endpoint& endpoint,
+                                 const FaceCreatedCallback& onCreated,
+                                 const FaceConnectFailedCallback& onConnectFailed);
 
 private:
   typedef std::map< tcp::Endpoint, shared_ptr<TcpChannel> > ChannelMap;
   ChannelMap m_channels;
+
+  std::string m_defaultPort;
 };
 
 } // namespace nfd
diff --git a/daemon/face/unix-stream-factory.cpp b/daemon/face/unix-stream-factory.cpp
index 070d8c1..20c01d9 100644
--- a/daemon/face/unix-stream-factory.cpp
+++ b/daemon/face/unix-stream-factory.cpp
@@ -12,13 +12,13 @@
 namespace nfd {
 
 shared_ptr<UnixStreamChannel>
-UnixStreamFactory::create(const std::string& unixSocketPath)
+UnixStreamFactory::createChannel(const std::string& unixSocketPath)
 {
   boost::filesystem::path p(unixSocketPath);
   p = boost::filesystem::canonical(p.parent_path()) / p.filename();
   unix_stream::Endpoint endpoint(p.string());
 
-  shared_ptr<UnixStreamChannel> channel = find(endpoint);
+  shared_ptr<UnixStreamChannel> channel = findChannel(endpoint);
   if (channel)
     return channel;
 
@@ -29,7 +29,7 @@
 }
 
 shared_ptr<UnixStreamChannel>
-UnixStreamFactory::find(const unix_stream::Endpoint& endpoint)
+UnixStreamFactory::findChannel(const unix_stream::Endpoint& endpoint)
 {
   ChannelMap::iterator i = m_channels.find(endpoint);
   if (i != m_channels.end())
@@ -38,4 +38,12 @@
     return shared_ptr<UnixStreamChannel>();
 }
 
+void
+UnixStreamFactory::createFace(const FaceUri& uri,
+                              const FaceCreatedCallback& onCreated,
+                              const FaceConnectFailedCallback& onConnectFailed)
+{
+  throw Error("UnixStreamFactory does not support 'createFace' operation");
+}
+
 } // namespace nfd
diff --git a/daemon/face/unix-stream-factory.hpp b/daemon/face/unix-stream-factory.hpp
index 4b26a3f..ef857d8 100644
--- a/daemon/face/unix-stream-factory.hpp
+++ b/daemon/face/unix-stream-factory.hpp
@@ -36,7 +36,14 @@
    * \throws UnixStreamFactory::Error
    */
   shared_ptr<UnixStreamChannel>
-  create(const std::string& unixSocketPath);
+  createChannel(const std::string& unixSocketPath);
+
+  // from Factory
+
+  virtual void
+  createFace(const FaceUri& uri,
+             const FaceCreatedCallback& onCreated,
+             const FaceConnectFailedCallback& onConnectFailed);
 
 private:
   /**
@@ -48,7 +55,7 @@
    * \throws never
    */
   shared_ptr<UnixStreamChannel>
-  find(const unix_stream::Endpoint& endpoint);
+  findChannel(const unix_stream::Endpoint& endpoint);
 
 private:
   typedef std::map< unix_stream::Endpoint, shared_ptr<UnixStreamChannel> > ChannelMap;