tools: Publish /localhop/ndn-autoconf/routable-prefixes from ndn-autoconfig-server

Change-Id: I2902b91cebf1f4b8ac1a7dabedb0f0cbd0a13b24
Refs: #1954
diff --git a/docs/manpages/ndn-autoconfig-server.rst b/docs/manpages/ndn-autoconfig-server.rst
index 8ed7968..c38b6c1 100644
--- a/docs/manpages/ndn-autoconfig-server.rst
+++ b/docs/manpages/ndn-autoconfig-server.rst
@@ -8,7 +8,7 @@
 
 ::
 
-    ndn-autoconfig-server [-h] FaceUri
+    ndn-autoconfig-server [-h] [-p prefix] [-p prefix] ... FaceUri
 
 
 Description
@@ -27,6 +27,13 @@
 ``FaceUri``
   FaceUri for this NDN hub.
 
+``-p prefix``
+  A local prefix for which the local hub allow end applications to register prefix
+  (See more details in :ref:`local-prefix-discovery`).  One can supply more than one
+  prefixes.  All supplied prefixes will be put into the local prefix discovery data
+  as described in :ref:`local-prefix-discovery`.  If no prefix is specified,
+  auto-config-server will not serve any local prefix discovery data.
+
 Examples
 --------
 
@@ -34,6 +41,8 @@
 
     ndn-autoconfig-server tcp://spurs.cs.ucla.edu
 
+    ndn-autoconfig-server -p /ndn/edu/ucla tcp://spurs.cs.ucla.edu
+
 
 See also
 --------
diff --git a/docs/misc/local-prefix-discovery.rst b/docs/misc/local-prefix-discovery.rst
new file mode 100644
index 0000000..f1bca84
--- /dev/null
+++ b/docs/misc/local-prefix-discovery.rst
@@ -0,0 +1,32 @@
+.. _local-prefix-discovery:
+
+Discover local hub prefix
+=========================
+
+Some applications need to discover prefix(es) under which they can publish data
+/ which Interests local hub will be able to forward down to the application.
+In order to discover that, applications need to send an interest for
+``/localhop/ndn-autoconf/routable-prefixes`` prefix. Response data to the
+interest contains a list of prefixes and should be encoded as:
+
+::
+
+    Response ::= DATA-TYPE TLV-LENGTH
+                 Name (= /localhop/ndn-autoconf/routable-prefixes/[version])
+                 MetaInfo (= ResponseMetaInfo)
+                 Content (= ResponseContent)
+                 Signature
+
+    ResponseMetaInfo ::= META-INFO-TYPE TLV-LENGTH
+                         ContentType (= DATA)
+                         FreshnessPeriod (= 5000)
+
+    ResponseContent ::= Name+
+
+.. note::
+ResponseContent should contain at least one Name, which should be routable
+towards the face from which the request has been received.  The requester may
+process list of the returned names and pick whichever it wants to use.
+
+For now, the ``/localhop/ndn-autoconf/routable-prefixes`` data is served by
+:ref:`ndn-autoconfig-server`.
diff --git a/tools/ndn-autoconfig-server.cpp b/tools/ndn-autoconfig-server.cpp
index bc038e3..1a01061 100644
--- a/tools/ndn-autoconfig-server.cpp
+++ b/tools/ndn-autoconfig-server.cpp
@@ -26,46 +26,111 @@
 #include <ndn-cxx/face.hpp>
 #include <ndn-cxx/security/key-chain.hpp>
 
+namespace ndn {
 
-void
+const static Name AUTOCONFIG_PREFIX          = "/localhop/ndn-autoconf";
+const static Name LOCALHOP_HUB               = "/localhop/ndn-autoconf/hub";
+const static Name LOCALHOP_ROUTABLE_PREFIXES = "/localhop/ndn-autoconf/routable-prefixes";
+
+static void
 usage(const char* programName)
 {
-  std::cout << "Usage:\n" << programName  << " [-h] [-V] Uri \n"
-            << "   -h  - print usage and exit\n"
-            << "   -V  - print version number and exit\n"
+  std::cout << "Usage:\n" << programName  << " [-h] [-V] [-p prefix] [-p prefix] ... Uri \n"
+            << "   -h        - print usage and exit\n"
+            << "   -V        - print version number and exit\n"
+            << "   -p prefix - the local prefix of the hub\n"
             << "\n"
             << "   Uri - a FaceMgmt URI\n"
             << std::endl;
 }
 
-using namespace ndn;
-
-class NdnAutoconfigServer
+class PrefixCollection : noncopyable
 {
 public:
-  explicit
-  NdnAutoconfigServer(const std::string& uri)
-    : m_faceMgmtUri(uri)
+  bool
+  empty() const
   {
+    return m_prefixes.empty();
   }
 
   void
-  onInterest(const Name& name, const Interest& interest)
+  add(const Name& prefix)
   {
-    ndn::Data data(ndn::Name(interest.getName()).appendVersion());
-    data.setFreshnessPeriod(ndn::time::hours(1)); // 1 hour
+    m_prefixes.push_back(prefix);
+  }
 
-    // create and encode uri block
-    Block uriBlock = dataBlock(tlv::nfd::Uri,
-                               reinterpret_cast<const uint8_t*>(m_faceMgmtUri.c_str()),
-                               m_faceMgmtUri.size());
-    data.setContent(uriBlock);
-    m_keyChain.sign(data);
-    m_face.put(data);
+  template<bool T>
+  size_t
+  wireEncode(EncodingImpl<T>& encoder) const
+  {
+    size_t totalLength = 0;
+
+    for (std::vector<Name>::const_reverse_iterator i = m_prefixes.rbegin();
+         i != m_prefixes.rend(); ++i) {
+      totalLength += i->wireEncode(encoder);
+    }
+
+    totalLength += encoder.prependVarNumber(totalLength);
+    totalLength += encoder.prependVarNumber(tlv::Content);
+    return totalLength;
+  }
+
+  Block
+  wireEncode() const
+  {
+    Block block;
+
+    EncodingEstimator estimator;
+    size_t estimatedSize = wireEncode(estimator);
+
+    EncodingBuffer buffer(estimatedSize);
+    wireEncode(buffer);
+
+    return buffer.block();
+  }
+
+private:
+  std::vector<Name> m_prefixes;
+};
+
+class NdnAutoconfigServer : noncopyable
+{
+public:
+  NdnAutoconfigServer(const std::string& hubFaceUri, const PrefixCollection& routablePrefixes)
+  {
+    KeyChain m_keyChain;
+
+    // pre-create hub Data
+    m_hubData = make_shared<Data>(Name(LOCALHOP_HUB).appendVersion());
+    m_hubData->setFreshnessPeriod(time::hours(1)); // 1 hour
+    m_hubData->setContent(dataBlock(tlv::nfd::Uri,
+                                    reinterpret_cast<const uint8_t*>(hubFaceUri.c_str()),
+                                    hubFaceUri.size()));
+    m_keyChain.sign(*m_hubData);
+
+    // pre-create routable prefix Data
+    if (!routablePrefixes.empty()) {
+      m_routablePrefixesData = make_shared<Data>(Name(LOCALHOP_ROUTABLE_PREFIXES).appendVersion());
+      m_routablePrefixesData->setContent(routablePrefixes.wireEncode());
+      m_routablePrefixesData->setFreshnessPeriod(time::seconds(5)); // 5s
+      m_keyChain.sign(*m_routablePrefixesData);
+    }
   }
 
   void
-  onRegisterFailed(const ndn::Name& prefix, const std::string& reason)
+  onHubInterest(const Name& name, const Interest& interest)
+  {
+    m_face.put(*m_hubData);
+  }
+
+  void
+  onRoutablePrefixesInterest(const Name& name, const Interest& interest)
+  {
+    m_face.put(*m_routablePrefixesData);
+  }
+
+  void
+  onRegisterFailed(const Name& prefix, const std::string& reason)
   {
     std::cerr << "ERROR: Failed to register prefix in local hub's daemon (" <<
               reason << ")" << std::endl;
@@ -73,29 +138,46 @@
   }
 
   void
-  listen()
+  afterPrefixRegistered()
   {
-    m_face.setInterestFilter("/localhop/ndn-autoconf/hub",
-                             ndn::bind(&NdnAutoconfigServer::onInterest, this, _1, _2),
-                             ndn::RegisterPrefixSuccessCallback(),
-                             ndn::bind(&NdnAutoconfigServer::onRegisterFailed, this, _1, _2));
+    BOOST_ASSERT(AUTOCONFIG_PREFIX.isPrefixOf(LOCALHOP_HUB));
+    m_face.setInterestFilter(LOCALHOP_HUB,
+                             bind(&NdnAutoconfigServer::onHubInterest, this, _1, _2));
+
+    if (static_cast<bool>(m_routablePrefixesData)) {
+      BOOST_ASSERT(AUTOCONFIG_PREFIX.isPrefixOf(LOCALHOP_ROUTABLE_PREFIXES));
+      m_face.setInterestFilter(LOCALHOP_ROUTABLE_PREFIXES,
+                               bind(&NdnAutoconfigServer::onRoutablePrefixesInterest,
+                                    this, _1, _2));
+    }
+  }
+
+  void
+  run()
+  {
+    m_face.registerPrefix(AUTOCONFIG_PREFIX,
+                          bind(&NdnAutoconfigServer::afterPrefixRegistered, this),
+                          bind(&NdnAutoconfigServer::onRegisterFailed, this, _1, _2));
+
     m_face.processEvents();
   }
 
 private:
-  ndn::Face m_face;
-  KeyChain m_keyChain;
-  std::string m_faceMgmtUri;
+  Face m_face;
 
+  shared_ptr<Data> m_hubData;
+  shared_ptr<Data> m_routablePrefixesData;
 };
 
 int
 main(int argc, char** argv)
 {
-  int opt;
   const char* programName = argv[0];
 
-  while ((opt = getopt(argc, argv, "hV")) != -1) {
+  PrefixCollection routablePrefixes;
+
+  int opt;
+  while ((opt = getopt(argc, argv, "hVp:")) != -1) {
     switch (opt) {
     case 'h':
       usage(programName);
@@ -103,6 +185,9 @@
     case 'V':
       std::cout << NFD_VERSION_BUILD_STRING << std::endl;
       return 0;
+    case 'p':
+      routablePrefixes.add(Name(optarg));
+      break;
     default:
       usage(programName);
       return 1;
@@ -113,11 +198,12 @@
     usage(programName);
     return 1;
   }
-  // get the configured face management uri
-  NdnAutoconfigServer producer(argv[optind]);
+
+  std::string hubFaceUri = argv[optind];
+  NdnAutoconfigServer instance(hubFaceUri, routablePrefixes);
 
   try {
-    producer.listen();
+    instance.run();
   }
   catch (const std::exception& error) {
     std::cerr << "ERROR: " << error.what() << std::endl;
@@ -125,3 +211,11 @@
   }
   return 0;
 }
+
+} // namespace ndn
+
+int
+main(int argc, char** argv)
+{
+  return ndn::main(argc, argv);
+}