face: use move semantics for sockets where possible

Change-Id: I2af595073f862c570c1ce0dcb3717f3d2b9cfd71
Refs: #2613
diff --git a/daemon/face/udp-channel.cpp b/daemon/face/udp-channel.cpp
index bd860cc..c0d0d3c 100644
--- a/daemon/face/udp-channel.cpp
+++ b/daemon/face/udp-channel.cpp
@@ -36,25 +36,10 @@
 UdpChannel::UdpChannel(const udp::Endpoint& localEndpoint,
                        const time::seconds& timeout)
   : m_localEndpoint(localEndpoint)
-  , m_isListening(false)
+  , m_socket(getGlobalIoService())
   , m_idleFaceTimeout(timeout)
 {
   setUri(FaceUri(m_localEndpoint));
-
-  m_socket = make_shared<ip::udp::socket>(ref(getGlobalIoService()));
-  m_socket->open(m_localEndpoint.protocol());
-  m_socket->set_option(ip::udp::socket::reuse_address(true));
-  if (m_localEndpoint.address().is_v6())
-    m_socket->set_option(ip::v6_only(true));
-
-  try {
-    m_socket->bind(m_localEndpoint);
-  }
-  catch (const boost::system::system_error& e) {
-    // bind failed, so the socket is useless now
-    m_socket->close();
-    throw Error("bind failed: " + std::string(e.what()));
-  }
 }
 
 void
@@ -65,14 +50,19 @@
     NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
     return;
   }
-  m_isListening = true;
 
-  m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
-                               m_newRemoteEndpoint,
-                               bind(&UdpChannel::handleNewPeer, this,
-                                    boost::asio::placeholders::error,
-                                    boost::asio::placeholders::bytes_transferred,
-                                    onFaceCreated, onReceiveFailed));
+  m_socket.open(m_localEndpoint.protocol());
+  m_socket.set_option(ip::udp::socket::reuse_address(true));
+  if (m_localEndpoint.address().is_v6())
+    m_socket.set_option(ip::v6_only(true));
+
+  m_socket.bind(m_localEndpoint);
+  m_socket.async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
+                              m_remoteEndpoint,
+                              bind(&UdpChannel::handleNewPeer, this,
+                                   boost::asio::placeholders::error,
+                                   boost::asio::placeholders::bytes_transferred,
+                                   onFaceCreated, onReceiveFailed));
 }
 
 void
@@ -80,34 +70,20 @@
                     const FaceCreatedCallback& onFaceCreated,
                     const ConnectFailedCallback& onConnectFailed)
 {
-  auto it = m_channelFaces.find(remoteEndpoint);
-  if (it != m_channelFaces.end()) {
-    it->second->setOnDemand(false);
-    onFaceCreated(it->second);
-    return;
-  }
-
-  // creating a new socket for the face that will be created soon
-  shared_ptr<ip::udp::socket> clientSocket =
-    make_shared<ip::udp::socket>(ref(getGlobalIoService()));
-
-  clientSocket->open(m_localEndpoint.protocol());
-  clientSocket->set_option(ip::udp::socket::reuse_address(true));
-
+  shared_ptr<UdpFace> face;
   try {
-    clientSocket->bind(m_localEndpoint);
-    clientSocket->connect(remoteEndpoint); //@todo connect or async_connect
-    //(since there is no handshake the connect shouldn't block). If we go for
-    //async_connect, make sure that if in the meantime we receive a UDP pkt from
-    //that endpoint nothing bad happen (it's difficult, but it could happen)
+    face = createFace(remoteEndpoint, false).second;
   }
   catch (const boost::system::system_error& e) {
-    clientSocket->close();
-    onConnectFailed("Failed to configure socket (" + std::string(e.what()) + ")");
+    NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << e.what());
+    if (onConnectFailed)
+      onConnectFailed(e.what());
     return;
   }
 
-  createFace(clientSocket, onFaceCreated, false);
+  // Need to invoke the callback regardless of whether or not we had already
+  // created the face so that control responses and such can be sent
+  onFaceCreated(face);
 }
 
 size_t
@@ -116,42 +92,34 @@
   return m_channelFaces.size();
 }
 
-shared_ptr<UdpFace>
-UdpChannel::createFace(const shared_ptr<ip::udp::socket>& socket,
-                       const FaceCreatedCallback& onFaceCreated,
-                       bool isOnDemand)
+std::pair<bool, shared_ptr<UdpFace>>
+UdpChannel::createFace(const udp::Endpoint& remoteEndpoint, bool isOnDemand)
 {
-  udp::Endpoint remoteEndpoint = socket->remote_endpoint();
-
-  shared_ptr<UdpFace> face;
-
   auto it = m_channelFaces.find(remoteEndpoint);
-  if (it == m_channelFaces.end())
-    {
-      face = make_shared<UdpFace>(socket, isOnDemand, m_idleFaceTimeout);
+  if (it != m_channelFaces.end()) {
+    // we already have a face for this endpoint, just reuse it
+    if (!isOnDemand)
+      // only on-demand -> non-on-demand transition is allowed
+      it->second->setOnDemand(false);
+    return {false, it->second};
+  }
 
-      face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
-        NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
-        m_channelFaces.erase(remoteEndpoint);
-      });
+  // else, create a new face
+  ip::udp::socket socket(getGlobalIoService(), m_localEndpoint.protocol());
+  socket.set_option(ip::udp::socket::reuse_address(true));
+  socket.bind(m_localEndpoint);
+  socket.connect(remoteEndpoint);
 
-      m_channelFaces[remoteEndpoint] = face;
-    }
-  else
-    {
-      // we've already created a a face for this endpoint, just reuse it
-      face = it->second;
+  auto face = make_shared<UdpFace>(FaceUri(remoteEndpoint), FaceUri(m_localEndpoint),
+                                   std::move(socket), isOnDemand, m_idleFaceTimeout);
 
-      boost::system::error_code error;
-      socket->shutdown(ip::udp::socket::shutdown_both, error);
-      socket->close(error);
-    }
+  face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
+    NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
+    m_channelFaces.erase(remoteEndpoint);
+  });
+  m_channelFaces[remoteEndpoint] = face;
 
-  // Need to invoke the callback regardless of whether or not we have already created
-  // the face so that control responses and such can be sent.
-  onFaceCreated(face);
-
-  return face;
+  return {true, face};
 }
 
 void
@@ -170,52 +138,33 @@
     return;
   }
 
-  NFD_LOG_DEBUG("[" << m_localEndpoint << "] New peer " << m_newRemoteEndpoint);
+  NFD_LOG_DEBUG("[" << m_localEndpoint << "] New peer " << m_remoteEndpoint);
 
+  bool created;
   shared_ptr<UdpFace> face;
-
-  auto it = m_channelFaces.find(m_newRemoteEndpoint);
-  if (it != m_channelFaces.end()) {
-    //The face already exists.
-    //Usually this shouldn't happen, because the channel creates a Udpface
-    //as soon as it receives a pkt from a new endpoint and then the
-    //traffic is dispatched by the kernel directly to the face.
-    //However, if the node receives multiple packets from the same endpoint
-    //"at the same time", while the channel is creating the face the kernel
-    //could dispatch the other pkts to the channel because the face is not yet
-    //ready. In this case, the channel has to pass the pkt to the face
-
-    NFD_LOG_DEBUG("The creation of the face for the remote endpoint "
-                  << m_newRemoteEndpoint << " is already in progress");
-    face = it->second;
+  try {
+    std::tie(created, face) = createFace(m_remoteEndpoint, true);
   }
-  else {
-    shared_ptr<ip::udp::socket> clientSocket =
-      make_shared<ip::udp::socket>(ref(getGlobalIoService()));
-    clientSocket->open(m_localEndpoint.protocol());
-    clientSocket->set_option(ip::udp::socket::reuse_address(true));
-    clientSocket->bind(m_localEndpoint);
-
-    boost::system::error_code ec;
-    clientSocket->connect(m_newRemoteEndpoint, ec);
-    if (ec) {
-      NFD_LOG_WARN("Error while creating on-demand UDP face from " << m_newRemoteEndpoint << ": "
-                   << boost::system::system_error(ec).what());
-      return;
-    }
-
-    face = createFace(clientSocket, onFaceCreated, true);
+  catch (const boost::system::system_error& e) {
+    NFD_LOG_WARN("[" << m_localEndpoint << "] Failed to create face for peer "
+                 << m_remoteEndpoint << ": " << e.what());
+    if (onReceiveFailed)
+      onReceiveFailed(e.what());
+    return;
   }
 
+  if (created)
+    onFaceCreated(face);
+
   // dispatch the datagram to the face for processing
   face->receiveDatagram(m_inputBuffer, nBytesReceived, error);
 
-  m_socket->async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
-                               m_newRemoteEndpoint,
-                               bind(&UdpChannel::handleNewPeer, this,
-                                    boost::asio::placeholders::error,
-                                    boost::asio::placeholders::bytes_transferred,
-                                    onFaceCreated, onReceiveFailed));
+  m_socket.async_receive_from(boost::asio::buffer(m_inputBuffer, ndn::MAX_NDN_PACKET_SIZE),
+                              m_remoteEndpoint,
+                              bind(&UdpChannel::handleNewPeer, this,
+                                   boost::asio::placeholders::error,
+                                   boost::asio::placeholders::bytes_transferred,
+                                   onFaceCreated, onReceiveFailed));
 }
 
 } // namespace nfd