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/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