face: support IPv6-only WebSocket channels

Change-Id: Ib9ba27d04611c13882e50995d701fc1b707cea5f
Refs: #4710
diff --git a/daemon/face/websocket-channel.cpp b/daemon/face/websocket-channel.cpp
index ca149b3..c4c392b 100644
--- a/daemon/face/websocket-channel.cpp
+++ b/daemon/face/websocket-channel.cpp
@@ -46,6 +46,12 @@
 
   // Setup WebSocket server
   m_server.init_asio(&getGlobalIoService());
+  m_server.set_tcp_pre_bind_handler([isV6 = m_localEndpoint.address().is_v6()] (const auto& acceptor) {
+    if (isV6) {
+      acceptor->set_option(boost::asio::ip::v6_only(true));
+    }
+    return websocketpp::lib::error_code{};
+  });
   m_server.set_open_handler(bind(&WebSocketChannel::handleOpen, this, _1));
   m_server.set_close_handler(bind(&WebSocketChannel::handleClose, this, _1));
   m_server.set_message_handler(bind(&WebSocketChannel::handleMessage, this, _1, _2));
diff --git a/daemon/face/websocket-factory.cpp b/daemon/face/websocket-factory.cpp
index 11921cc..6b493de 100644
--- a/daemon/face/websocket-factory.cpp
+++ b/daemon/face/websocket-factory.cpp
@@ -91,28 +91,30 @@
       "to disable WebSocket channels or enable at least one channel type."));
   }
 
-  if (!enableV4 && enableV6) {
-    // websocketpp's IPv6 socket always accepts IPv4 connections.
-    BOOST_THROW_EXCEPTION(ConfigFile::Error("NFD does not allow pure IPv6 WebSocket channel."));
+  if (context.isDryRun) {
+    return;
   }
 
-  if (!context.isDryRun) {
-    if (!wantListen) {
-      if (!m_channels.empty()) {
-        NFD_LOG_WARN("Cannot close WebSocket channel after initialization");
-      }
-      return;
+  if (!wantListen) {
+    if (!m_channels.empty()) {
+      NFD_LOG_WARN("Cannot disable WebSocket channels after initialization");
     }
+    return;
+  }
 
-    BOOST_ASSERT(enableV4);
-    websocket::Endpoint endpoint(enableV6 ? ip::tcp::v6() : ip::tcp::v4(), port);
+  if (enableV4) {
+    websocket::Endpoint endpoint(ip::tcp::v4(), port);
+    auto v4Channel = this->createChannel(endpoint);
+    if (!v4Channel->isListening()) {
+      v4Channel->listen(this->addFace);
+    }
+  }
 
-    auto channel = this->createChannel(endpoint);
-    if (!channel->isListening()) {
-      channel->listen(this->addFace);
-      if (m_channels.size() > 1) {
-        NFD_LOG_WARN("Adding WebSocket channel for new endpoint; cannot close existing channels");
-      }
+  if (enableV6) {
+    websocket::Endpoint endpoint(ip::tcp::v6(), port);
+    auto v6Channel = this->createChannel(endpoint);
+    if (!v6Channel->isListening()) {
+      v6Channel->listen(this->addFace);
     }
   }
 }
diff --git a/daemon/face/websocketpp.hpp b/daemon/face/websocketpp.hpp
index 35581fe..022682b 100644
--- a/daemon/face/websocketpp.hpp
+++ b/daemon/face/websocketpp.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-2018,  Regents of the University of California,
  *                           Arizona Board of Regents,
  *                           Colorado State University,
  *                           University Pierre & Marie Curie, Sorbonne University,
@@ -34,8 +34,8 @@
 #pragma GCC system_header
 #pragma clang system_header
 
-#include <websocketpp/config/asio_no_tls_client.hpp>
-#include <websocketpp/client.hpp>
+#include "websocketpp/config/asio_no_tls_client.hpp"
+#include "websocketpp/client.hpp"
 #include "websocketpp/config/asio_no_tls.hpp"
 #include "websocketpp/server.hpp"